Merge remote-tracking branch 'origin/master' into spring-boot-3-2-upgrade

# Conflicts:
#	README.md
#	doc/rbac.md
#	src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
#	src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
#	src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
#	src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
#	src/main/resources/db/changelog/1-rbac/1050-rbac-base.sql
#	src/main/resources/db/changelog/1-rbac/1058-rbac-generators.sql
#	src/main/resources/db/changelog/1-rbac/1080-rbac-global.sql
#	src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.md
#	src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql
#	src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.md
#	src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql
#	src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.md
#	src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql
#	src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
This commit is contained in:
Michael Hoennig 2024-04-02 12:46:41 +02:00
commit 3107bd33cc
20 changed files with 181 additions and 88 deletions

View File

@ -82,7 +82,7 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy: # the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
curl \ curl \
-H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:admin' \ -H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
http://localhost:8080/api/test/packages http://localhost:8080/api/test/packages
# add a new customer # add a new customer

View File

@ -206,7 +206,7 @@ and the *role-stereotype* describes a role relative to a referenced business-obj
#### owner #### owner
The owner-role is granted to the subject which created the business object. The owner-role is granted to the subject which created the business object.
E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...:admin'. E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...:ADMIN'.
Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it. Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it.
@ -470,14 +470,14 @@ together {
permCustomerXyzSELECT--> boCustXyz permCustomerXyzSELECT--> boCustXyz
} }
entity "Role customer#xyz:tenant" as roleCustXyzTenant entity "Role customer#xyz:TENANT" as roleCustXyzTenant
roleCustXyzTenant --> permCustomerXyzSELECT roleCustXyzTenant --> permCustomerXyzSELECT
entity "Role customer#xyz:admin" as roleCustXyzAdmin entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
roleCustXyzAdmin --> roleCustXyzTenant roleCustXyzAdmin --> roleCustXyzTenant
roleCustXyzAdmin --> permCustomerXyzINSERT:package roleCustXyzAdmin --> permCustomerXyzINSERT:package
entity "Role customer#xyz:owner" as roleCustXyzOwner entity "Role customer#xyz:OWNER" as roleCustXyzOwner
roleCustXyzOwner ..> roleCustXyzAdmin roleCustXyzOwner ..> roleCustXyzAdmin
roleCustXyzOwner --> permCustomerXyzDELETE roleCustXyzOwner --> permCustomerXyzDELETE
@ -493,7 +493,7 @@ actorHostmaster --> roleAdmins
``` ```
As you can see, there something special: As you can see, there something special:
From the 'Role customer#xyz:owner' to the 'Role customer#xyz:admin' there is a dashed line, whereas all other lines are solid lines. From the 'Role customer#xyz:OWNER' to the 'Role customer#xyz:admin' there is a dashed line, whereas all other lines are solid lines.
Solid lines means, that one role is granted to another and automatically assumed in all queries to the restricted views. Solid lines means, that one role is granted to another and automatically assumed in all queries to the restricted views.
The dashed line means that one role is granted to another but not automatically assumed in queries to the restricted views. The dashed line means that one role is granted to another but not automatically assumed in queries to the restricted views.
@ -541,15 +541,15 @@ together {
} }
package { package {
entity "Role customer#xyz:tenant" as roleCustXyzTenant entity "Role customer#xyz:TENANT" as roleCustXyzTenant
entity "Role customer#xyz:admin" as roleCustXyzAdmin entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
entity "Role customer#xyz:owner" as roleCustXyzOwner entity "Role customer#xyz:OWNER" as roleCustXyzOwner
} }
package { package {
entity "Role package#xyz00:owner" as rolePacXyz00Owner entity "Role package#xyz00:OWNER" as rolePacXyz00Owner
entity "Role package#xyz00:admin" as rolePacXyz00Admin entity "Role package#xyz00:ADMIN" as rolePacXyz00Admin
entity "Role package#xyz00:tenant" as rolePacXyz00Tenant entity "Role package#xyz00:TENANT" as rolePacXyz00Tenant
} }
rolePacXyz00Tenant --> permPacXyz00SELECT rolePacXyz00Tenant --> permPacXyz00SELECT

View File

@ -1,16 +1,29 @@
package net.hostsharing.hsadminng.hs.office.coopassets; package net.hostsharing.hsadminng.hs.office.coopassets;
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.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
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.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
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;
@ -20,8 +33,11 @@ import java.util.UUID;
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.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.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; 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.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.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -34,7 +50,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DisplayName("CoopAssetsTransaction") @DisplayName("CoopAssetsTransaction")
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacObject { public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class) private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber) .withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
@ -109,7 +125,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
.toRole("membership", ADMIN).grantPermission(INSERT) .toRole("membership", ADMIN).grantPermission(INSERT)
.toRole("membership", ADMIN).grantPermission(UPDATE) .toRole("membership", ADMIN).grantPermission(UPDATE)
.toRole("membership", ADMIN).grantPermission(SELECT); .toRole("membership", AGENT).grantPermission(SELECT);
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {

View File

@ -1,15 +1,28 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
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.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
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 jakarta.persistence.*; import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
@ -17,9 +30,11 @@ import java.util.UUID;
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.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.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
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.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.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -32,7 +47,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DisplayName("CoopShareTransaction") @DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject { public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class) private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged) .withProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged)
@ -105,7 +120,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
.toRole("membership", ADMIN).grantPermission(INSERT) .toRole("membership", ADMIN).grantPermission(INSERT)
.toRole("membership", ADMIN).grantPermission(UPDATE) .toRole("membership", ADMIN).grantPermission(UPDATE)
.toRole("membership", ADMIN).grantPermission(SELECT); .toRole("membership", AGENT).grantPermission(SELECT);
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {

View File

@ -28,7 +28,8 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; 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.REFERRER; 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;
@ -148,14 +149,14 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);
with.incomingSuperRole("partnerRel", ADMIN);
with.permission(DELETE);
}) })
.createSubRole(ADMIN, (with) -> { .createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("partnerRel", AGENT); with.incomingSuperRole("partnerRel", ADMIN);
with.permission(DELETE);
with.permission(UPDATE); with.permission(UPDATE);
}) })
.createSubRole(REFERRER, (with) -> { .createSubRole(AGENT, (with) -> {
with.incomingSuperRole("partnerRel", AGENT);
with.outgoingSubRole("partnerRel", TENANT); with.outgoingSubRole("partnerRel", TENANT);
with.permission(SELECT); with.permission(SELECT);
}); });

View File

@ -248,7 +248,7 @@ declare
objectUuidOfRole uuid; objectUuidOfRole uuid;
roleUuid uuid; roleUuid uuid;
begin begin
-- TODO.refa: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences -- TODO.refact: 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,13 +356,16 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
/* /*
*/ */
create domain RbacOp as varchar(6) create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone
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
@ -414,6 +417,37 @@ 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
@ -615,6 +649,25 @@ 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
@ -638,7 +691,7 @@ declare
superRoleId uuid; superRoleId uuid;
subRoleId uuid; subRoleId uuid;
begin begin
-- TODO.refa: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references -- TODO: 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;

View File

@ -73,6 +73,15 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'TENANT', assumed); return roleDescriptor('%2$s', entity.uuid, 'TENANT', assumed);
end; $f$; end; $f$;
-- TODO: remove guest role
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
return roleDescriptor('%2$s', entity.uuid, 'GUEST', assumed);
end; $f$;
create or replace function %1$sReferrer(entity %2$s) create or replace function %1$sReferrer(entity %2$s)
returns RbacRoleDescriptor returns RbacRoleDescriptor
language plpgsql language plpgsql

View File

@ -139,7 +139,7 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'GU
$$; $$;
begin transaction; begin transaction;
call defineContext('creating role:global#loba:guest', null, null, null); call defineContext('creating role:global#global:guest', null, null, null);
select createRole(globalGuest()); select createRole(globalGuest());
commit; commit;
--// --//

View File

@ -42,7 +42,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]] role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]] role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]] role:membership:AGENT[[membership:AGENT]]
end end
subgraph membership:permissions[ ] subgraph membership:permissions[ ]
@ -105,16 +105,16 @@ 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:ADMIN ==> role:membership:OWNER
role:membership:OWNER ==> role:membership:ADMIN role:membership:OWNER ==> role:membership:ADMIN
role:partnerRel:AGENT ==> role:membership:ADMIN role:partnerRel:ADMIN ==> role:membership:ADMIN
role:membership:ADMIN ==> role:membership:REFERRER role:membership:ADMIN ==> role:membership:AGENT
role:membership:REFERRER ==> role:partnerRel:TENANT role:partnerRel:AGENT ==> role:membership:AGENT
role:membership:AGENT ==> role:partnerRel:TENANT
%% granting permissions to roles %% granting permissions to roles
role:global:ADMIN ==> perm:membership:INSERT role:global:ADMIN ==> perm:membership:INSERT
role:membership:OWNER ==> perm:membership:DELETE role:membership:ADMIN ==> perm:membership:DELETE
role:membership:ADMIN ==> perm:membership:UPDATE role:membership:ADMIN ==> perm:membership:UPDATE
role:membership:REFERRER ==> perm:membership:SELECT role:membership:AGENT ==> perm:membership:SELECT
``` ```

View File

@ -45,23 +45,23 @@ begin
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeMembershipOWNER(NEW), hsOfficeMembershipOWNER(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[hsOfficeRelationADMIN(newPartnerRel)],
userUuids => array[currentUserUuid()] userUuids => array[currentUserUuid()]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeMembershipADMIN(NEW), hsOfficeMembershipADMIN(NEW),
permissions => array['UPDATE'], permissions => array['DELETE', 'UPDATE'],
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficeMembershipOWNER(NEW), hsOfficeMembershipOWNER(NEW),
hsOfficeRelationAGENT(newPartnerRel)] hsOfficeRelationADMIN(newPartnerRel)]
); );
perform createRoleWithGrants( perform createRoleWithGrants(
hsOfficeMembershipREFERRER(NEW), hsOfficeMembershipAGENT(NEW),
permissions => array['SELECT'], permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeMembershipADMIN(NEW)], incomingSuperRoles => array[
hsOfficeMembershipADMIN(NEW),
hsOfficeRelationAGENT(newPartnerRel)],
outgoingSubRoles => array[hsOfficeRelationTENANT(newPartnerRel)] outgoingSubRoles => array[hsOfficeRelationTENANT(newPartnerRel)]
); );

View File

@ -54,7 +54,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]] role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]] role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]] role:membership:AGENT[[membership:AGENT]]
end end
end end
@ -106,15 +106,15 @@ 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:ADMIN -.-> role:membership:OWNER
role:membership:OWNER -.-> role:membership:ADMIN role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:AGENT -.-> role:membership:ADMIN role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:REFERRER role:membership:ADMIN -.-> role:membership:AGENT
role:membership:REFERRER -.-> role:membership.partnerRel:TENANT role:membership.partnerRel:AGENT -.-> role:membership:AGENT
role:membership:AGENT -.-> role:membership.partnerRel:TENANT
%% granting permissions to roles %% granting permissions to roles
role:membership:ADMIN ==> perm:coopSharesTransaction:INSERT role:membership:ADMIN ==> perm:coopSharesTransaction:INSERT
role:membership:ADMIN ==> perm:coopSharesTransaction:UPDATE role:membership:ADMIN ==> perm:coopSharesTransaction:UPDATE
role:membership:ADMIN ==> perm:coopSharesTransaction:SELECT role:membership:AGENT ==> perm:coopSharesTransaction:SELECT
``` ```

View File

@ -38,7 +38,7 @@ begin
SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership; SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership;
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipADMIN(newMembership)); call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership)); call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);

View File

@ -54,7 +54,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]] role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]] role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]] role:membership:AGENT[[membership:AGENT]]
end end
end end
@ -106,15 +106,15 @@ 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:ADMIN -.-> role:membership:OWNER
role:membership:OWNER -.-> role:membership:ADMIN role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:AGENT -.-> role:membership:ADMIN role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:REFERRER role:membership:ADMIN -.-> role:membership:AGENT
role:membership:REFERRER -.-> role:membership.partnerRel:TENANT role:membership.partnerRel:AGENT -.-> role:membership:AGENT
role:membership:AGENT -.-> role:membership.partnerRel:TENANT
%% granting permissions to roles %% granting permissions to roles
role:membership:ADMIN ==> perm:coopAssetsTransaction:INSERT role:membership:ADMIN ==> perm:coopAssetsTransaction:INSERT
role:membership:ADMIN ==> perm:coopAssetsTransaction:UPDATE role:membership:ADMIN ==> perm:coopAssetsTransaction:UPDATE
role:membership:ADMIN ==> perm:coopAssetsTransaction:SELECT role:membership:AGENT ==> perm:coopAssetsTransaction:SELECT
``` ```

View File

@ -38,7 +38,7 @@ begin
SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership; SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership;
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipADMIN(newMembership)); call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership)); call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid); call leaveTriggerForObjectUuid(NEW.uuid);

View File

@ -112,7 +112,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm:coopassetstransaction#temprefB:SELECT to role:membership#M-1000101:ADMIN by system and assume }", "{ grant perm:coopassetstransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
"{ grant perm:coopassetstransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }", "{ grant perm:coopassetstransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null)); null));
} }

View File

@ -111,7 +111,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm:coopsharestransaction#temprefB:SELECT to role:membership#M-1000101:ADMIN by system and assume }", "{ grant perm:coopsharestransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
"{ grant perm:coopsharestransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }", "{ grant perm:coopsharestransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null)); null));
} }

View File

@ -335,18 +335,18 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
} }
@Test @Test
void partnerRelAgent_canPatchValidityOfRelatedMembership() { void partnerRelAdmin_canPatchValidityOfRelatedMembership() {
// given // given
final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT"; final var givenPartnerAdmin = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN";
context.define("superuser-alex@hostsharing.net", givenPartnerAgent); context.define("superuser-alex@hostsharing.net", givenPartnerAdmin);
final var givenMembership = givenSomeTemporaryMembershipBessler("First"); final var givenMembership = givenSomeTemporaryMembershipBessler("First");
// when // when
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.header("assumed-roles", givenPartnerAgent) .header("assumed-roles", givenPartnerAdmin)
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {

View File

@ -110,9 +110,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
final var all = rawRoleRepo.findAll(); final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from( assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_membership#M-1000117:ADMIN",
"hs_office_membership#M-1000117:OWNER", "hs_office_membership#M-1000117:OWNER",
"hs_office_membership#M-1000117:REFERRER")); "hs_office_membership#M-1000117:ADMIN",
"hs_office_membership#M-1000117:AGENT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())) assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
@ -122,21 +122,20 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
"{ grant perm:membership#M-1000117:INSERT>coopsharestransaction to role:membership#M-1000117:ADMIN by system and assume }", "{ grant perm:membership#M-1000117:INSERT>coopsharestransaction to role:membership#M-1000117:ADMIN by system and assume }",
// owner // owner
"{ grant perm:membership#M-1000117:DELETE to role:membership#M-1000117:OWNER by system and assume }", "{ grant perm:membership#M-1000117:DELETE to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:OWNER to user:superuser-alex@hostsharing.net by membership#M-1000117:OWNER and assume }",
// admin // admin
"{ grant perm:membership#M-1000117:UPDATE to role:membership#M-1000117:ADMIN by system and assume }", "{ grant perm:membership#M-1000117:UPDATE to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:ADMIN to role:membership#M-1000117:OWNER by system and assume }", "{ grant role:membership#M-1000117:ADMIN to role:membership#M-1000117:OWNER by system and assume }",
"{ grant role:membership#M-1000117:OWNER to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }", "{ grant role:membership#M-1000117:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:OWNER to user:superuser-alex@hostsharing.net by membership#M-1000117:OWNER and assume }",
// agent // agent
"{ grant role:membership#M-1000117:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH: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 }",
// referrer "{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
"{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:REFERRER by system and assume }", "{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:AGENT by system and assume }",
"{ grant role:membership#M-1000117:REFERRER to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:REFERRER by system and assume }",
null)); null));
} }
@ -224,20 +223,20 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
} }
@Test @Test
public void membershipReferrer_canViewButNotUpdateRelatedMembership() { public void membershipAgent_canViewButNotUpdateRelatedMembership() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenMembership = givenSomeTemporaryMembership("First", "13"); final var givenMembership = givenSomeTemporaryMembership("First", "13");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership); assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
assertThatMembershipIsVisibleForRole( assertThatMembershipIsVisibleForRole(
givenMembership, givenMembership,
"hs_office_membership#M-1000113:REFERRER"); "hs_office_membership#M-1000113:AGENT");
final var newValidityEnd = LocalDate.now(); final var newValidityEnd = LocalDate.now();
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
// TODO: we should test with debitor- and partner-admin as well // TODO: we should test with debitor- and partner-admin as well
context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113:REFERRER"); context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113:AGENT");
givenMembership.setValidity( givenMembership.setValidity(
Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd)); Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership); return membershipRepo.save(givenMembership);

View File

@ -507,7 +507,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
}).assertNotNull().returnedValue(); }).assertNotNull().returnedValue();
} }
RbacRoleRvEntity getRbacRoleByName(final String roleName) { RbacRoleEntity getRbacRoleByName(final String roleName) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
return rbacRoleRepository.findByRoleName(roleName); return rbacRoleRepository.findByRoleName(roleName);

View File

@ -4,11 +4,11 @@ import static java.util.UUID.randomUUID;
public class TestRbacRole { public class TestRbacRole {
public static final RbacRoleRvEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.ADMIN); public static final RbacRoleEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.ADMIN);
static final RbacRoleRvEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.OWNER); static final RbacRoleEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.OWNER);
static final RbacRoleRvEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.ADMIN); static final RbacRoleEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.ADMIN);
static public RbacRoleRvEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) { static public RbacRoleEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) {
return new RbacRoleRvEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+':'+roleType); return new RbacRoleEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+':'+roleType);
} }
} }