move Parter+Debitor person+contact to related Relationsship #20

Merged
hsh-michaelhoennig merged 101 commits from remove-direct-partner-person-and-contact into master 2024-03-28 12:15:14 +01:00
4 changed files with 45 additions and 120 deletions
Showing only changes of commit bb3f979273 - Show all commits

View File

@ -12,6 +12,8 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
import java.io.IOException;
@ -65,6 +67,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
WHERE pRel.relHolderUuid = dRel.relAnchorUuid
)
""")
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerEntity partner;
@Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
@ -160,8 +163,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
dependsOnColumn("refundBankAccountUuid"),
fetchedBySql("""
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
FROM hs_office_bankaccount AS b
WHERE b.uuid = ${REF}.refundBankAccountUuid
"""),
NULLABLE
)

View File

@ -21,6 +21,8 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.
@Service
public class RbacGrantsDiagramService {
private static final int GRANT_LIMIT = 500;
public static void writeToFile(final String title, final String graph, final String fileName) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
@ -59,7 +61,7 @@ public class RbacGrantsDiagramService {
private EntityManager em;
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
final var graph = new HashSet<RawRbacGrantEntity>();
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
traverseGrantsTo(graph, subjectUuid, includes);
}
@ -96,7 +98,7 @@ public class RbacGrantsDiagramService {
.setParameter("targetObject", targetObject)
.setParameter("op", op)
.getSingleResult();
final var graph = new HashSet<RawRbacGrantEntity>();
final var graph = new LimitedHashSet<RawRbacGrantEntity>();
traverseGrantsFrom(graph, refUuid, includes);
return toMermaidFlowchart(graph, includes);
}
@ -143,6 +145,7 @@ public class RbacGrantsDiagramService {
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
+ (grants.length() > GRANT_LIMIT ? "%% too many grants, graph is cropped\n" : "")
+ "flowchart TB\n\n"
+ entities
+ grants;
@ -208,6 +211,20 @@ public class RbacGrantsDiagramService {
return idName.replace(" ", ":").replaceAll("@.*", "")
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
}
class LimitedHashSet<T> extends HashSet<T> {
@Override
public boolean add(final T t) {
if (size() < GRANT_LIMIT ) {
return super.add(t);
} else {
return false;
}
}
}
}
record Node(String idName, UUID uuid) {

View File

@ -53,8 +53,8 @@ begin
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
FROM hs_office_bankaccount AS b
WHERE b.uuid = NEW.refundbankaccountuuid
INTO newRefundBankAccount;
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
@ -113,103 +113,9 @@ declare
newRefundBankAccount hs_office_bankaccount;
begin
call enterTriggerForObjectUuid(NEW.uuid);
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND OLD.debitorRelUuid = debitorRel.uuid
INTO oldPartnerRel;
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
SELECT partnerRel.*
FROM hs_office_relationship AS partnerRel
JOIN hs_office_relationship AS debitorRel
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
WHERE partnerRel.relType = 'PARTNER'
AND NEW.debitorRelUuid = debitorRel.uuid
INTO newPartnerRel;
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.uuid = OLD.debitorRelUuid
INTO oldDebitorRel;
assert oldDebitorRel.uuid is not null, format('oldDebitorRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid
INTO newDebitorRel;
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = OLD.debitorRelUuid
INTO oldRefundBankAccount;
SELECT *
FROM hs_office_relationship AS r
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
INTO newRefundBankAccount;
if NEW.debitorRelUuid <> OLD.debitorRelUuid then
assert OLD.uuid=NEW.uuid, 'NEW vs. OLD uuids must be equal';
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationshipOwner(oldDebitorRel));
call grantPermissionToRole(getPermissionId(NEW.uuid, 'DELETE'), hsOfficeRelationshipOwner(newDebitorRel));
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationshipAdmin(oldDebitorRel));
call grantPermissionToRole(getPermissionId(NEW.uuid, 'UPDATE'), hsOfficeRelationshipAdmin(newDebitorRel));
call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationshipTenant(oldDebitorRel));
call grantPermissionToRole(getPermissionId(NEW.uuid, 'SELECT'), hsOfficeRelationshipTenant(newDebitorRel));
if oldRefundBankAccount is not null then
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldRefundBankAccount));
end if;
if newRefundBankAccount is not null then
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
end if;
if oldRefundBankAccount is not null then
call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldRefundBankAccount), hsOfficeRelationshipAgent(oldDebitorRel));
end if;
if newRefundBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
end if;
call revokeRoleFromRole(hsOfficeRelationshipAdmin(oldDebitorRel), hsOfficeRelationshipAdmin(oldPartnerRel));
call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel));
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeRelationshipAgent(oldPartnerRel));
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel));
call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRel), hsOfficeRelationshipAgent(oldDebitorRel));
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel));
end if;
if NEW.refundBankAccountUuid <> OLD.refundBankAccountUuid then
if oldRefundBankAccount is not null then
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldRefundBankAccount));
end if;
if newRefundBankAccount is not null then
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
end if;
if oldRefundBankAccount is not null then
call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldRefundBankAccount), hsOfficeRelationshipAgent(oldDebitorRel));
end if;
if newRefundBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
end if;
end if;
call leaveTriggerForObjectUuid(NEW.uuid);
call buildRbacSystemForHsOfficeDebitor(NEW);
end; $$;
/*

View File

@ -319,6 +319,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
givenDebitor,
"hs_office_relationship#FourtheG-with-ACCOUNTING-FourtheG.admin");
final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
final var givenNewBillingPerson = one(personRepo.findPersonByOptionalNameLike("Firby"));
final var givenNewContact = one(contactRepo.findContactByOptionalLabelLike("sixth contact"));
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
final String givenNewVatId = "NEW-VAT-ID";
@ -331,7 +332,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
givenDebitor.setDebitorRel(HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenNewPartnerPerson)
.relHolder(givenNewPartnerPerson)
.relHolder(givenNewBillingPerson)
.contact(givenNewContact)
.build());
givenDebitor.setRefundBankAccount(givenNewBankAccount);
@ -353,7 +354,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"hs_office_relationship#FourtheG-with-ACCOUNTING-FourtheG.admin");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_relationship#FirstGmbH-with-ACCOUNTING-FirstGmbH.agent");
"hs_office_relationship#FirstGmbH-with-ACCOUNTING-FirbySusan.agent");
// ... contact role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
@ -366,10 +367,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// ... bank-account role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#FourtheG.admin");
"hs_office_bankaccount#DE02200505501015871393.admin");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#FirstGmbH.admin");
"hs_office_bankaccount#DE02120300000000202051.admin");
}
@Test
@ -379,7 +380,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
"hs_office_relationship#FourtheG-with-ACCOUNTING-FourtheG.admin");
assertThatDebitorActuallyInDatabase(givenDebitor);
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
@ -399,7 +400,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// ... bank-account role was assigned:
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#FirstGmbH.admin");
"hs_office_bankaccount#DE02120300000000202051.admin");
}
@Test
@ -409,7 +410,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fih");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_partner#10004:FourtheG-fourthcontact.admin");
"hs_office_relationship#HostsharingeG-with-PARTNER-FourtheG.agent");
assertThatDebitorActuallyInDatabase(givenDebitor);
// when
@ -428,33 +429,29 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// ... bank-account role was removed from previous bank-account admin:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#FourtheG.admin");
"hs_office_bankaccount#DE02200505501015871393.admin");
}
@Test
public void partnerAdmin_canNotUpdateRelatedDebitor() {
public void partnerAgent_canNotUpdateRelatedDebitor() {
// given
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_relationship#HostsharingeG-with-PARTNER-FourtheG.admin");
"hs_office_relationship#HostsharingeG-with-PARTNER-FourtheG.agent");
assertThatDebitorActuallyInDatabase(givenDebitor);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_relationship#HostsharingeG-with-PARTNER-FourtheG.admin");
context("superuser-alex@hostsharing.net", "hs_office_relationship#HostsharingeG-with-PARTNER-FourtheG.agent");
givenDebitor.setVatId("NEW-VAT-ID");
return toCleanup(debitorRepo.save(givenDebitor));
});
// then
// FIXME: This error message would be better:
// result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
// "[403] Subject ", " is not allowed to update hs_office_debitor uuid");
result.assertExceptionWithRootCauseMessage(
JpaObjectRetrievalFailureException.class,
"Unable to find net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity with id ");
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
"[403] Subject ", " is not allowed to update hs_office_debitor uuid");
}
@Test
@ -489,7 +486,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
found.ifPresent(foundEntity -> {
em.refresh(foundEntity);
assertThat(foundEntity).isNotSameAs(saved);
//assertThat(foundEntity.getPartner()).isNotNull();
if ( saved.getPartner() != null) { // FIXME: check, why there is no partner for the updated contact
assertThat(foundEntity.getPartner()).isNotNull();
}
assertThat(foundEntity.getDebitorRel()).extracting(HsOfficeRelationshipEntity::toString)
.isEqualTo(saved.getDebitorRel().toString());
});