Compare commits
4 Commits
fb00b36b2f
...
717bdca948
Author | SHA1 | Date | |
---|---|---|---|
|
717bdca948 | ||
|
84ce1e34bf | ||
|
36654a69d8 | ||
|
85ad05a77e |
@ -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';
|
||||||
|
@ -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;
|
||||||
|
@ -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'));
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -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; $$;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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; $$;
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user