Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Hoennig
138f020aa9 Merge remote-tracking branch 'origin/master' into add-version-propertie-to-all-entities 2024-04-08 11:16:44 +02:00
44ff30c54a RBAC generator with conditional grants used for REPRESENTATIVE-Relation (#33)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #33
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
2024-04-08 11:16:06 +02:00
896968e110 Merge pull request 'add-salut-and-title-to-person' (#25) from add-salut-and-title-to-person into master
Reviewed-on: #25
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-08 10:28:09 +02:00
Michael Hoennig
552146a98c add title+salutation to EntityPatcherUnitTest 2024-04-08 10:22:10 +02:00
Michael Hoennig
898aa858b1 toShortString without title+salutation 2024-04-08 10:17:02 +02:00
Michael Hoennig
ca952ce748 merging master + rbac-generation 2024-04-08 10:14:22 +02:00
Michael Hoennig
4438e7abd5 Merge remote-tracking branch 'origin/master' into add-salut-and-title-to-person 2024-04-08 10:08:30 +02:00
Marc O. Sandlus
cc7b8fcf9b some tests 2024-03-18 17:41:58 +01:00
Marc O. Sandlus
1a3fad80ee wip initial commit 2024-03-18 15:40:48 +01:00
36 changed files with 692 additions and 378 deletions

View File

@ -10,15 +10,12 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.io.IOException;
import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Optional; import java.util.Optional;

View File

@ -1,6 +1,10 @@
package net.hostsharing.hsadminng.hs.office.debitor; package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.*; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
@ -15,7 +19,13 @@ import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*; import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
@ -26,6 +36,7 @@ import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH; import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
@ -160,6 +171,8 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
// TODO.spec: do we need a distinct case for DEBITOR-Relation?
usingDefaultCase(),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid")) dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(DELETE).grantedTo("debitorRel", OWNER)

View File

@ -29,8 +29,6 @@ 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.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;

View File

@ -23,6 +23,7 @@ import java.util.UUID;
import static jakarta.persistence.CascadeType.*; import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -95,18 +96,19 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
usingDefaultCase(),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid")) dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN) .createPermission(DELETE).grantedTo("partnerRel", OWNER)
.createPermission(UPDATE).grantedTo("partnerRel", AGENT) .createPermission(UPDATE).grantedTo("partnerRel", ADMIN)
.createPermission(SELECT).grantedTo("partnerRel", TENANT) .createPermission(SELECT).grantedTo("partnerRel", TENANT)
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class, .importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("detailsUuid")) dependsOnColumn("detailsUuid"))
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN) .createPermission("partnerDetails", DELETE).grantedTo("partnerRel", OWNER)
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT) .createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
.createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT); .createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT); // not TENANT!
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {

View File

@ -35,6 +35,8 @@ public class HsOfficePersonEntity implements RbacObject, Stringifyable {
private static Stringify<HsOfficePersonEntity> toString = stringify(HsOfficePersonEntity.class, "person") private static Stringify<HsOfficePersonEntity> toString = stringify(HsOfficePersonEntity.class, "person")
.withProp(Fields.personType, HsOfficePersonEntity::getPersonType) .withProp(Fields.personType, HsOfficePersonEntity::getPersonType)
.withProp(Fields.tradeName, HsOfficePersonEntity::getTradeName) .withProp(Fields.tradeName, HsOfficePersonEntity::getTradeName)
.withProp(Fields.salutation, HsOfficePersonEntity::getSalutation)
.withProp(Fields.title, HsOfficePersonEntity::getTitle)
.withProp(Fields.familyName, HsOfficePersonEntity::getFamilyName) .withProp(Fields.familyName, HsOfficePersonEntity::getFamilyName)
.withProp(Fields.givenName, HsOfficePersonEntity::getGivenName); .withProp(Fields.givenName, HsOfficePersonEntity::getGivenName);
@ -51,6 +53,12 @@ public class HsOfficePersonEntity implements RbacObject, Stringifyable {
@Column(name = "tradename") @Column(name = "tradename")
private String tradeName; private String tradeName;
@Column(name = "salutation")
private String salutation;
@Column(name = "title")
private String title;
@Column(name = "familyname") @Column(name = "familyname")
private String familyName; private String familyName;
@ -71,7 +79,7 @@ public class HsOfficePersonEntity implements RbacObject, Stringifyable {
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("person", HsOfficePersonEntity.class) return rbacViewFor("person", HsOfficePersonEntity.class)
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)")) .withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName") .withUpdatableColumns("personType", "title", "salutation", "tradeName", "givenName", "familyName")
.toRole("global", GUEST).grantPermission(INSERT) .toRole("global", GUEST).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {

View File

@ -22,6 +22,8 @@ class HsOfficePersonEntityPatcher implements EntityPatcher<HsOfficePersonPatchRe
.map(HsOfficePersonType::valueOf) .map(HsOfficePersonType::valueOf)
.ifPresent(entity::setPersonType); .ifPresent(entity::setPersonType);
OptionalFromJson.of(resource.getTradeName()).ifPresent(entity::setTradeName); OptionalFromJson.of(resource.getTradeName()).ifPresent(entity::setTradeName);
OptionalFromJson.of(resource.getSalutation()).ifPresent(entity::setSalutation);
OptionalFromJson.of(resource.getTitle()).ifPresent(entity::setTitle);
OptionalFromJson.of(resource.getFamilyName()).ifPresent(entity::setFamilyName); OptionalFromJson.of(resource.getFamilyName()).ifPresent(entity::setFamilyName);
OptionalFromJson.of(resource.getGivenName()).ifPresent(entity::setGivenName); OptionalFromJson.of(resource.getGivenName()).ifPresent(entity::setGivenName);
} }

View File

@ -11,17 +11,19 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.persistence.Column;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inOtherCases;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@ -104,31 +106,55 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NOT_NULL) NOT_NULL)
.createRole(OWNER, (with) -> { .switchOnColumn("type",
inCaseOf("REPRESENTATIVE", then -> {
then.createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN); with.incomingSuperRole(GLOBAL, ADMIN);
// TODO: if type=REPRESENTATIIVE with.incomingSuperRole("holderPerson", ADMIN);
// with.incomingSuperRole("holderPerson", ADMIN);
with.permission(DELETE); with.permission(DELETE);
}) })
.createSubRole(ADMIN, (with) -> { .createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("anchorPerson", ADMIN); with.outgoingSubRole("anchorPerson", OWNER);
// TODO: if type=REPRESENTATIIVE
// with.outgoingSuperRole("anchorPerson", OWNER);
with.permission(UPDATE); with.permission(UPDATE);
}) })
.createSubRole(AGENT, (with) -> { .createSubRole(AGENT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN); with.incomingSuperRole("anchorPerson", ADMIN);
}) })
.createSubRole(TENANT, (with) -> { .createSubRole(TENANT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("contact", ADMIN); with.incomingSuperRole("contact", ADMIN);
with.outgoingSubRole("anchorPerson", REFERRER); with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER); with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("contact", REFERRER); with.outgoingSubRole("contact", REFERRER);
with.permission(SELECT); with.permission(SELECT);
});
}),
// inCaseOf("DEBITOR", then -> {}), TODO.spec: needs to be defined
inOtherCases(then -> {
then.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
with.incomingSuperRole("anchorPerson", ADMIN);
with.permission(DELETE);
}) })
.createSubRole(ADMIN, (with) -> {
with.permission(UPDATE);
})
.createSubRole(AGENT, (with) -> {
// TODO.spec: we need relation:PROXY, to allow changing the relation contact.
// the alternative would be to move this to the relation:ADMIN role,
// but then the partner holder person could update the partner relation itself,
// see partner entity.
with.incomingSuperRole("holderPerson", ADMIN);
})
.createSubRole(TENANT, (with) -> {
with.incomingSuperRole("contact", ADMIN);
with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("contact", REFERRER);
with.permission(SELECT);
});
}))
.toRole("anchorPerson", ADMIN).grantPermission(INSERT); .toRole("anchorPerson", ADMIN).grantPermission(INSERT);
} }

View File

@ -14,7 +14,6 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity; import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
import net.hostsharing.hsadminng.test.dom.TestDomainEntity; import net.hostsharing.hsadminng.test.dom.TestDomainEntity;
import net.hostsharing.hsadminng.test.pac.TestPackageEntity; import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
@ -27,18 +26,22 @@ import java.lang.reflect.Method;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.lang.reflect.Modifier.isStatic; import static java.lang.reflect.Modifier.isStatic;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static org.apache.commons.collections4.SetUtils.hashSet;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@Getter @Getter
// TODO.refa: rename to RbacDSL
public class RbacView { public class RbacView {
public static final String GLOBAL = "global"; public static final String GLOBAL = "global";
@ -61,11 +64,23 @@ public class RbacView {
}; };
private final Set<String> updatableColumns = new LinkedHashSet<>(); private final Set<String> updatableColumns = new LinkedHashSet<>();
private final Set<RbacGrantDefinition> grantDefs = new LinkedHashSet<>(); private final Set<RbacGrantDefinition> grantDefs = new LinkedHashSet<>();
private final Set<CaseDef> allCases = new LinkedHashSet<>();
private String discriminatorColumName;
private CaseDef processingCase;
private SQL identityViewSqlQuery; private SQL identityViewSqlQuery;
private SQL orderBySqlExpression; private SQL orderBySqlExpression;
private EntityAlias rootEntityAliasProxy; private EntityAlias rootEntityAliasProxy;
private RbacRoleDefinition previousRoleDef; private RbacRoleDefinition previousRoleDef;
private final Map<String, CaseDef> cases = new LinkedHashMap<>() {
@Override
public CaseDef put(final String key, final CaseDef value) {
if (containsKey(key)) {
throw new IllegalArgumentException("duplicate case: " + key);
}
return super.put(key, value);
}
};
/** Crates an RBAC definition template for the given entity class and defining the given alias. /** Crates an RBAC definition template for the given entity class and defining the given alias.
* *
@ -239,7 +254,11 @@ public class RbacView {
} }
private RbacPermissionDefinition createPermission(final EntityAlias entityAlias, final Permission permission) { private RbacPermissionDefinition createPermission(final EntityAlias entityAlias, final Permission permission) {
return new RbacPermissionDefinition(entityAlias, permission, null, true); return permDefs.stream()
.filter(p -> p.permission == permission && p.entityAlias == entityAlias)
.findFirst()
// .map(g -> g.forCase(processingCase)) TODO.impl: not implemented case dependent
.orElseGet(() -> new RbacPermissionDefinition(entityAlias, permission, null, true));
} }
public <EC extends RbacObject> RbacView declarePlaceholderEntityAliases(final String... aliasNames) { public <EC extends RbacObject> RbacView declarePlaceholderEntityAliases(final String... aliasNames) {
@ -278,12 +297,13 @@ public class RbacView {
public <EC extends RbacObject> RbacView importRootEntityAliasProxy( public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
final String aliasName, final String aliasName,
final Class<? extends RbacObject> entityClass, final Class<? extends RbacObject> entityClass,
final ColumnValue forCase,
final SQL fetchSql, final SQL fetchSql,
final Column dependsOnColum) { final Column dependsOnColum) {
if (rootEntityAliasProxy != null) { if (rootEntityAliasProxy != null) {
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy); throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
} }
rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL); rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, forCase, fetchSql, dependsOnColum, false, NOT_NULL);
return this; return this;
} }
@ -302,7 +322,7 @@ public class RbacView {
public RbacView importSubEntityAlias( public RbacView importSubEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass, final String aliasName, final Class<? extends RbacObject> entityClass,
final SQL fetchSql, final Column dependsOnColum) { final SQL fetchSql, final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL); importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, true, NOT_NULL);
return this; return this;
} }
@ -336,25 +356,17 @@ public class RbacView {
public RbacView importEntityAlias( public RbacView importEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass, final String aliasName, final Class<? extends RbacObject> entityClass,
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) { final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable); importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, false, nullable);
return this;
}
// TODO: remove once it's not used in HsOffice...Entity anymore
public RbacView importEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass,
final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this; return this;
} }
private EntityAlias importEntityAliasImpl( private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends RbacObject> entityClass, final String aliasName, final Class<? extends RbacObject> entityClass, final ColumnValue forCase,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable); final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, entityAlias); entityAliases.put(aliasName, entityAlias);
try { try {
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity); importAsAlias(aliasName, rbacDefinition(entityClass), forCase, asSubEntity);
} catch (final ReflectiveOperationException exc) { } catch (final ReflectiveOperationException exc) {
throw new RuntimeException("cannot import entity: " + entityClass, exc); throw new RuntimeException("cannot import entity: " + entityClass, exc);
} }
@ -366,7 +378,7 @@ public class RbacView {
return (RbacView) entityClass.getMethod("rbac").invoke(null); return (RbacView) entityClass.getMethod("rbac").invoke(null);
} }
private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final boolean asSubEntity) { private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final ColumnValue forCase, final boolean asSubEntity) {
final var mapper = new AliasNameMapper(importedRbacView, aliasName, final var mapper = new AliasNameMapper(importedRbacView, aliasName,
asSubEntity ? entityAliases.keySet() : null); asSubEntity ? entityAliases.keySet() : null);
importedRbacView.getEntityAliases().values().stream() importedRbacView.getEntityAliases().values().stream()
@ -381,7 +393,8 @@ public class RbacView {
new RbacRoleDefinition(findEntityAlias(mapper.map(roleDef.entityAlias.aliasName)), roleDef.role); new RbacRoleDefinition(findEntityAlias(mapper.map(roleDef.entityAlias.aliasName)), roleDef.role);
}); });
importedRbacView.getGrantDefs().forEach(grantDef -> { importedRbacView.getGrantDefs().forEach(grantDef -> {
if (grantDef.grantType() == RbacGrantDefinition.GrantType.ROLE_TO_ROLE) { if ( grantDef.grantType() == RbacGrantDefinition.GrantType.ROLE_TO_ROLE &&
(grantDef.forCases == null || grantDef.matchesCase(forCase)) ) {
final var importedGrantDef = findOrCreateGrantDef( final var importedGrantDef = findOrCreateGrantDef(
findRbacRole( findRbacRole(
mapper.map(grantDef.getSubRoleDef().entityAlias.aliasName), mapper.map(grantDef.getSubRoleDef().entityAlias.aliasName),
@ -398,6 +411,18 @@ public class RbacView {
return this; return this;
} }
public RbacView switchOnColumn(final String discriminatorColumName, final CaseDef... caseDefs) {
this.discriminatorColumName = discriminatorColumName;
allCases.addAll(stream(caseDefs).toList());
stream(caseDefs).forEach(caseDef -> {
this.processingCase = caseDef;
caseDef.def.accept(this);
this.processingCase = null;
});
return this;
}
private void verifyVersionColumnExists() { private void verifyVersionColumnExists() {
if (stream(rootEntityAlias.entityClass.getDeclaredFields()) if (stream(rootEntityAlias.entityClass.getDeclaredFields())
.noneMatch(f -> f.getAnnotation(Version.class) != null)) { .noneMatch(f -> f.getAnnotation(Version.class) != null)) {
@ -455,7 +480,15 @@ public class RbacView {
} }
public void generateWithBaseFileName(final String baseFileName) { public void generateWithBaseFileName(final String baseFileName) {
if (allCases.size() > 1) {
allCases.forEach(caseDef -> {
final var fileName = baseFileName + (caseDef.isDefaultCase() ? "" : "-" + caseDef.value) + ".md";
new RbacViewMermaidFlowchartGenerator(this, caseDef)
.generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, fileName));
});
} else {
new RbacViewMermaidFlowchartGenerator(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md")); new RbacViewMermaidFlowchartGenerator(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md"));
}
new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql")); new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql"));
} }
@ -495,22 +528,28 @@ public class RbacView {
private final RbacPermissionDefinition permDef; private final RbacPermissionDefinition permDef;
private boolean assumed = true; private boolean assumed = true;
private boolean toCreate = false; private boolean toCreate = false;
private Set<CaseDef> forCases = new HashSet<>();
@Override @Override
public String toString() { public String toString() {
final var arrow = isAssumed() ? " --> " : " -- // --> "; final var arrow = isAssumed() ? " --> " : " -- // --> ";
return switch (grantType()) { final var grant = switch (grantType()) {
case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString(); case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString();
case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef; case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef;
case PERM_TO_ROLE -> superRoleDef + arrow + permDef; case PERM_TO_ROLE -> superRoleDef + arrow + permDef;
}; };
final var condition = isConditional()
? (" (" +forCases.stream().map(CaseDef::toString).collect(Collectors.joining("||")) + ")")
: "";
return grant + condition;
} }
RbacGrantDefinition(final RbacRoleDefinition subRoleDef, final RbacRoleDefinition superRoleDef) { RbacGrantDefinition(final RbacRoleDefinition subRoleDef, final RbacRoleDefinition superRoleDef, final CaseDef forCase) {
this.userDef = null; this.userDef = null;
this.subRoleDef = subRoleDef; this.subRoleDef = subRoleDef;
this.superRoleDef = superRoleDef; this.superRoleDef = superRoleDef;
this.permDef = null; this.permDef = null;
this.forCases = forCase != null ? hashSet(forCase) : null;
register(this); register(this);
} }
@ -536,7 +575,7 @@ public class RbacView {
@NotNull @NotNull
GrantType grantType() { GrantType grantType() {
return permDef != null ? GrantType.PERM_TO_ROLE return permDef != null ? PERM_TO_ROLE
: userDef != null ? GrantType.ROLE_TO_USER : userDef != null ? GrantType.ROLE_TO_USER
: GrantType.ROLE_TO_ROLE; : GrantType.ROLE_TO_ROLE;
} }
@ -545,6 +584,23 @@ public class RbacView {
return assumed; return assumed;
} }
RbacGrantDefinition forCase(final CaseDef processingCase) {
forCases.add(processingCase);
return this;
}
boolean isConditional() {
return forCases != null && !forCases.isEmpty() && forCases.size()<allCases.size();
}
boolean matchesCase(final ColumnValue requestedCase) {
final var noCasesDefined = forCases.isEmpty();
final var generateForAllCases = requestedCase == null;
final boolean isGrantedForRequestedCase = forCases.stream().anyMatch(c -> c.isCase(requestedCase));
return noCasesDefined || generateForAllCases || isGrantedForRequestedCase;
}
boolean isToCreate() { boolean isToCreate() {
return toCreate; return toCreate;
} }
@ -566,8 +622,9 @@ public class RbacView {
.orElse(false); .orElse(false);
} }
public void unassumed() { public RbacGrantDefinition unassumed() {
this.assumed = false; this.assumed = false;
return this;
} }
public enum GrantType { public enum GrantType {
@ -793,7 +850,7 @@ public class RbacView {
private RbacGrantDefinition findOrCreateGrantDef(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef) { private RbacGrantDefinition findOrCreateGrantDef(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef) {
return grantDefs.stream() return grantDefs.stream()
.filter(g -> g.permDef == permDef && g.subRoleDef == roleDef) .filter(g -> g.permDef == permDef && g.superRoleDef == roleDef)
.findFirst() .findFirst()
.orElseGet(() -> new RbacGrantDefinition(permDef, roleDef)); .orElseGet(() -> new RbacGrantDefinition(permDef, roleDef));
} }
@ -801,10 +858,12 @@ public class RbacView {
private RbacGrantDefinition findOrCreateGrantDef( private RbacGrantDefinition findOrCreateGrantDef(
final RbacRoleDefinition subRoleDefinition, final RbacRoleDefinition subRoleDefinition,
final RbacRoleDefinition superRoleDefinition) { final RbacRoleDefinition superRoleDefinition) {
return grantDefs.stream() final var distinctGrantDef = grantDefs.stream()
.filter(g -> g.subRoleDef == subRoleDefinition && g.superRoleDef == superRoleDefinition) .filter(g -> g.subRoleDef == subRoleDefinition && g.superRoleDef == superRoleDefinition)
.findFirst() .findFirst()
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition)); .map(g -> g.forCase(processingCase))
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition, processingCase));
return distinctGrantDef;
} }
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) { record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
@ -1021,6 +1080,23 @@ public class RbacView {
} }
} }
public static class ColumnValue {
public static ColumnValue usingDefaultCase() {
return new ColumnValue(null);
}
public static ColumnValue usingCase(final String value) {
return new ColumnValue(value);
}
public final String value;
private ColumnValue(final String value) {
this.value = value;
}
}
private static class AliasNameMapper { private static class AliasNameMapper {
private final RbacView importedRbacView; private final RbacView importedRbacView;
@ -1045,6 +1121,55 @@ public class RbacView {
} }
} }
public static class CaseDef extends ColumnValue {
final Consumer<RbacView> def;
private CaseDef(final String discriminatorColumnValue, final Consumer<RbacView> def) {
super(discriminatorColumnValue);
this.def = def;
}
public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer<RbacView> def) {
return new CaseDef(discriminatorColumnValue, def);
}
public static CaseDef inOtherCases(final Consumer<RbacView> def) {
return new CaseDef(null, def);
}
@Override
public int hashCode() {
return ofNullable(value).map(String::hashCode).orElse(0);
}
@Override
public boolean equals(final Object other) {
if (this == other)
return true;
if (other == null || getClass() != other.getClass())
return false;
final CaseDef caseDef = (CaseDef) other;
return Objects.equals(value, caseDef.value);
}
boolean isDefaultCase() {
return value == null;
}
@Override
public String toString() {
return isDefaultCase()
? "inOtherCases"
: "inCaseOf:" + value;
}
public boolean isCase(final ColumnValue requestedCase) {
return Objects.equals(requestedCase.value, this.value);
}
}
private static void generateRbacView(final Class<? extends RbacObject> c) { private static void generateRbacView(final Class<? extends RbacObject> c) {
final Method mainMethod = stream(c.getMethods()).filter( final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main") m -> isStatic(m.getModifiers()) && m.getName().equals("main")

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.rbac.rbacdef; package net.hostsharing.hsadminng.rbac.rbacdef;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.nio.file.*; import java.nio.file.*;
@ -15,10 +16,13 @@ public class RbacViewMermaidFlowchartGenerator {
public static final String HOSTSHARING_DARK_BLUE = "#274d6e"; public static final String HOSTSHARING_DARK_BLUE = "#274d6e";
public static final String HOSTSHARING_LIGHT_BLUE = "#99bcdb"; public static final String HOSTSHARING_LIGHT_BLUE = "#99bcdb";
private final RbacView rbacDef; private final RbacView rbacDef;
private final CaseDef forCase;
private final StringWriter flowchart = new StringWriter(); private final StringWriter flowchart = new StringWriter();
public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef) { public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef, final CaseDef forCase) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
this.forCase = forCase;
flowchart.writeLn(""" flowchart.writeLn("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB
@ -26,6 +30,10 @@ public class RbacViewMermaidFlowchartGenerator {
renderEntitySubgraphs(); renderEntitySubgraphs();
renderGrants(); renderGrants();
} }
public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef) {
this(rbacDef, null);
}
private void renderEntitySubgraphs() { private void renderEntitySubgraphs() {
rbacDef.getEntityAliases().values().stream() rbacDef.getEntityAliases().values().stream()
.filter(entityAlias -> !rbacDef.isEntityAliasProxy(entityAlias)) .filter(entityAlias -> !rbacDef.isEntityAliasProxy(entityAlias))
@ -99,6 +107,7 @@ public class RbacViewMermaidFlowchartGenerator {
private void renderGrants(final RbacView.RbacGrantDefinition.GrantType grantType, final String comment) { private void renderGrants(final RbacView.RbacGrantDefinition.GrantType grantType, final String comment) {
final var grantsOfRequestedType = rbacDef.getGrantDefs().stream() final var grantsOfRequestedType = rbacDef.getGrantDefs().stream()
.filter(g -> g.grantType() == grantType) .filter(g -> g.grantType() == grantType)
.filter(this::isToBeRenderedInThisGraph)
.toList(); .toList();
if ( !grantsOfRequestedType.isEmpty()) { if ( !grantsOfRequestedType.isEmpty()) {
flowchart.ensureSingleEmptyLine(); flowchart.ensureSingleEmptyLine();
@ -107,10 +116,19 @@ public class RbacViewMermaidFlowchartGenerator {
} }
} }
private boolean isToBeRenderedInThisGraph(final RbacView.RbacGrantDefinition g) {
if ( g.grantType() != ROLE_TO_ROLE )
return true;
if ( forCase == null && !g.isConditional() )
return true;
final var isToBeRenderedInThisGraph = g.getForCases() == null || g.getForCases().contains(forCase);
return isToBeRenderedInThisGraph;
}
private String grantDef(final RbacView.RbacGrantDefinition grant) { private String grantDef(final RbacView.RbacGrantDefinition grant) {
final var arrow = (grant.isToCreate() ? " ==>" : " -.->") final var arrow = (grant.isToCreate() ? " ==>" : " -.->")
+ (grant.isAssumed() ? " " : "|XX| "); + (grant.isAssumed() ? " " : "|XX| ");
return switch (grant.grantType()) { final var grantDef = switch (grant.grantType()) {
case ROLE_TO_USER -> case ROLE_TO_USER ->
// TODO: other user types not implemented yet // TODO: other user types not implemented yet
"user:creator" + arrow + roleId(grant.getSubRoleDef()); "user:creator" + arrow + roleId(grant.getSubRoleDef());
@ -118,6 +136,7 @@ public class RbacViewMermaidFlowchartGenerator {
roleId(grant.getSuperRoleDef()) + arrow + roleId(grant.getSubRoleDef()); roleId(grant.getSuperRoleDef()) + arrow + roleId(grant.getSubRoleDef());
case PERM_TO_ROLE -> roleId(grant.getSuperRoleDef()) + arrow + permId(grant.getPermDef()); case PERM_TO_ROLE -> roleId(grant.getSuperRoleDef()) + arrow + permId(grant.getPermDef());
}; };
return grantDef;
} }
private String permDef(final RbacView.RbacPermissionDefinition perm) { private String permDef(final RbacView.RbacPermissionDefinition perm) {
@ -146,7 +165,7 @@ public class RbacViewMermaidFlowchartGenerator {
Files.writeString( Files.writeString(
path, path,
""" """
### rbac %{entityAlias} ### rbac %{entityAlias}%{case}
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
@ -155,7 +174,8 @@ public class RbacViewMermaidFlowchartGenerator {
``` ```
""" """
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName()) .replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
.replace("%{flowchart}", flowchart.toString()), .replace("%{flowchart}", flowchart.toString())
.replace("%{case}", forCase == null ? "" : " " + forCase),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("Markdown-File: " + path.toAbsolutePath()); System.out.println("Markdown-File: " + path.toAbsolutePath());
} }

View File

@ -1,10 +1,13 @@
package net.hostsharing.hsadminng.rbac.rbacdef; package net.hostsharing.hsadminng.rbac.rbacdef;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
@ -22,7 +25,7 @@ import static org.apache.commons.lang3.StringUtils.uncapitalize;
class RolesGrantsAndPermissionsGenerator { class RolesGrantsAndPermissionsGenerator {
private final RbacView rbacDef; private final RbacView rbacDef;
private final Set<RbacView.RbacGrantDefinition> rbacGrants = new HashSet<>(); private final Set<RbacGrantDefinition> rbacGrants = new HashSet<>();
private final String liquibaseTagPrefix; private final String liquibaseTagPrefix;
private final String simpleEntityName; private final String simpleEntityName;
private final String simpleEntityVarName; private final String simpleEntityVarName;
@ -31,7 +34,7 @@ class RolesGrantsAndPermissionsGenerator {
RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
this.rbacGrants.addAll(rbacDef.getGrantDefs().stream() this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate) .filter(RbacGrantDefinition::isToCreate)
.collect(toSet())); .collect(toSet()));
this.liquibaseTagPrefix = liquibaseTagPrefix; this.liquibaseTagPrefix = liquibaseTagPrefix;
@ -67,13 +70,11 @@ class RolesGrantsAndPermissionsGenerator {
NEW ${rawTableName} NEW ${rawTableName}
) )
language plpgsql as $$ language plpgsql as $$
declare
""" """
.replace("${simpleEntityName}", simpleEntityName) .replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableName}", rawTableName)); .replace("${rawTableName}", rawTableName));
plPgSql.chopEmptyLines(); plPgSql.writeLn("declare");
plPgSql.indented(() -> { plPgSql.indented(() -> {
referencedEntityAliases() referencedEntityAliases()
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";")); .forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
@ -172,6 +173,10 @@ class RolesGrantsAndPermissionsGenerator {
.anyMatch(e -> true); .anyMatch(e -> true);
} }
private boolean hasAnyConditionalGrants() {
return rbacDef.getGrantDefs().stream().anyMatch(RbacGrantDefinition::isConditional);
}
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) { private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
referencedEntityAliases() referencedEntityAliases()
.forEach((ea) -> { .forEach((ea) -> {
@ -186,7 +191,25 @@ class RolesGrantsAndPermissionsGenerator {
createRolesWithGrantsSql(plPgSql, REFERRER); createRolesWithGrantsSql(plPgSql, REFERRER);
generateGrants(plPgSql, ROLE_TO_USER); generateGrants(plPgSql, ROLE_TO_USER);
generateGrants(plPgSql, ROLE_TO_ROLE); generateGrants(plPgSql, ROLE_TO_ROLE);
if (!rbacDef.getAllCases().isEmpty()) {
plPgSql.writeLn();
final var ifOrElsIf = new AtomicReference<>("IF ");
rbacDef.getAllCases().forEach(caseDef -> {
if (caseDef.value != null) {
plPgSql.writeLn(ifOrElsIf + "NEW." + rbacDef.getDiscriminatorColumName() + " = '" + caseDef.value + "' THEN");
} else {
plPgSql.writeLn("ELSE");
}
plPgSql.indented(() -> {
generateGrants(plPgSql, ROLE_TO_ROLE, caseDef);
});
ifOrElsIf.set("ELSIF ");
});
plPgSql.writeLn("END IF;");
}
generateGrants(plPgSql, PERM_TO_ROLE); generateGrants(plPgSql, PERM_TO_ROLE);
} }
@ -248,7 +271,7 @@ class RolesGrantsAndPermissionsGenerator {
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) { private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
rbacDef.getGrantDefs().stream() rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate) .filter(RbacGrantDefinition::isToCreate)
.filter(g -> g.dependsOnColumn(columnName)) .filter(g -> g.dependsOnColumn(columnName))
.filter(g -> !isInsertPermissionGrant(g)) .filter(g -> !isInsertPermissionGrant(g))
.forEach(g -> { .forEach(g -> {
@ -259,21 +282,31 @@ class RolesGrantsAndPermissionsGenerator {
}); });
} }
private static Boolean isInsertPermissionGrant(final RbacView.RbacGrantDefinition g) { private static Boolean isInsertPermissionGrant(final RbacGrantDefinition g) {
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false); final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
return isInsertPermissionGrant; return isInsertPermissionGrant;
} }
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) { private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType, final CaseDef caseDef) {
plPgSql.ensureSingleEmptyLine();
rbacGrants.stream() rbacGrants.stream()
.filter(g -> g.matchesCase(caseDef))
.filter(g -> g.grantType() == grantType) .filter(g -> g.grantType() == grantType)
.map(this::generateGrant) .map(this::generateGrant)
.sorted() .sorted()
.forEach(text -> plPgSql.writeLn(text)); .forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
} }
private String generateRevoke(RbacView.RbacGrantDefinition grantDef) { private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType) {
plPgSql.ensureSingleEmptyLine();
rbacGrants.stream()
.filter(g -> !g.isConditional())
.filter(g -> g.grantType() == grantType)
.map(this::generateGrant)
.sorted()
.forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
}
private String generateRevoke(RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) { return switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant"); case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});" case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
@ -285,8 +318,8 @@ class RolesGrantsAndPermissionsGenerator {
}; };
} }
private String generateGrant(RbacView.RbacGrantDefinition grantDef) { private String generateGrant(RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) { final var grantSql = switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant"); case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});" case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
.replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()") .replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
@ -298,6 +331,7 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef())) .replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
}; };
return grantSql;
} }
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
@ -362,11 +396,8 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${roleSuffix}", capitalize(role.name()))); .replace("${roleSuffix}", capitalize(role.name())));
generatePermissionsForRole(plPgSql, role); generatePermissionsForRole(plPgSql, role);
generateIncomingSuperRolesForRole(plPgSql, role); generateIncomingSuperRolesForRole(plPgSql, role);
generateOutgoingSubRolesForRole(plPgSql, role); generateOutgoingSubRolesForRole(plPgSql, role);
generateUserGrantsForRole(plPgSql, role); generateUserGrantsForRole(plPgSql, role);
plPgSql.chopTail(",\n"); plPgSql.chopTail(",\n");
@ -380,7 +411,7 @@ class RolesGrantsAndPermissionsGenerator {
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role); final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
if (!grantsToUsers.isEmpty()) { if (!grantsToUsers.isEmpty()) {
final var arrayElements = grantsToUsers.stream() final var arrayElements = grantsToUsers.stream()
.map(RbacView.RbacGrantDefinition::getUserDef) .map(RbacGrantDefinition::getUserDef)
.map(this::toPlPgSqlReference) .map(this::toPlPgSqlReference)
.toList(); .toList();
plPgSql.indented(() -> plPgSql.indented(() ->
@ -393,7 +424,7 @@ class RolesGrantsAndPermissionsGenerator {
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role); final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
if (!permissionGrantsForRole.isEmpty()) { if (!permissionGrantsForRole.isEmpty()) {
final var arrayElements = permissionGrantsForRole.stream() final var arrayElements = permissionGrantsForRole.stream()
.map(RbacView.RbacGrantDefinition::getPermDef) .map(RbacGrantDefinition::getPermDef)
.map(RbacPermissionDefinition::getPermission) .map(RbacPermissionDefinition::getPermission)
.map(RbacView.Permission::name) .map(RbacView.Permission::name)
.map(p -> "'" + p + "'") .map(p -> "'" + p + "'")
@ -406,26 +437,30 @@ class RolesGrantsAndPermissionsGenerator {
} }
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) { private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); final var unconditionalIncomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
if (!incomingGrants.isEmpty()) { .filter(g -> !g.isConditional())
final var arrayElements = incomingGrants.stream() .toList();
if (!unconditionalIncomingGrants.isEmpty()) {
final var arrayElements = unconditionalIncomingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed())) .map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
.sorted().toList(); .sorted().toList();
plPgSql.indented(() -> plPgSql.indented(() ->
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n")); plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(incomingGrants); rbacGrants.removeAll(unconditionalIncomingGrants);
} }
} }
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) { private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role); final var unconditionalOutgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
if (!outgoingGrants.isEmpty()) { .filter(g -> !g.isConditional())
final var arrayElements = outgoingGrants.stream() .toList();
if (!unconditionalOutgoingGrants.isEmpty()) {
final var arrayElements = unconditionalOutgoingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed())) .map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
.sorted().toList(); .sorted().toList();
plPgSql.indented(() -> plPgSql.indented(() ->
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n")); plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(outgoingGrants); rbacGrants.removeAll(unconditionalOutgoingGrants);
} }
} }
@ -435,7 +470,7 @@ class RolesGrantsAndPermissionsGenerator {
: arrayElements.stream().collect(joining(",\n\t", "\n\t", "")); : arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
} }
private Set<RbacView.RbacGrantDefinition> findPermissionsGrantsForRole( private Set<RbacGrantDefinition> findPermissionsGrantsForRole(
final RbacView.EntityAlias entityAlias, final RbacView.EntityAlias entityAlias,
final RbacView.Role role) { final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role); final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -444,7 +479,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet()); .collect(toSet());
} }
private Set<RbacView.RbacGrantDefinition> findGrantsToUserForRole( private Set<RbacGrantDefinition> findGrantsToUserForRole(
final RbacView.EntityAlias entityAlias, final RbacView.EntityAlias entityAlias,
final RbacView.Role role) { final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role); final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -453,7 +488,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet()); .collect(toSet());
} }
private Set<RbacView.RbacGrantDefinition> findIncomingSuperRolesForRole( private Set<RbacGrantDefinition> findIncomingSuperRolesForRole(
final RbacView.EntityAlias entityAlias, final RbacView.EntityAlias entityAlias,
final RbacView.Role role) { final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role); final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -462,7 +497,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet()); .collect(toSet());
} }
private Set<RbacView.RbacGrantDefinition> findOutgoingSuperRolesForRole( private Set<RbacGrantDefinition> findOutgoingSuperRolesForRole(
final RbacView.EntityAlias entityAlias, final RbacView.EntityAlias entityAlias,
final RbacView.Role role) { final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role); final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -506,7 +541,7 @@ class RolesGrantsAndPermissionsGenerator {
private void generateUpdateTrigger(final StringWriter plPgSql) { private void generateUpdateTrigger(final StringWriter plPgSql) {
generateHeader(plPgSql, "update"); generateHeader(plPgSql, "update");
if ( hasAnyUpdatableAndNullableEntityAliases() ) { if ( hasAnyUpdatableAndNullableEntityAliases() || hasAnyConditionalGrants() ) {
generateSimplifiedUpdateTriggerFunction(plPgSql); generateSimplifiedUpdateTriggerFunction(plPgSql);
} else { } else {
generateUpdateTriggerFunction(plPgSql); generateUpdateTriggerFunction(plPgSql);

View File

@ -167,7 +167,7 @@ public class RbacGrantsDiagramService {
return "users"; return "users";
} }
if (refType.equals("perm")) { if (refType.equals("perm")) {
return node.idName().split(" ", 4)[3]; return node.idName().split(":", 3)[1];
} }
if (refType.equals("role")) { if (refType.equals("role")) {
final var withoutRolePrefix = node.idName().substring("role:".length()); final var withoutRolePrefix = node.idName().substring("role:".length());
@ -209,7 +209,7 @@ public class RbacGrantsDiagramService {
} }
class LimitedHashSet<T> extends HashSet<T> { static class LimitedHashSet<T> extends HashSet<T> {
@Override @Override
public boolean add(final T t) { public boolean add(final T t) {

View File

@ -23,6 +23,10 @@ components:
$ref: '#/components/schemas/HsOfficePersonType' $ref: '#/components/schemas/HsOfficePersonType'
tradeName: tradeName:
type: string type: string
salutation:
type: string
title:
type: string
givenName: givenName:
type: string type: string
familyName: familyName:
@ -35,6 +39,10 @@ components:
$ref: '#/components/schemas/HsOfficePersonType' $ref: '#/components/schemas/HsOfficePersonType'
tradeName: tradeName:
type: string type: string
salutation:
type: string
title:
type: string
givenName: givenName:
type: string type: string
familyName: familyName:
@ -51,6 +59,12 @@ components:
tradeName: tradeName:
type: string type: string
nullable: true nullable: true
salutation:
type: string
nullable: true
title:
type: string
nullable: true
givenName: givenName:
type: string type: string
nullable: true nullable: true

View File

@ -248,7 +248,7 @@ declare
objectUuidOfRole uuid; objectUuidOfRole uuid;
roleUuid uuid; roleUuid uuid;
begin begin
-- TODO.refact: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences -- TODO.refa: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences
roleParts = overlay(roleIdName placing '#' from length(roleIdName) + 1 - strpos(reverse(roleIdName), ':')); roleParts = overlay(roleIdName placing '#' from length(roleIdName) + 1 - strpos(reverse(roleIdName), ':'));
objectTableFromRoleIdName = split_part(roleParts, '#', 1); objectTableFromRoleIdName = split_part(roleParts, '#', 1);
objectNameFromRoleIdName = split_part(roleParts, '#', 2); objectNameFromRoleIdName = split_part(roleParts, '#', 2);
@ -356,16 +356,13 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
/* /*
*/ */
create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone create domain RbacOp as varchar(6)
check ( check (
VALUE = 'DELETE' VALUE = 'DELETE'
or VALUE = 'UPDATE' or VALUE = 'UPDATE'
or VALUE = 'SELECT' or VALUE = 'SELECT'
or VALUE = 'INSERT' or VALUE = 'INSERT'
or VALUE = 'ASSUME' or VALUE = 'ASSUME'
-- TODO: all values below are deprecated, use insert with table
or VALUE ~ '^add-[a-z]+$'
or VALUE ~ '^new-[a-z-]+$'
); );
create table RbacPermission create table RbacPermission
@ -417,37 +414,6 @@ begin
return permissionUuid; return permissionUuid;
end; $$; end; $$;
-- TODO: deprecated, remove and amend all usages to createPermission
create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[])
returns uuid[]
language plpgsql as $$
declare
refId uuid;
permissionIds uuid[] = array []::uuid[];
begin
if (forObjectUuid is null) then
raise exception 'forObjectUuid must not be null';
end if;
for i in array_lower(permitOps, 1)..array_upper(permitOps, 1)
loop
refId = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = permitOps[i]);
if (refId is null) then
insert
into RbacReference ("type")
values ('RbacPermission')
returning uuid into refId;
insert
into RbacPermission (uuid, objectUuid, op)
values (refId, forObjectUuid, permitOps[i]);
end if;
permissionIds = permissionIds || refId;
end loop;
return permissionIds;
end;
$$;
create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null) create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
returns uuid returns uuid
returns null on null input returns null on null input
@ -649,25 +615,6 @@ begin
end; end;
$$; $$;
-- TODO: deprecated, remove and use grantPermissionToRole(...)
create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[])
language plpgsql as $$
begin
if cardinality(permissionIds) = 0 then return; end if;
for i in array_lower(permissionIds, 1)..array_upper(permissionIds, 1)
loop
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission');
insert
into RbacGrants (grantedByTriggerOf, ascendantUuid, descendantUuid, assumed)
values (currentTriggerObjectUuid(), roleUuid, permissionIds[i], true)
on conflict do nothing; -- allow granting multiple times
end loop;
end;
$$;
create or replace procedure grantRoleToRole(subRoleId uuid, superRoleId uuid, doAssume bool = true) create or replace procedure grantRoleToRole(subRoleId uuid, superRoleId uuid, doAssume bool = true)
language plpgsql as $$ language plpgsql as $$
begin begin
@ -691,7 +638,7 @@ declare
superRoleId uuid; superRoleId uuid;
subRoleId uuid; subRoleId uuid;
begin begin
-- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references -- TODO.refa: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
if superRole.objectUuid is null or subRole.objectuuid is null then if superRole.objectUuid is null or subRole.objectuuid is null then
return; return;
end if; end if;
@ -712,30 +659,6 @@ begin
on conflict do nothing; -- allow granting multiple times on conflict do nothing; -- allow granting multiple times
end; $$; end; $$;
create or replace procedure grantRoleToRoleIfNotNull(subRole RbacRoleDescriptor, superRole RbacRoleDescriptor, doAssume bool = true)
language plpgsql as $$
declare
superRoleId uuid;
subRoleId uuid;
begin
if ( superRoleId is null ) then return; end if;
superRoleId := findRoleId(superRole);
if ( subRoleId is null ) then return; end if;
subRoleId := findRoleId(subRole);
perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole');
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
if isGranted(subRoleId, superRoleId) then
call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
end if;
insert
into RbacGrants (grantedByTriggerOf, ascendantuuid, descendantUuid, assumed)
values (currentTriggerObjectUuid(), superRoleId, subRoleId, doAssume)
on conflict do nothing; -- allow granting multiple times
end; $$;
create or replace procedure revokeRoleFromRole(subRole RbacRoleDescriptor, superRole RbacRoleDescriptor) create or replace procedure revokeRoleFromRole(subRole RbacRoleDescriptor, superRole RbacRoleDescriptor)
language plpgsql as $$ language plpgsql as $$
declare declare

View File

@ -20,19 +20,18 @@ begin
return currentSubjectsUuids[1]; return currentSubjectsUuids[1];
end; $$; end; $$;
create or replace procedure grantRoleToUserUnchecked(grantedByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true) create or replace procedure grantRoleToUserUnchecked(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)
language plpgsql as $$ language plpgsql as $$
begin begin
perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole'); perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
perform assertReferenceType('roleId (descendant)', roleUuid, 'RbacRole'); perform assertReferenceType('roleId (descendant)', grantedRoleUuid, 'RbacRole');
perform assertReferenceType('userId (ascendant)', userUuid, 'RbacUser'); perform assertReferenceType('userId (ascendant)', userUuid, 'RbacUser');
insert insert
into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed) into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
values (grantedByRoleUuid, userUuid, roleUuid, doAssume); values (grantedByRoleUuid, userUuid, grantedRoleUuid, doAssume)
-- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same? -- TODO: check if grantedByRoleUuid+doAssume are the same, otherwise raise exception?
-- Most powerful or latest grant wins? What about managed? on conflict do nothing; -- allow granting multiple times
-- on conflict do nothing; -- allow granting multiple times
end; $$; end; $$;
create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true) create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)

View File

@ -6,6 +6,7 @@
--changeset rbac-role-builder-create-role:1 endDelimiter:--// --changeset rbac-role-builder-create-role:1 endDelimiter:--//
-- ----------------------------------------------------------------- -- -----------------------------------------------------------------
-- TODO: rename to defineRoleWithGrants because it does not complain if the role already exists
create or replace function createRoleWithGrants( create or replace function createRoleWithGrants(
roleDescriptor RbacRoleDescriptor, roleDescriptor RbacRoleDescriptor,
permissions RbacOp[] = array[]::RbacOp[], permissions RbacOp[] = array[]::RbacOp[],
@ -28,7 +29,7 @@ declare
userUuid uuid; userUuid uuid;
userGrantsByRoleUuid uuid; userGrantsByRoleUuid uuid;
begin begin
roleUuid := createRole(roleDescriptor); roleUuid := coalesce(findRoleId(roleDescriptor), createRole(roleDescriptor));
foreach permission in array permissions foreach permission in array permissions
loop loop

View File

@ -20,6 +20,8 @@ create table if not exists hs_office_person
version int not null default 0, version int not null default 0,
personType HsOfficePersonType not null, personType HsOfficePersonType not null,
tradeName varchar(96), tradeName varchar(96),
salutation varchar(30),
title varchar(20),
givenName varchar(48), givenName varchar(48),
familyName varchar(48) familyName varchar(48)
); );

View File

@ -138,6 +138,8 @@ call generateRbacRestrictedView('hs_office_person',
$orderBy$, $orderBy$,
$updates$ $updates$
personType = new.personType, personType = new.personType,
title = new.title,
salutation = new.salutation,
tradeName = new.tradeName, tradeName = new.tradeName,
givenName = new.givenName, givenName = new.givenName,
familyName = new.familyName familyName = new.familyName

View File

@ -0,0 +1,102 @@
### rbac relation inCaseOf:REPRESENTATIVE
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
subgraph holderPerson["`**holderPerson**`"]
direction TB
style holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph holderPerson:roles[ ]
style holderPerson:roles fill:#99bcdb,stroke:white
role:holderPerson:OWNER[[holderPerson:OWNER]]
role:holderPerson:ADMIN[[holderPerson:ADMIN]]
role:holderPerson:REFERRER[[holderPerson:REFERRER]]
end
end
subgraph anchorPerson["`**anchorPerson**`"]
direction TB
style anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph anchorPerson:roles[ ]
style anchorPerson:roles fill:#99bcdb,stroke:white
role:anchorPerson:OWNER[[anchorPerson:OWNER]]
role:anchorPerson:ADMIN[[anchorPerson:ADMIN]]
role:anchorPerson:REFERRER[[anchorPerson:REFERRER]]
end
end
subgraph contact["`**contact**`"]
direction TB
style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
subgraph contact:roles[ ]
style contact:roles fill:#99bcdb,stroke:white
role:contact:OWNER[[contact:OWNER]]
role:contact:ADMIN[[contact:ADMIN]]
role:contact:REFERRER[[contact:REFERRER]]
end
end
subgraph relation["`**relation**`"]
direction TB
style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px
subgraph relation:roles[ ]
style relation:roles fill:#dd4901,stroke:white
role:relation:OWNER[[relation:OWNER]]
role:relation:ADMIN[[relation:ADMIN]]
role:relation:AGENT[[relation:AGENT]]
role:relation:TENANT[[relation:TENANT]]
end
subgraph relation:permissions[ ]
style relation:permissions fill:#dd4901,stroke:white
perm:relation:DELETE{{relation:DELETE}}
perm:relation:UPDATE{{relation:UPDATE}}
perm:relation:SELECT{{relation:SELECT}}
perm:relation:INSERT{{relation:INSERT}}
end
end
%% granting roles to users
user:creator ==> role:relation:OWNER
%% granting roles to roles
role:global:ADMIN -.-> role:anchorPerson:OWNER
role:anchorPerson:OWNER -.-> role:anchorPerson:ADMIN
role:anchorPerson:ADMIN -.-> role:anchorPerson:REFERRER
role:global:ADMIN -.-> role:holderPerson:OWNER
role:holderPerson:OWNER -.-> role:holderPerson:ADMIN
role:holderPerson:ADMIN -.-> role:holderPerson:REFERRER
role:global:ADMIN -.-> role:contact:OWNER
role:contact:OWNER -.-> role:contact:ADMIN
role:contact:ADMIN -.-> role:contact:REFERRER
role:global:ADMIN ==> role:relation:OWNER
role:holderPerson:ADMIN ==> role:relation:OWNER
role:relation:OWNER ==> role:relation:ADMIN
role:relation:ADMIN ==> role:anchorPerson:OWNER
role:relation:ADMIN ==> role:relation:AGENT
role:anchorPerson:ADMIN ==> role:relation:AGENT
role:relation:AGENT ==> role:relation:TENANT
role:contact:ADMIN ==> role:relation:TENANT
role:relation:TENANT ==> role:anchorPerson:REFERRER
role:relation:TENANT ==> role:holderPerson:REFERRER
role:relation:TENANT ==> role:contact:REFERRER
%% granting permissions to roles
role:relation:OWNER ==> perm:relation:DELETE
role:relation:ADMIN ==> perm:relation:UPDATE
role:relation:TENANT ==> perm:relation:SELECT
role:anchorPerson:ADMIN ==> perm:relation:INSERT
```

View File

@ -1,4 +1,4 @@
### rbac relation ### rbac relation inOtherCases
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
@ -83,15 +83,14 @@ role:contact:OWNER -.-> role:contact:ADMIN
role:contact:ADMIN -.-> role:contact:REFERRER role:contact:ADMIN -.-> role:contact:REFERRER
role:global:ADMIN ==> role:relation:OWNER role:global:ADMIN ==> role:relation:OWNER
role:relation:OWNER ==> role:relation:ADMIN role:relation:OWNER ==> role:relation:ADMIN
role:anchorPerson:ADMIN ==> role:relation:ADMIN
role:relation:ADMIN ==> role:relation:AGENT role:relation:ADMIN ==> role:relation:AGENT
role:holderPerson:ADMIN ==> role:relation:AGENT
role:relation:AGENT ==> role:relation:TENANT role:relation:AGENT ==> role:relation:TENANT
role:holderPerson:ADMIN ==> role:relation:TENANT
role:contact:ADMIN ==> role:relation:TENANT role:contact:ADMIN ==> role:relation:TENANT
role:relation:TENANT ==> role:anchorPerson:REFERRER role:relation:TENANT ==> role:anchorPerson:REFERRER
role:relation:TENANT ==> role:holderPerson:REFERRER role:relation:TENANT ==> role:holderPerson:REFERRER
role:relation:TENANT ==> role:contact:REFERRER role:relation:TENANT ==> role:contact:REFERRER
role:anchorPerson:ADMIN ==> role:relation:OWNER
role:holderPerson:ADMIN ==> role:relation:AGENT
%% granting permissions to roles %% granting permissions to roles
role:relation:OWNER ==> perm:relation:DELETE role:relation:OWNER ==> perm:relation:DELETE

View File

@ -57,16 +57,12 @@ begin
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationADMIN(NEW), hsOfficeRelationADMIN(NEW),
permissions => array['UPDATE'], permissions => array['UPDATE'],
incomingSuperRoles => array[ incomingSuperRoles => array[hsOfficeRelationOWNER(NEW)]
hsOfficePersonADMIN(newAnchorPerson),
hsOfficeRelationOWNER(NEW)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeRelationAGENT(NEW), hsOfficeRelationAGENT(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[hsOfficeRelationADMIN(NEW)]
hsOfficePersonADMIN(newHolderPerson),
hsOfficeRelationADMIN(NEW)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
@ -74,7 +70,6 @@ begin
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeContactADMIN(newContact), hsOfficeContactADMIN(newContact),
hsOfficePersonADMIN(newHolderPerson),
hsOfficeRelationAGENT(NEW)], hsOfficeRelationAGENT(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeContactREFERRER(newContact), hsOfficeContactREFERRER(newContact),
@ -82,6 +77,15 @@ begin
hsOfficePersonREFERRER(newHolderPerson)] hsOfficePersonREFERRER(newHolderPerson)]
); );
IF NEW.type = 'REPRESENTATIVE' THEN
call grantRoleToRole(hsOfficePersonOWNER(newAnchorPerson), hsOfficeRelationADMIN(NEW));
call grantRoleToRole(hsOfficeRelationAGENT(NEW), hsOfficePersonADMIN(newAnchorPerson));
call grantRoleToRole(hsOfficeRelationOWNER(NEW), hsOfficePersonADMIN(newHolderPerson));
ELSE
call grantRoleToRole(hsOfficeRelationAGENT(NEW), hsOfficePersonADMIN(newHolderPerson));
call grantRoleToRole(hsOfficeRelationOWNER(NEW), hsOfficePersonADMIN(newAnchorPerson));
END IF;
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);
end; $$; end; $$;
@ -118,48 +122,12 @@ create or replace procedure updateRbacRulesForHsOfficeRelation(
NEW hs_office_relation NEW hs_office_relation
) )
language plpgsql as $$ language plpgsql as $$
declare
oldHolderPerson hs_office_person;
newHolderPerson hs_office_person;
oldAnchorPerson hs_office_person;
newAnchorPerson hs_office_person;
oldContact hs_office_contact;
newContact hs_office_contact;
begin begin
call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.holderUuid INTO oldHolderPerson;
assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.anchorUuid INTO oldAnchorPerson;
assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
SELECT * FROM hs_office_contact WHERE uuid = OLD.contactUuid INTO oldContact;
assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid);
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
if NEW.contactUuid <> OLD.contactUuid then
call revokeRoleFromRole(hsOfficeRelationTENANT(OLD), hsOfficeContactADMIN(oldContact));
call grantRoleToRole(hsOfficeRelationTENANT(NEW), hsOfficeContactADMIN(newContact));
call revokeRoleFromRole(hsOfficeContactREFERRER(oldContact), hsOfficeRelationTENANT(OLD));
call grantRoleToRole(hsOfficeContactREFERRER(newContact), hsOfficeRelationTENANT(NEW));
if NEW.contactUuid is distinct from OLD.contactUuid then
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
call buildRbacSystemForHsOfficeRelation(NEW);
end if; end if;
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$; end; $$;
/* /*

View File

@ -98,22 +98,21 @@ role:partnerRel.contact:OWNER -.-> role:partnerRel.contact:ADMIN
role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER
role:global:ADMIN -.-> role:partnerRel:OWNER role:global:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel:OWNER -.-> role:partnerRel:ADMIN role:partnerRel:OWNER -.-> role:partnerRel:ADMIN
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:ADMIN
role:partnerRel:ADMIN -.-> role:partnerRel:AGENT role:partnerRel:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel:AGENT -.-> role:partnerRel:TENANT role:partnerRel:AGENT -.-> role:partnerRel:TENANT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
%% granting permissions to roles %% granting permissions to roles
role:global:ADMIN ==> perm:partner:INSERT role:global:ADMIN ==> perm:partner:INSERT
role:partnerRel:ADMIN ==> perm:partner:DELETE role:partnerRel:OWNER ==> perm:partner:DELETE
role:partnerRel:AGENT ==> perm:partner:UPDATE role:partnerRel:ADMIN ==> perm:partner:UPDATE
role:partnerRel:TENANT ==> perm:partner:SELECT role:partnerRel:TENANT ==> perm:partner:SELECT
role:partnerRel:ADMIN ==> perm:partnerDetails:DELETE role:partnerRel:OWNER ==> perm:partnerDetails:DELETE
role:partnerRel:AGENT ==> perm:partnerDetails:UPDATE role:partnerRel:AGENT ==> perm:partnerDetails:UPDATE
role:partnerRel:AGENT ==> perm:partnerDetails:SELECT role:partnerRel:AGENT ==> perm:partnerDetails:SELECT

View File

@ -42,10 +42,10 @@ begin
SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationADMIN(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOWNER(newPartnerRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTENANT(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTENANT(newPartnerRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationADMIN(newPartnerRel));
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationADMIN(newPartnerRel)); call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationOWNER(newPartnerRel));
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAGENT(newPartnerRel)); call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAGENT(newPartnerRel));
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel)); call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel));
@ -110,17 +110,17 @@ begin
if NEW.partnerRelUuid <> OLD.partnerRelUuid then if NEW.partnerRelUuid <> OLD.partnerRelUuid then
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationADMIN(oldPartnerRel)); call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationOWNER(oldPartnerRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationADMIN(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOWNER(newPartnerRel));
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationAGENT(oldPartnerRel)); call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationADMIN(oldPartnerRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationADMIN(newPartnerRel));
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTENANT(oldPartnerRel)); call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTENANT(oldPartnerRel));
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTENANT(newPartnerRel)); call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTENANT(newPartnerRel));
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationADMIN(oldPartnerRel)); call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationOWNER(oldPartnerRel));
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationADMIN(newPartnerRel)); call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationOWNER(newPartnerRel));
call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(oldPartnerRel)); call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(oldPartnerRel));
call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel)); call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAGENT(newPartnerRel));

View File

@ -151,15 +151,14 @@ role:debitorRel.contact:OWNER -.-> role:debitorRel.contact:ADMIN
role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitorRel:OWNER role:global:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel:OWNER -.-> role:debitorRel:ADMIN role:debitorRel:OWNER -.-> role:debitorRel:ADMIN
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:ADMIN
role:debitorRel:ADMIN -.-> role:debitorRel:AGENT role:debitorRel:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT -.-> role:debitorRel:TENANT role:debitorRel:AGENT -.-> role:debitorRel:TENANT
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:global:ADMIN -.-> role:refundBankAccount:OWNER role:global:ADMIN -.-> role:refundBankAccount:OWNER
role:refundBankAccount:OWNER -.-> role:refundBankAccount:ADMIN role:refundBankAccount:OWNER -.-> role:refundBankAccount:ADMIN
role:refundBankAccount:ADMIN -.-> role:refundBankAccount:REFERRER role:refundBankAccount:ADMIN -.-> role:refundBankAccount:REFERRER
@ -176,15 +175,14 @@ role:partnerRel.contact:OWNER -.-> role:partnerRel.contact:ADMIN
role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER
role:global:ADMIN -.-> role:partnerRel:OWNER role:global:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel:OWNER -.-> role:partnerRel:ADMIN role:partnerRel:OWNER -.-> role:partnerRel:ADMIN
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:ADMIN
role:partnerRel:ADMIN -.-> role:partnerRel:AGENT role:partnerRel:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel:AGENT -.-> role:partnerRel:TENANT role:partnerRel:AGENT -.-> role:partnerRel:TENANT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel:ADMIN ==> role:debitorRel:ADMIN role:partnerRel:ADMIN ==> role:debitorRel:ADMIN
role:partnerRel:AGENT ==> role:debitorRel:AGENT role:partnerRel:AGENT ==> role:debitorRel:AGENT
role:debitorRel:AGENT ==> role:partnerRel:TENANT role:debitorRel:AGENT ==> role:partnerRel:TENANT

View File

@ -110,15 +110,14 @@ role:debitorRel.contact:OWNER -.-> role:debitorRel.contact:ADMIN
role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitorRel:OWNER role:global:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel:OWNER -.-> role:debitorRel:ADMIN role:debitorRel:OWNER -.-> role:debitorRel:ADMIN
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:ADMIN
role:debitorRel:ADMIN -.-> role:debitorRel:AGENT role:debitorRel:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT -.-> role:debitorRel:TENANT role:debitorRel:AGENT -.-> role:debitorRel:TENANT
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:global:ADMIN -.-> role:bankAccount:OWNER role:global:ADMIN -.-> role:bankAccount:OWNER
role:bankAccount:OWNER -.-> role:bankAccount:ADMIN role:bankAccount:OWNER -.-> role:bankAccount:ADMIN
role:bankAccount:ADMIN -.-> role:bankAccount:REFERRER role:bankAccount:ADMIN -.-> role:bankAccount:REFERRER

View File

@ -96,15 +96,14 @@ role:partnerRel.contact:OWNER -.-> role:partnerRel.contact:ADMIN
role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER role:partnerRel.contact:ADMIN -.-> role:partnerRel.contact:REFERRER
role:global:ADMIN -.-> role:partnerRel:OWNER role:global:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel:OWNER -.-> role:partnerRel:ADMIN role:partnerRel:OWNER -.-> role:partnerRel:ADMIN
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:ADMIN
role:partnerRel:ADMIN -.-> role:partnerRel:AGENT role:partnerRel:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
role:partnerRel:AGENT -.-> role:partnerRel:TENANT role:partnerRel:AGENT -.-> role:partnerRel:TENANT
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER
role:partnerRel.anchorPerson:ADMIN -.-> role:partnerRel:OWNER
role:partnerRel.holderPerson:ADMIN -.-> role:partnerRel:AGENT
role:membership:OWNER ==> role:membership:ADMIN role:membership:OWNER ==> role:membership:ADMIN
role:partnerRel:ADMIN ==> role:membership:ADMIN role:partnerRel:ADMIN ==> role:membership:ADMIN
role:membership:ADMIN ==> role:membership:AGENT role:membership:ADMIN ==> role:membership:AGENT

View File

@ -97,15 +97,14 @@ role:membership.partnerRel.contact:OWNER -.-> role:membership.partnerRel.contact
role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER
role:global:ADMIN -.-> role:membership.partnerRel:OWNER role:global:ADMIN -.-> role:membership.partnerRel:OWNER
role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN
role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:ADMIN
role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER
role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:OWNER
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership:OWNER -.-> role:membership:ADMIN role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:AGENT role:membership:ADMIN -.-> role:membership:AGENT

View File

@ -97,15 +97,14 @@ role:membership.partnerRel.contact:OWNER -.-> role:membership.partnerRel.contact
role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER
role:global:ADMIN -.-> role:membership.partnerRel:OWNER role:global:ADMIN -.-> role:membership.partnerRel:OWNER
role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN
role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:ADMIN
role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER
role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:OWNER
role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT
role:membership:OWNER -.-> role:membership:ADMIN role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:AGENT role:membership:ADMIN -.-> role:membership:AGENT

View File

@ -186,13 +186,13 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant perm:debitor#D-1000122:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }", "{ grant perm:debitor#D-1000122:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
"{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }", "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to role:global#global:ADMIN by system and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to role:global#global:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to role:person#FirstGmbH:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to user:superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to user:superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER and assume }",
// admin // admin
"{ grant perm:debitor#D-1000122:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }", "{ grant perm:debitor#D-1000122:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
"{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }", "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:person#FirstGmbH:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
// agent // agent
@ -208,7 +208,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role:person#FirstGmbH:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }", "{ grant role:person#FirstGmbH:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
"{ grant role:person#FourtheG:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }", "{ grant role:person#FourtheG:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:contact#fourthcontact:ADMIN by system and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:contact#fourthcontact:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:person#FourtheG:ADMIN by system and assume }",
"{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT by system and assume }", "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT by system and assume }",
null)); null));

View File

@ -134,6 +134,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
"{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:AGENT by system and assume }", "{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:AGENT by system and assume }",
"{ grant role:membership#M-1000117:AGENT to role:membership#M-1000117:ADMIN by system and assume }", "{ grant role:membership#M-1000117:AGENT to role:membership#M-1000117:ADMIN by system and assume }",
// referrer
"{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }", "{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:AGENT by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:AGENT by system and assume }",

View File

@ -218,23 +218,6 @@ public class ImportOfficeData extends ContextBasedTest {
} }
} }
@Test
@Order(1021)
void buildDebitorRelations() {
debitors.forEach( (id, debitor) -> {
final var debitorRel = HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
.anchor(debitor.getPartner().getPartnerRel().getHolder())
.holder(debitor.getPartner().getPartnerRel().getHolder()) // just 1 debitor/partner in legacy hsadmin
// FIXME .contact()
.build();
if (debitorRel.getAnchor() != null && debitorRel.getHolder() != null &&
debitorRel.getContact() != null ) {
relations.put(relationId++, debitorRel);
}
});
}
@Test @Test
@Order(1029) @Order(1029)
void verifyContacts() { void verifyContacts() {
@ -292,29 +275,25 @@ public class ImportOfficeData extends ContextBasedTest {
{ {
2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000001=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000001=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000002=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000002=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000003=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000003=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
2000004=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), 2000004=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000005=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), 2000005=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000006=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000006=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
2000007=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000007=rel(anchor='null null, null', type='DEBITOR'),
2000008=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000008=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000009=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'), 2000009=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000010=rel(anchor='null null, null', type='DEBITOR'), 2000010=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
2000011=rel(anchor='null null, null', type='DEBITOR'), 2000011=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000012=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000012=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000013=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000013=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000014=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'), 2000014=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000015=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000015=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000016=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000016=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000017=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000017=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
2000018=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000018=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000019=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000019=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000020=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000020=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga ')
2000021=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
2000022=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000023=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000024=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga ')
} }
"""); """);
} }
@ -425,14 +404,33 @@ public class ImportOfficeData extends ContextBasedTest {
} }
@Test @Test
@Order(2009) @Order(3001)
void removeSelfRepresentativeRelations() {
assumeThatWeAreImportingControlledTestData();
// this happens if a natural person is marked as 'contractual' for itself
final var idsToRemove = new HashSet<Integer>();
relations.forEach( (id, r) -> {
if (r.getHolder() == r.getAnchor() ) {
idsToRemove.add(id);
}
});
// remove self-representatives
idsToRemove.forEach(id -> {
System.out.println("removing self representative relation: " + relations.get(id).toString());
relations.remove(id);
});
}
@Test
@Order(3002)
void removeEmptyRelations() { void removeEmptyRelations() {
assumeThatWeAreImportingControlledTestData(); assumeThatWeAreImportingControlledTestData();
// avoid a error when persisting the deliberately invalid partner entry #99 // avoid a error when persisting the deliberately invalid partner entry #99
final var idsToRemove = new HashSet<Integer>(); final var idsToRemove = new HashSet<Integer>();
relations.forEach( (id, r) -> { relations.forEach( (id, r) -> {
// such a record
if (r.getContact() == null || r.getContact().getLabel() == null || if (r.getContact() == null || r.getContact().getLabel() == null ||
r.getHolder() == null || r.getHolder().getPersonType() == null ) { r.getHolder() == null || r.getHolder().getPersonType() == null ) {
idsToRemove.add(id); idsToRemove.add(id);
@ -447,7 +445,7 @@ public class ImportOfficeData extends ContextBasedTest {
} }
@Test @Test
@Order(2002) @Order(3003)
void removeEmptyPartners() { void removeEmptyPartners() {
assumeThatWeAreImportingControlledTestData(); assumeThatWeAreImportingControlledTestData();
@ -471,7 +469,7 @@ public class ImportOfficeData extends ContextBasedTest {
} }
@Test @Test
@Order(2003) @Order(3004)
void removeEmptyDebitors() { void removeEmptyDebitors() {
assumeThatWeAreImportingControlledTestData(); assumeThatWeAreImportingControlledTestData();
@ -490,7 +488,7 @@ public class ImportOfficeData extends ContextBasedTest {
} }
@Test @Test
@Order(3000) @Order(9000)
@Commit @Commit
void persistEntities() { void persistEntities() {
@ -516,6 +514,7 @@ public class ImportOfficeData extends ContextBasedTest {
relations.forEach(this::persist); relations.forEach(this::persist);
}).assertSuccessful(); }).assertSuccessful();
System.out.println("persisting " + partners.size() + " partners");
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context(rbacSuperuser); context(rbacSuperuser);
partners.forEach((id, partner) -> { partners.forEach((id, partner) -> {
@ -533,7 +532,7 @@ public class ImportOfficeData extends ContextBasedTest {
context(rbacSuperuser); context(rbacSuperuser);
debitors.forEach((id, debitor) -> { debitors.forEach((id, debitor) -> {
debitor.setDebitorRel(em.merge(debitor.getDebitorRel())); debitor.setDebitorRel(em.merge(debitor.getDebitorRel()));
em.persist(debitor); persist(id, debitor);
}); });
}).assertSuccessful(); }).assertSuccessful();
@ -721,7 +720,6 @@ public class ImportOfficeData extends ContextBasedTest {
null, // will be set in contacts import null, // will be set in contacts import
null // will beset in contacts import null // will beset in contacts import
); );
relations.put(relationId++, debitorRel);
final var debitor = HsOfficeDebitorEntity.builder() final var debitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix("00") .debitorNumberSuffix("00")

View File

@ -10,7 +10,6 @@ import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@ -25,13 +24,13 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectEntity.objectDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectEntity.objectDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf; import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.Array.fromFormatted; import static net.hostsharing.test.Array.from;
import static net.hostsharing.test.JpaAttempt.attempt; import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -130,7 +129,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
}).assertSuccessful(); }).assertSuccessful();
// then // then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(from(
initialRoleNames, initialRoleNames,
"hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:OWNER", "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:OWNER",
"hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:ADMIN", "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:ADMIN",
@ -140,17 +139,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess")) .map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("fourthcontact", "4th")) .map(s -> s.replace("fourthcontact", "4th"))
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(fromFormatted( .containsExactlyInAnyOrder(distinct(from(
initialGrantNames, initialGrantNames,
"{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:INSERT>sepamandate to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }", "{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:INSERT>sepamandate to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
// permissions on partner // permissions on partner
"{ grant perm:partner#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }", "{ grant perm:partner#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",
"{ grant perm:partner#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }", "{ grant perm:partner#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
"{ grant perm:partner#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }", "{ grant perm:partner#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
// permissions on partner-details // permissions on partner-details
"{ grant perm:partner_details#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }", "{ grant perm:partner_details#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",
"{ grant perm:partner_details#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }", "{ grant perm:partner_details#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
"{ grant perm:partner_details#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }", "{ grant perm:partner_details#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
@ -165,7 +164,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// relation admin // relation admin
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN to role:person#HostsharingeG:ADMIN by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:OWNER to role:person#HostsharingeG:ADMIN by system and assume }",
// relation agent // relation agent
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:AGENT to role:person#EBess:ADMIN by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:AGENT to role:person#EBess:ADMIN by system and assume }",
@ -176,7 +175,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role:person#EBess:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }", "{ grant role:person#EBess:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
"{ grant role:person#HostsharingeG:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }", "{ grant role:person#HostsharingeG:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:contact#4th:ADMIN by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:contact#4th:ADMIN by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:person#EBess:ADMIN by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
null))); null)));
} }
@ -411,9 +409,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
public void deletingAPartnerAlsoDeletesRelatedRolesAndGrants() { public void deletingAPartnerAlsoDeletesRelatedRolesAndGrants() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialObjects = Array.from(objectDisplaysOf(rawObjectRepo.findAll())); final var initialObjects = from(objectDisplaysOf(rawObjectRepo.findAll()));
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll())); final var initialRoleNames = from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
final var givenPartner = givenSomeTemporaryHostsharingPartner(20034, "Erben Bessler", "twelfth"); final var givenPartner = givenSomeTemporaryHostsharingPartner(20034, "Erben Bessler", "twelfth");
// when // when
@ -499,8 +497,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
private String[] distinct(final String[] strings) { private String[] distinct(final String[] strings) {
// TODO: alternatively cleanup all rbac objects in @AfterEach? // TODO: alternatively cleanup all rbac objects in @AfterEach?
final var set = new HashSet<String>(); return Arrays.stream(strings).filter(Objects::nonNull).distinct().toList().toArray(new String[0]);
set.addAll(List.of(strings));
return set.toArray(new String[0]);
} }
} }

View File

@ -23,7 +23,9 @@ class HsOfficePersonEntityPatcherUnitTest extends PatchUnitTestBase<
final var entity = new HsOfficePersonEntity(); final var entity = new HsOfficePersonEntity();
entity.setUuid(INITIAL_PERSON_UUID); entity.setUuid(INITIAL_PERSON_UUID);
entity.setPersonType(HsOfficePersonType.LEGAL_PERSON); entity.setPersonType(HsOfficePersonType.LEGAL_PERSON);
entity.setTradeName("initial@example.org"); entity.setTradeName("initial trade name");
entity.setTitle("Dr. Init.");
entity.setSalutation("Herr Initial");
entity.setFamilyName("initial postal address"); entity.setFamilyName("initial postal address");
entity.setGivenName("+01 100 123456789"); entity.setGivenName("+01 100 123456789");
return entity; return entity;
@ -54,6 +56,16 @@ class HsOfficePersonEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficePersonPatchResource::setTradeName, HsOfficePersonPatchResource::setTradeName,
"patched trade name", "patched trade name",
HsOfficePersonEntity::setTradeName), HsOfficePersonEntity::setTradeName),
new JsonNullableProperty<>(
"title",
HsOfficePersonPatchResource::setTitle,
"Dr. Patch.",
HsOfficePersonEntity::setTitle),
new JsonNullableProperty<>(
"salutation",
HsOfficePersonPatchResource::setSalutation,
"Hallo Ini",
HsOfficePersonEntity::setSalutation),
new JsonNullableProperty<>( new JsonNullableProperty<>(
"familyName", "familyName",
HsOfficePersonPatchResource::setFamilyName, HsOfficePersonPatchResource::setFamilyName,

View File

@ -60,19 +60,63 @@ class HsOfficePersonEntityUnitTest {
assertThat(actualDisplay).isEqualTo("NP some family name, some given name"); assertThat(actualDisplay).isEqualTo("NP some family name, some given name");
} }
@Test
void toShortStringWithSalutationAndTitleReturnsSalutationAndTitle() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL_PERSON)
.salutation("Frau")
.title("Dr.")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toShortString();
assertThat(actualDisplay).isEqualTo("NP some family name, some given name");
}
@Test
void toShortStringWithSalutationAndWithoutTitleReturnsSalutation() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL_PERSON)
.salutation("Frau")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toShortString();
assertThat(actualDisplay).isEqualTo("NP Frau some family name, some given name");
}
@Test
void toShortStringWithoutSalutationAndWithTitleReturnsTitle() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.NATURAL_PERSON)
.title("Dr. Dr.")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toShortString();
assertThat(actualDisplay).isEqualTo("NP some family name, some given name");
}
@Test @Test
void toStringWithAllFieldsReturnsAllButUuid() { void toStringWithAllFieldsReturnsAllButUuid() {
final var givenPersonEntity = HsOfficePersonEntity.builder() final var givenPersonEntity = HsOfficePersonEntity.builder()
.uuid(UUID.randomUUID()) .uuid(UUID.randomUUID())
.personType(HsOfficePersonType.NATURAL_PERSON) .personType(HsOfficePersonType.NATURAL_PERSON)
.tradeName("some trade name") .tradeName("some trade name")
.title("Dr.")
.familyName("some family name") .familyName("some family name")
.givenName("some given name") .givenName("some given name")
.build(); .build();
final var actualDisplay = givenPersonEntity.toString(); final var actualDisplay = givenPersonEntity.toString();
assertThat(actualDisplay).isEqualTo("person(personType='NP', tradeName='some trade name', familyName='some family name', givenName='some given name')"); assertThat(actualDisplay).isEqualTo("person(personType='NP', tradeName='some trade name', title='Dr.', familyName='some family name', givenName='some given name')");
} }
@Test @Test
@ -86,4 +130,42 @@ class HsOfficePersonEntityUnitTest {
assertThat(actualDisplay).isEqualTo("person(familyName='some family name', givenName='some given name')"); assertThat(actualDisplay).isEqualTo("person(familyName='some family name', givenName='some given name')");
} }
@Test
void toStringWithSalutationAndTitleRetursSalutationAndTitle() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.salutation("Herr")
.title("Prof. Dr.")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toString();
assertThat(actualDisplay).isEqualTo("person(salutation='Herr', title='Prof. Dr.', familyName='some family name', givenName='some given name')");
}
@Test
void toStringWithSalutationAndWithoutTitleSkipsTitle() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.salutation("Herr")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toString();
assertThat(actualDisplay).isEqualTo("person(salutation='Herr', familyName='some family name', givenName='some given name')");
}
@Test
void toStringWithoutSalutationAndWithTitleSkipsSalutation() {
final var givenPersonEntity = HsOfficePersonEntity.builder()
.title("some title")
.familyName("some family name")
.givenName("some given name")
.build();
final var actualDisplay = givenPersonEntity.toString();
assertThat(actualDisplay).isEqualTo("person(title='some title', familyName='some family name', givenName='some given name')");
}
} }

View File

@ -362,7 +362,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact"); assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)

View File

@ -140,9 +140,10 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }", "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_person#ErbenBesslerMelBessler:OWNER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }", "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
@ -153,8 +154,6 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// REPRESENTATIVE holder person -> (represented) anchor person // REPRESENTATIVE holder person -> (represented) anchor person
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
null) null)
); );
} }
@ -217,10 +216,10 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelation = givenSomeTemporaryRelationBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Bert", "fifth contact"); "Bert", "fifth contact");
assertThatRelationActuallyInDatabase(givenRelation);
assertThatRelationIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelation, givenRelation,
"hs_office_person#ErbenBesslerMelBessler:ADMIN"); "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow(); final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow();
@ -249,19 +248,19 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
} }
@Test @Test
public void holderAdmin_canNotUpdateRelatedRelation() { public void relationAgent_canSelectButNotUpdateRelatedRelation() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelation = givenSomeTemporaryRelationBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "eighth"); "Anita", "eighth");
assertThatRelationIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelation, givenRelation,
"hs_office_person#BesslerAnita:ADMIN"); "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT");
assertThatRelationActuallyInDatabase(givenRelation); assertThatRelationActuallyInDatabase(givenRelation);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita:ADMIN"); context("superuser-alex@hostsharing.net", "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT");
givenRelation.setContact(null); givenRelation.setContact(null);
return relationRepo.save(givenRelation); return relationRepo.save(givenRelation);
}); });

View File

@ -17,7 +17,6 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository; import org.springframework.data.repository.Repository;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import static java.lang.System.out; import static java.lang.System.out;
@ -272,12 +271,11 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
/** /**
* Generates a diagram of the RBAC-Grants to the current subjects (user or assumed roles). * Generates a diagram of the RBAC-Grants to the current subjects (user or assumed roles).
*/ */
protected void generateRbacDiagramForCurrentSubjects(final EnumSet<RbacGrantsDiagramService.Include> include) { protected void generateRbacDiagramForCurrentSubjects(final EnumSet<RbacGrantsDiagramService.Include> include, final String name) {
final var title = testInfo.getTestMethod().map(Method::getName).orElseThrow();
RbacGrantsDiagramService.writeToFile( RbacGrantsDiagramService.writeToFile(
title, name,
diagramService.allGrantsToCurrentUser(include), diagramService.allGrantsToCurrentUser(include),
"doc/" + title + ".md" "doc/temp/" + name + ".md"
); );
} }