Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Hoennig
717bdca948 WIP 2024-02-16 17:04:48 +01:00
Michael Hoennig
84ce1e34bf Merge branch 'fix-findPermissionId' into remove-direct-partner-person-and-contact
# Conflicts:
#	src/main/resources/db/changelog/050-rbac-base.sql
2024-02-16 16:55:58 +01:00
Michael Hoennig
36654a69d8 fix misleading findPermissionId naming 2024-02-16 16:48:37 +01:00
Michael Hoennig
85ad05a77e improve RbacGrantsMermaidService formatting 2024-02-15 17:20:50 +01:00
10 changed files with 215 additions and 87 deletions

View File

@ -74,7 +74,7 @@ For restricted DB-users, which are used by the backend, access to rows is filter
FOR SELECT FOR SELECT
TO restricted TO restricted
USING ( USING (
isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserUuid()) isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid())
); );
SET SESSION AUTHORIZATION restricted; SET SESSION AUTHORIZATION restricted;
@ -101,7 +101,7 @@ We are bound to PostgreSQL, including integration tests and testing the RBAC sys
CREATE OR REPLACE RULE "_RETURN" AS CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view ON SELECT TO cust_view
DO INSTEAD DO INSTEAD
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('customer', id, 'view'), currentUserUuid()); SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid());
SET SESSION AUTHORIZATION restricted; SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com'; SET hsadminng.currentUser TO 'alex@example.com';

View File

@ -19,11 +19,11 @@ select *
FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com')); FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com'));
select * select *
FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('customer', FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
(SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
'add-package')); 'add-package'));
select * select *
FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('package', FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
'delete')); 'delete'));
@ -34,12 +34,12 @@ $$
result bool; result bool;
BEGIN BEGIN
userId = findRbacUser('superuser-alex@hostsharing.net'); userId = findRbacUser('superuser-alex@hostsharing.net');
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId)); result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'add-package'), userId));
IF (result) THEN IF (result) THEN
RAISE EXCEPTION 'expected permission NOT to be granted, but it is'; RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
end if; end if;
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'view'), userId)); result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'view'), userId));
IF (NOT result) THEN IF (NOT result) THEN
RAISE EXCEPTION 'expected permission to be granted, but it is NOT'; RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
end if; end if;

View File

@ -20,7 +20,7 @@ CREATE POLICY customer_policy ON customer
TO restricted TO restricted
USING ( USING (
-- id=1000 -- id=1000
isPermissionGrantedToSubject(findPermissionId('test_customer', id, 'view'), currentUserUuid()) isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid())
); );
SET SESSION AUTHORIZATION restricted; SET SESSION AUTHORIZATION restricted;
@ -35,7 +35,7 @@ SELECT * FROM customer;
CREATE OR REPLACE RULE "_RETURN" AS CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view ON SELECT TO cust_view
DO INSTEAD DO INSTEAD
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('test_customer', id, 'view'), currentUserUuid()); SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid());
SELECT * from cust_view LIMIT 10; SELECT * from cust_view LIMIT 10;
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net')); select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net'));

View File

@ -12,6 +12,7 @@ import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
@ -28,7 +29,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid { public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class) private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withProp(HsOfficeCoopAssetsTransactionEntity::getMemberNumber) .withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
.withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate) .withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate)
.withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType) .withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType)
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue) .withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
@ -75,8 +76,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
private String comment; private String comment;
public Integer getMemberNumber() { public String getTaggedMemberNumber() {
return ofNullable(membership).map(HsOfficeMembershipEntity::getMemberNumber).orElse(null); return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-?????");
} }
@Override @Override
@ -86,6 +87,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
@Override @Override
public String toShortString() { public String toShortString() {
return "%s%+1.2f".formatted(getMemberNumber(), assetValue); return "%s:%+1.2f".formatted(getTaggedMemberNumber(), Optional.ofNullable(assetValue).orElse(BigDecimal.ZERO));
} }
} }

View File

@ -1,6 +1,10 @@
package net.hostsharing.hsadminng.hs.office.partner; package net.hostsharing.hsadminng.hs.office.partner;
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.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
@ -11,7 +15,14 @@ import net.hostsharing.hsadminng.stringify.Stringifyable;
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.CascadeType;
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 java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
@ -48,7 +59,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null") @Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber; private Integer partnerNumber;
@ManyToOne @ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "partnerroleuuid", nullable = false) @JoinColumn(name = "partnerroleuuid", nullable = false)
private HsOfficeRelationshipEntity partnerRole; private HsOfficeRelationshipEntity partnerRole;

View File

@ -11,11 +11,13 @@ import java.io.BufferedWriter;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Stream;
import static java.lang.String.join; import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*; import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*;
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
@Service @Service
public class RbacGrantsMermaidService { public class RbacGrantsMermaidService {
@ -102,47 +104,93 @@ public class RbacGrantsMermaidService {
} }
private String toMermaidFlowchart(final HashSet<RawRbacGrantEntity> graph) { private String toMermaidFlowchart(final HashSet<RawRbacGrantEntity> graph) {
return "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n" + final var entities = graph.stream()
"flowchart TB\n\n" + graph.stream().sorted() .flatMap(g -> Stream.of(
.map(g -> node(g.getAscendantIdName(), g.getAscendingUuid()) + new Node(g.getAscendantIdName(), g.getAscendingUuid()),
new Node(g.getDescendantIdName(), g.getDescendantUuid()))
)
.collect(groupingBy(RbacGrantsMermaidService::entityIdName));
return "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n" +
"flowchart TB\n\n"
+ entities.entrySet().stream()
.map(entity -> "subgraph " + quoted(entity.getKey()) + subgraphDisplay(entity.getKey()) + "\n\n "
+ entity.getValue().stream()
.map(n -> node(n.idName(), n.uuid()).replace("\n", "\n "))
.sorted()
.distinct()
.collect(joining("\n\n ")))
.collect(joining("\n\nend\n\n"))
+ "\n\nend\n\n"
+ graph.stream()
.map(g -> quoted(g.getAscendantIdName()) +
(g.isAssumed() ? " --> " : " -.-> ") + (g.isAssumed() ? " --> " : " -.-> ") +
node(g.getDescendantIdName(), g.getDescendantUuid())) quoted(g.getDescendantIdName()))
.collect(Collectors.joining("\n")); .sorted()
.collect(joining("\n"));
}
private String subgraphDisplay(final String entityId) {
// this does not work according to Mermaid bug https://github.com/mermaid-js/mermaid/issues/3806
// if (entityId.contains("#")) {
// final var parts = entityId.split("#");
// final var table = parts[0];
// final var entity = parts[1];
// if (table.equals("entity")) {
// return "[" + entity "]";
// }
// return "[" + table + "\n" + entity + "]";
// }
return "[" + entityId + "]";
}
private static String entityIdName(final Node node) {
final var refType = refType(node.idName());
if (refType.equals("user")) {
return "users";
}
if (refType.equals("perm")) {
return node.idName().split(" ", 4)[3];
}
if (refType.equals("role")) {
final var withoutRolePrefix = node.idName().substring("role:".length());
return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf('.'));
}
throw new IllegalArgumentException("unknown refType '" + refType + "' in '" + node.idName() + "'");
} }
private String node(final String idName, final UUID uuid) { private String node(final String idName, final UUID uuid) {
return quoted(idName) + display(idName, uuid); return quoted(idName) + nodeContent(idName, uuid);
} }
private String display(final String idName, final UUID uuid) { private String nodeContent(final String idName, final UUID uuid) {
// role hs_office_relationship#FirstGmbH-with-REPRESENTATIVE-FirbySusan.admin final var refType = refType(idName);
// TODO: refactor by separate algorithms for perm/role/user
final var refType = idName.split(" ", 2)[0];
if (refType.equals("user")) { if (refType.equals("user")) {
final var displayName = idName.substring(refType.length()+1); final var displayName = idName.substring(refType.length()+1);
return "(" + displayName + "\n" + uuid + ")"; return "(" + displayName + "\nref:" + uuid + ")";
} }
if (refType.equals("role")) { if (refType.equals("role")) {
final var roleType = idName.substring(idName.lastIndexOf('.') + 1); final var roleType = idName.substring(idName.lastIndexOf('.') + 1);
final var objectName = idName.substring(refType.length()+1, idName.length() - roleType.length() - 1); return "[" + roleType + "\nref:" + uuid + "]";
final var tableName = objectName.contains("#") ? objectName.split("#")[0] : "";
final var instanceName = objectName.contains("#") ? objectName.split("#", 2)[1] : objectName;
final var displayName = "\n" + (tableName.equals("global") ? "" : tableName) + "\n" + instanceName + "\n" + uuid + "\n" + roleType;
return "[" + displayName + "]";
} }
if (refType.equals("perm")) { if (refType.equals("perm")) {
final var roleType = idName.split(" ")[1]; final var roleType = idName.split(" ")[1];
final var objectName = idName.split(" ")[3]; return "{{" + roleType + "\nref:" + uuid + "}}";
final var tableName = objectName.contains("#") ? objectName.split("#")[0] : "";
final var instanceName = objectName.contains("#") ? objectName.split("#", 2)[1] : objectName;
final var displayName = "\n" + tableName + "\n" + instanceName + "\n" + uuid + (roleType == null ? "" : ("\n" + roleType));return "{{" + displayName + "}}";
} }
return ""; return "";
} }
private static String refType(final String idName) {
return idName.split(" ", 2)[0];
}
@NotNull @NotNull
private static String quoted(final String idName) { private static String quoted(final String idName) {
return idName.replace(" ", ":").replaceAll("@.*", ""); return idName.replace(" ", ":").replaceAll("@.*", "");
} }
} }
record Node(String idName, UUID uuid) {
}

View File

@ -439,8 +439,23 @@ create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp)
select uuid select uuid
from RbacPermission p from RbacPermission p
where p.objectUuid = forObjectUuid where p.objectUuid = forObjectUuid
and p.op in ('*', forOp) and p.op = forOp
$$; $$;
create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp)
returns uuid
returns null on null input
stable -- leakproof
language plpgsql as $$
declare
permissionId uuid;
begin
permissionId := findPermissionId(forObjectUuid, forOp);
if permissionId is null and forOp <> '*' then
permissionId := findPermissionId(forObjectUuid, '*');
end if;
return permissionId;
end $$;
--// --//
@ -665,28 +680,47 @@ begin
if (isGranted(superRoleId, subRoleId)) then if (isGranted(superRoleId, subRoleId)) then
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId; delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
else else
raise exception 'cannot revoke role % (%) from % (% because it is not granted', raise exception 'cannot revoke role % (%) from % (%) because it is not granted',
subRole, subRoleId, superRole, superRoleId; subRole, subRoleId, superRole, superRoleId;
end if; end if;
end; $$; end; $$;
create or replace procedure revokePermissionFromRole(permission RbacRoleDescriptor, superRole RbacRoleDescriptor) create or replace procedure revokePermissionFromRole(permissionId UUID, superRole RbacRoleDescriptor)
language plpgsql as $$ language plpgsql as $$
declare declare
superRoleId uuid; superRoleId uuid;
subRoleId uuid; permissionOp text;
objectTable text;
objectUuid uuid;
begin begin
superRoleId := findRoleId(superRole); superRoleId := findRoleId(superRole);
subRoleId := findRoleId(subRole);
perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole');
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
if (isGranted(superRoleId, subRoleId)) then if (isGranted(superRoleId, permissionId)) then
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId; delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = permissionId;
else else
raise exception 'cannot revoke role % (%) from % (% because it is not granted',
subRole, subRoleId, superRole, superRoleId; -- FOR grantUuid IN SELECT grantUuid FROM rbacGrants where ascendantUuid=superRoleId LOOP
-- select p.op, o.objectTable, o.uuid
-- from rbacGrants g
-- join rbacPermission p on p.uuid=g.descendantUuid
-- join rbacobject o on o.uuid=p.objectUuid
-- where g.uuid=
-- into permissionOp, objectTable, objectUuid;
-- RAISE NOTICE 'col1: %, col2: %', quote_ident(items.col1), quote_ident(items.col2);
-- END LOOP;
select p.op, o.objectTable, o.uuid
from rbacGrants g
join rbacPermission p on p.uuid=g.descendantUuid
join rbacobject o on o.uuid=p.objectUuid
where g.uuid=permissionId
into permissionOp, objectTable, objectUuid;
raise exception 'cannot revoke permission % on %#% (%) from % (%) because it is not granted',
permissionOp, objectTable, objectUuid, permissionId, superRole, superRoleId;
end if; end if;
end; $$; end; $$;

View File

@ -56,14 +56,17 @@ begin
roleTypeToAssume = split_part(roleNameParts, '#', 3); roleTypeToAssume = split_part(roleNameParts, '#', 3);
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume); objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
if objectUuidToAssume is null then
raise exception '[401] object % cannot be found in table %', objectNameToAssume, objectTableToAssume;
end if;
select uuid as roleuuidToAssume select uuid
from RbacRole r from RbacRole r
where r.objectUuid = objectUuidToAssume where r.objectUuid = objectUuidToAssume
and r.roleType = roleTypeToAssume and r.roleType = roleTypeToAssume
into roleUuidToAssume; into roleUuidToAssume;
if roleUuidToAssume is null then if roleUuidToAssume is null then
raise exception '[403] role % not accessible for user %', roleName, currentSubjects(); raise exception '[403] role % does not exist or is not accessible for user %', roleName, currentUser();
end if; end if;
if not isGranted(currentUserUuid, roleUuidToAssume) then if not isGranted(currentUserUuid, roleUuidToAssume) then
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName; raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;

View File

@ -20,98 +20,130 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
oldPartnerRole hs_office_relationship; partnerUuid uuid default new.uuid;
newPartnerRole hs_office_relationship; partnerDetailsUuid uuid default new.detailsUuid;
oldPartnerRel hs_office_relationship;
newPartnerRel hs_office_relationship;
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRole; select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRel;
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
-- Permissions and Grants for Partner -- Permissions and Grants for Partner
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
createPermissions(NEW.uuid, array ['*']) createPermissions(partnerUuid, array ['*'])
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
createPermissions(NEW.uuid, array ['edit']) createPermissions(partnerUuid, array ['edit'])
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
createPermissions(NEW.uuid, array ['view']) createPermissions(partnerUuid, array ['view'])
); );
-- Permissions and Grants for PartnerDetails -- Permissions and Grants for PartnerDetails
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
createPermissions(NEW.detailsUuid, array ['*']) createPermissions(partnerDetailsUuid, array ['*'])
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
createPermissions(NEW.detailsUuid, array ['edit']) createPermissions(partnerDetailsUuid, array ['edit'])
); );
call grantPermissionsToRole( call grantPermissionsToRole(
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT. -- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT.
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT! -- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
-- Otherwise package-admins etc. would be able to read the data. -- Otherwise package-admins etc. would be able to read the data.
getRoleId(hsOfficeRelationshipAgent(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'),
createPermissions(NEW.detailsUuid, array ['view']) createPermissions(partnerDetailsUuid, array ['view'])
); );
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then
select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole; select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRel;
-- Revoke all Permissions from old partner relationship -- Revokes from Partner
-- TODO: introduce call revokeAllPermissionsOnDescendantFromAllRolesOfAscendant(OLD, oldPartnerRole);
delete from rbacGrants where descendantUuid==OLD.uuid and ascendantUuid==OLD.partnerRoleUuid; call revokePermissionFromRole(
findPermissionId(partnerUuid, 'view'),
hsOfficeRelationshipTenant(oldPartnerRel)
);
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, 'edit'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerUuid, '*'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- Grants for Partner -- Grants for Partner
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
array[findPermissionId(NEW.uuid, '*')] array[findPermissionId(partnerUuid, '*')]
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
array[findPermissionId(NEW.uuid, 'edit')] array[findPermissionId(partnerUuid, 'edit')]
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
array[findPermissionId(NEW.uuid, 'view')] array[findPermissionId(partnerUuid, 'view')]
); );
-- Revokes from PartnerDetails
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'view'),
-- hsOfficeRelationshipAgent(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, 'edit'),
-- hsOfficeRelationshipAdmin(oldPartnerRel)
-- );
--
-- call revokePermissionFromRole(
-- findPermissionId(partnerDetailsUuid, '*'),
-- hsOfficeRelationshipOwner(oldPartnerRel)
-- );
-- Grants for PartnerDetails -- Grants for PartnerDetails
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
array[findPermissionId(NEW.detailsUuid, '*')] array[findPermissionId(partnerDetailsUuid, '*')]
); );
call grantPermissionsToRole( call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
array[findPermissionId(NEW.detailsUuid, array ['edit'])] array[findPermissionId(partnerDetailsUuid, 'edit')]
); );
call grantPermissionsToRole( call grantPermissionsToRole(
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT. -- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT! -- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
-- Otherwise package-admins etc. would be able to read the data. -- Otherwise package-admins etc. would be able to read the data.
getRoleId(hsOfficeRelationshipAgent(newPartnerRole), 'fail'), getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'),
array[findPermissionId(NEW.detailsUuid, 'view')] array[findPermissionId(partnerDetailsUuid, 'view')]
); );
end if; end if;
@ -120,7 +152,7 @@ begin
raise exception 'invalid usage of TRIGGER'; raise exception 'invalid usage of TRIGGER';
end if; end if;
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(partnerUuid);
return NEW; return NEW;
end; $$; end; $$;

View File

@ -263,12 +263,12 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
givenPartner, givenPartner,
"hs_office_person#ErbenBesslerMelBessler.admin"); "hs_office_person#ErbenBesslerMelBessler.admin");
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
RbacGrantsMermaidService.writeToFile("initial partner: Erben Bessler + fifth contact",
// when
RbacGrantsMermaidService.writeToFile("givenPartner with partner Erben Bessler + fifth contact",
mermaidService.allGrantsFrom(givenPartner.getUuid(), "view", EnumSet.of(Include.USERS)), mermaidService.allGrantsFrom(givenPartner.getUuid(), "view", EnumSet.of(Include.USERS)),
"doc/all-grants-before-update.md"); "doc/all-grants-before-update.md");
// when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
givenPartner.setPartnerRole(givenSomeTemporaryHostsharingPartnerRole("Third OHG", "sixth contact")); givenPartner.setPartnerRole(givenSomeTemporaryHostsharingPartnerRole("Third OHG", "sixth contact"));
@ -277,10 +277,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then // then
result.assertSuccessful(); result.assertSuccessful();
RbacGrantsMermaidService.writeToFile("givenPartner with partner to Third OHG + sixth contact", RbacGrantsMermaidService.writeToFile("updated partner: Third OHG + sixth contact",
mermaidService.allGrantsFrom(result.returnedValue().getUuid(), "view", EnumSet.of(Include.USERS)), mermaidService.allGrantsFrom(result.returnedValue().getUuid(), "view", EnumSet.of(Include.USERS)),
"doc/all-grants-after-update.md"); "doc/all-grants-after-update.md");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"global#global.admin"); "global#global.admin");
@ -335,7 +334,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final String assumedRoles) { final String assumedRoles) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles); context("superuser-alex@hostsharing.net", assumedRoles);
RbacGrantsMermaidService.writeToFile("givenPartner within assertThatPartnerIsNotVisibleForUserWithRole", RbacGrantsMermaidService.writeToFile("partner visible in assertThatPartnerIsNotVisibleForUserWithRole",
mermaidService.allGrantsFrom(entity.getUuid(), "view", EnumSet.of(Include.USERS)), mermaidService.allGrantsFrom(entity.getUuid(), "view", EnumSet.of(Include.USERS)),
"doc/all-grants-within-assertThatPartnerIsNotVisibleForUserWithRole.md"); "doc/all-grants-within-assertThatPartnerIsNotVisibleForUserWithRole.md");