Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Hoennig
92086d8634 scribbled the DSL for switchOnColumn+inCaseOf 2024-04-03 10:53:28 +02:00
Michael Hoennig
b5ef54e83c baseline with green tests 2024-04-03 09:58:12 +02:00
Michael Hoennig
630a9fe3d0 Merge remote-tracking branch 'origin/master' into rbac-generator-with-conditional-grants
# Conflicts:
#	README.md
#	build.gradle
#	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/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/503-relation/5033-hs-office-relation-rbac.md
#	src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.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/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
2024-04-03 08:17:09 +02:00
73c378b456 spring-boot-3-2-upgrade (#32)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #32
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 13:24:25 +02:00
ad04faa21d cleanup-todos (#31)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #31
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 13:14:46 +02:00
277369a960 debitornumbersuffix-as-string (#30)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #30
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 13:09:12 +02:00
87af20a3a1 structured-liquibase-files (#29)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #29
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 12:29:31 +02:00
7f418c12a1 uniform idnames (#28)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #28
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 12:01:37 +02:00
f8fb273918 generated RBAC for coopshares and -assets (#27)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #27
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 11:04:56 +02:00
36 changed files with 318 additions and 297 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

@ -62,7 +62,6 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi:2.4.0' implementation 'org.springdoc:springdoc-openapi:2.4.0'
implementation 'org.postgresql:postgresql:42.7.3' implementation 'org.postgresql:postgresql:42.7.3'
implementation 'org.liquibase:liquibase-core:4.27.0' implementation 'org.liquibase:liquibase-core:4.27.0'
//implementation 'com.vladmihalcea:hibernate-types-60:2.21.1'
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.3' implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.3'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
implementation 'org.openapitools:jackson-databind-nullable:0.2.6' implementation 'org.openapitools:jackson-databind-nullable:0.2.6'

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,7 +1,11 @@
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.rbac.rbacobject.RbacObject;
@ -10,7 +14,15 @@ 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.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
@ -20,8 +32,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;
@ -109,7 +124,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,27 @@
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.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
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.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
@ -17,9 +29,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;
@ -105,7 +119,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

@ -26,6 +26,7 @@ import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH; import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
@ -157,6 +158,7 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
usingCase("DEBITOR"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid")) dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(DELETE).grantedTo("debitorRel", OWNER)

View File

@ -28,7 +28,6 @@ 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.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 +147,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

@ -29,6 +29,7 @@ import java.util.UUID;
import static jakarta.persistence.CascadeType.*; import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -98,6 +99,7 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
usingDefaultCase(),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid")) dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN) .createPermission(DELETE).grantedTo("partnerRel", ADMIN)

View File

@ -11,17 +11,17 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.persistence.Column;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@ -101,28 +101,53 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NOT_NULL) NOT_NULL)
.createRole(OWNER, (with) -> { .switchOnColumn("type",
inCaseOf("REPRESENTATIVE", then -> {
then.createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN); with.incomingSuperRole(GLOBAL, ADMIN);
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type = 'REPRESENTATIVE'"); with.incomingSuperRole("holderPerson", ADMIN);
with.permission(DELETE); with.permission(DELETE);
}) })
.createSubRole(ADMIN, (with) -> { .createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("anchorPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'"); with.incomingSuperRole("anchorPerson", ADMIN);
with.outgoingSubRole("anchorPerson", OWNER).where("${REF}.type = 'REPRESENTATIVE'"); with.outgoingSubRole("anchorPerson", OWNER);
with.permission(UPDATE); with.permission(UPDATE);
}) })
.createSubRole(AGENT, (with) -> { .createSubRole(AGENT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'");
}) })
.createSubRole(TENANT, (with) -> { .createSubRole(TENANT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("contact", ADMIN); with.incomingSuperRole("contact", ADMIN);
with.outgoingSubRole("anchorPerson", REFERRER); with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER); with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("contact", REFERRER); with.outgoingSubRole("contact", REFERRER);
with.permission(SELECT); with.permission(SELECT);
});
}),
//FIXME: .inCaseOf("DEBITOR")
inOtherCases(then -> {
then.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
with.permission(DELETE);
}) })
.createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("anchorPerson", ADMIN);
with.permission(UPDATE);
})
.createSubRole(AGENT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
})
.createSubRole(TENANT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("contact", ADMIN);
with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("contact", REFERRER);
with.permission(SELECT);
});
}))
.toRole("anchorPerson", ADMIN).grantPermission(INSERT); .toRole("anchorPerson", ADMIN).grantPermission(INSERT);
} }

View File

@ -1,58 +0,0 @@
package net.hostsharing.hsadminng.mapper;
import lombok.experimental.UtilityClass;
import org.postgresql.util.PGtokenizer;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;
@UtilityClass
public class PostgresArray {
/**
* Converts a byte[], as returned for a Postgres-array by native queries, to a Java array.
*
* <p>This example code worked with Hibernate 5 (Spring Boot 3.0.x):
* <pre><code>
* return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
* </code></pre>
* </p>
*
* <p>With Hibernate 6 (Spring Boot 3.1.x), this utility method can be used like such:
* <pre><code>
* final byte[] result = (byte[]) em.createNativeQuery("select * from currentSubjectsUuids() as uuids", UUID[].class)
* .getSingleResult();
* return fromPostgresArray(result, UUID.class, UUID::fromString);
* </code></pre>
* </p>
*
* @param pgArray the byte[] returned by a native query containing as rendered for a Postgres array
* @param elementClass the class of a single element of the Java array to be returned
* @param itemParser converts a string element to the specified elementClass
* @return a Java array containing the data from pgArray
* @param <T> type of a single element of the Java array
*/
public static <T> T[] fromPostgresArray(final byte[] pgArray, final Class<T> elementClass, final Function<String, T> itemParser) {
final var pgArrayLiteral = new String(pgArray, StandardCharsets.UTF_8);
if (pgArrayLiteral.length() == 2) {
return newGenericArray(elementClass, 0);
}
final PGtokenizer tokenizer = new PGtokenizer(pgArrayLiteral.substring(1, pgArrayLiteral.length()-1), ',');
tokenizer.remove("\"", "\"");
final T[] array = newGenericArray(elementClass, tokenizer.getSize()); // Create a new array of the specified type and length
for ( int n = 0; n < tokenizer.getSize(); ++n ) {
final String token = tokenizer.getToken(n);
if ( !"NULL".equals(token) ) {
array[n] = itemParser.apply(token.trim().replace("\\\"", "\""));
}
}
return array;
}
@SuppressWarnings("unchecked")
private static <T> T[] newGenericArray(final Class<T> elementClass, final int length) {
return (T[]) Array.newInstance(elementClass, length);
}
}

View File

@ -14,7 +14,6 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity; import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
import net.hostsharing.hsadminng.test.dom.TestDomainEntity; import net.hostsharing.hsadminng.test.dom.TestDomainEntity;
import net.hostsharing.hsadminng.test.pac.TestPackageEntity; import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
@ -35,10 +34,10 @@ import static java.util.Optional.ofNullable;
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.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@Getter @Getter
// TODO.refa: rename to RbacDSL
public class RbacView { public class RbacView {
public static final String GLOBAL = "global"; public static final String GLOBAL = "global";
@ -66,6 +65,15 @@ public class RbacView {
private SQL orderBySqlExpression; private SQL orderBySqlExpression;
private EntityAlias rootEntityAliasProxy; private EntityAlias rootEntityAliasProxy;
private RbacRoleDefinition previousRoleDef; private RbacRoleDefinition previousRoleDef;
private final Map<String, String> cases = new HashMap<>() {
@Override
public String put(final String key, final String value) {
if (containsKey(key)) {
throw new IllegalArgumentException("duplicate case: " + key);
}
return super.put(key, value);
}
};
/** Crates an RBAC definition template for the given entity class and defining the given alias. /** Crates an RBAC definition template for the given entity class and defining the given alias.
* *
@ -278,6 +286,7 @@ public class RbacView {
public <EC extends RbacObject> RbacView importRootEntityAliasProxy( public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
final String aliasName, final String aliasName,
final Class<? extends RbacObject> entityClass, final Class<? extends RbacObject> entityClass,
final ColumnValue forCase,
final SQL fetchSql, final SQL fetchSql,
final Column dependsOnColum) { final Column dependsOnColum) {
if (rootEntityAliasProxy != null) { if (rootEntityAliasProxy != null) {
@ -340,14 +349,6 @@ public class RbacView {
return this; return this;
} }
// TODO: remove once it's not used in HsOffice...Entity anymore
public RbacView importEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass,
final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this;
}
private EntityAlias importEntityAliasImpl( private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends RbacObject> entityClass, final String aliasName, final Class<? extends RbacObject> entityClass,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
@ -398,6 +399,12 @@ public class RbacView {
return this; return this;
} }
public RbacView switchOnColumn(final String discriminatorColumName, final CaseDef... caseDefs) {
// FIXME: currently only the default case is executed
stream(caseDefs).filter(caseDef -> caseDef.val == null).findAny().orElseThrow().def.accept(this);
return this;
}
private void verifyVersionColumnExists() { private void verifyVersionColumnExists() {
if (stream(rootEntityAlias.entityClass.getDeclaredFields()) if (stream(rootEntityAlias.entityClass.getDeclaredFields())
.noneMatch(f -> f.getAnnotation(Version.class) != null)) { .noneMatch(f -> f.getAnnotation(Version.class) != null)) {
@ -481,6 +488,25 @@ public class RbacView {
} }
public static class CaseDef {
private final String val;
private final Consumer<RbacView> def;
public CaseDef(final String discriminatorColumnValue, final Consumer<RbacView> def) {
this.val = discriminatorColumnValue;
this.def = def;
}
}
public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer<RbacView> def) {
return new CaseDef(discriminatorColumnValue, def);
}
public static CaseDef inOtherCases(final Consumer<RbacView> def) {
return new CaseDef(null, def);
}
public enum Nullable { public enum Nullable {
NOT_NULL, // DEFAULT NOT_NULL, // DEFAULT
NULLABLE NULLABLE
@ -496,7 +522,8 @@ public class RbacView {
private final RbacPermissionDefinition permDef; private final RbacPermissionDefinition permDef;
private boolean assumed = true; private boolean assumed = true;
private boolean toCreate = false; private boolean toCreate = false;
private String sqlWhere; private String onlyInCaseOf;
private String exceptInCaseOf;
@Override @Override
public String toString() { public String toString() {
@ -550,7 +577,7 @@ public class RbacView {
} }
boolean isConditional() { boolean isConditional() {
return sqlWhere != null; return onlyInCaseOf != null;
} }
boolean isToCreate() { boolean isToCreate() {
@ -579,8 +606,14 @@ public class RbacView {
return this; return this;
} }
public RbacGrantDefinition where(final String sqlWhere) { public RbacGrantDefinition onlyInCaseOf(final String caseName) {
this.sqlWhere = sqlWhere; this.onlyInCaseOf = caseName;
return this;
}
public RbacGrantDefinition exceptInCaseOf(final String caseName) {
this.exceptInCaseOf = caseName;
return this; return this;
} }
@ -1035,6 +1068,23 @@ public class RbacView {
} }
} }
public static class ColumnValue {
public static ColumnValue usingDefaultCase() {
return new ColumnValue(null);
}
public static ColumnValue usingCase(final String value) {
return new ColumnValue(value);
}
public final String value;
private ColumnValue(final String value) {
this.value = value;
}
}
private static class AliasNameMapper { private static class AliasNameMapper {
private final RbacView importedRbacView; private final RbacView importedRbacView;

View File

@ -301,11 +301,11 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef())) .replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
}; };
if (grantDef.isConditional()) { // if (grantDef.isConditional()) {
return "if " + grantDef.getSqlWhere() + " then\n" // return "if " + grantDef.getOnlyInCaseOf() + " then\n"
+ " " + grantSql + "\n" // + " " + grantSql + "\n"
+ "end if;"; // + "end if;";
} // }
return grantSql; return grantSql;
} }

View File

@ -167,7 +167,7 @@ public class RbacGrantsDiagramService {
return "users"; return "users";
} }
if (refType.equals("perm")) { if (refType.equals("perm")) {
return node.idName().split(" ", 4)[3]; return node.idName().split(":", 3)[1];
} }
if (refType.equals("role")) { if (refType.equals("role")) {
final var withoutRolePrefix = node.idName().substring("role:".length()); final var withoutRolePrefix = node.idName().substring("role:".length());

View File

@ -31,7 +31,7 @@ public class RbacRoleController implements RbacRolesApi {
context.define(currentUser, assumedRoles); context.define(currentUser, assumedRoles);
final List<RbacRoleRvEntity> result = rbacRoleRepository.findAll(); final List<RbacRoleEntity> result = rbacRoleRepository.findAll();
return ResponseEntity.ok(mapper.mapList(result, RbacRoleResource.class)); return ResponseEntity.ok(mapper.mapList(result, RbacRoleResource.class));
} }

View File

@ -15,7 +15,7 @@ import java.util.UUID;
@Immutable @Immutable
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class RbacRoleRvEntity { public class RbacRoleEntity {
@Id @Id
@GeneratedValue @GeneratedValue

View File

@ -5,7 +5,7 @@ import org.springframework.data.repository.Repository;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface RbacRoleRepository extends Repository<RbacRoleRvEntity, UUID> { public interface RbacRoleRepository extends Repository<RbacRoleEntity, UUID> {
/** /**
* @return the number of persistent RbacRoleEntity instances, mostly for testing purposes. * @return the number of persistent RbacRoleEntity instances, mostly for testing purposes.
@ -15,7 +15,7 @@ public interface RbacRoleRepository extends Repository<RbacRoleRvEntity, UUID> {
/** /**
* @return all persistent RbacRoleEntity instances, assigned to the current subject (user or assumed roles) * @return all persistent RbacRoleEntity instances, assigned to the current subject (user or assumed roles)
*/ */
List<RbacRoleRvEntity> findAll(); List<RbacRoleEntity> findAll();
RbacRoleRvEntity findByRoleName(String roleName); RbacRoleEntity findByRoleName(String roleName);
} }

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

@ -82,13 +82,12 @@ role:global:ADMIN -.-> role:contact:OWNER
role:contact:OWNER -.-> role:contact:ADMIN role:contact:OWNER -.-> role:contact:ADMIN
role:contact:ADMIN -.-> role:contact:REFERRER role:contact:ADMIN -.-> role:contact:REFERRER
role:global:ADMIN ==> role:relation:OWNER role:global:ADMIN ==> role:relation:OWNER
role:holderPerson:ADMIN ==> |??| role:relation:OWNER
role:relation:OWNER ==> role:relation:ADMIN role:relation:OWNER ==> role:relation:ADMIN
role:anchorPerson:ADMIN ==> |??| role:relation:ADMIN role:anchorPerson:ADMIN ==> role:relation:ADMIN
role:relation:ADMIN ==> |??| role:anchorPerson:OWNER
role:relation:ADMIN ==> role:relation:AGENT role:relation:ADMIN ==> role:relation:AGENT
role:holderPerson:ADMIN ==> |??| role:relation:AGENT role:holderPerson:ADMIN ==> role:relation:AGENT
role:relation:AGENT ==> role:relation:TENANT role:relation:AGENT ==> role:relation:TENANT
role:holderPerson:ADMIN ==> role:relation:TENANT
role:contact:ADMIN ==> role:relation:TENANT role:contact:ADMIN ==> role:relation:TENANT
role:relation:TENANT ==> role:anchorPerson:REFERRER role:relation:TENANT ==> role:anchorPerson:REFERRER
role:relation:TENANT ==> role:holderPerson:REFERRER role:relation:TENANT ==> role:holderPerson:REFERRER

View File

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

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,21 @@ 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 // referrer
"{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:REFERRER by system and assume }", "{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH: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:AGENT 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 +224,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

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

View File

@ -103,69 +103,6 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll()); final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()); final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
// when
attempt(em, () -> {
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
.filter(p -> p.getPersonType() == UNINCORPORATED_FIRM)
.findFirst().orElseThrow();
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Bert").stream()
.filter(p -> p.getPersonType() == NATURAL_PERSON)
.findFirst().orElseThrow();
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").stream()
.findFirst().orElseThrow();
final var newRelation = HsOfficeRelationEntity.builder()
.anchor(givenAnchorPerson)
.holder(givenHolderPerson)
.type(HsOfficeRelationType.SUBSCRIBER)
.mark("dummy")
.contact(givenContact)
.build();
return toCleanup(relationRepo.save(newRelation));
});
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// TODO: this grant should only be created for DEBITOR-Relationships, thus the RBAC DSL needs to support conditional grants
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:INSERT>hs_office_sepamandate to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:DELETE to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER to role:global#global:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER to user:superuser-alex@hostsharing.net by hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT by system and assume }",
"{ grant role:hs_office_person#BesslerBert:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_person#ErbenBesslerMelBessler:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_contact#fourthcontact:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
// SUBSCRIBER holder person -> (represented) anchor person
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
null)
);
}
@Test
public void createsAndGrantsRolesForTypeRepresentative() {
// given
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
// when // when
attempt(em, () -> { attempt(em, () -> {
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream() final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
@ -203,9 +140,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }", "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_person#ErbenBesslerMelBessler:OWNER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to role:hs_office_person#BesslerBert:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }", "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
@ -216,6 +153,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// REPRESENTATIVE holder person -> (represented) anchor person // REPRESENTATIVE holder person -> (represented) anchor person
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }", "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
null) null)
); );
@ -279,10 +217,10 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenRelation = givenSomeTemporaryRelationBessler( final var givenRelation = givenSomeTemporaryRelationBessler(
"Bert", "fifth contact"); "Bert", "fifth contact");
assertThatRelationActuallyInDatabase(givenRelation);
assertThatRelationIsVisibleForUserWithRole( assertThatRelationIsVisibleForUserWithRole(
givenRelation, givenRelation,
"hs_office_person#ErbenBesslerMelBessler:ADMIN"); "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow(); final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow();

View File

@ -5,8 +5,7 @@ import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity; import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository; import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService; import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRvEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -255,7 +254,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net", null); context.define("superuser-alex@hostsharing.net", null);
return rbacRoleRepo.findAll().stream() return rbacRoleRepo.findAll().stream()
.map(RbacRoleRvEntity::getRoleName) .map(RbacRoleEntity::getRoleName)
.collect(toSet()); .collect(toSet());
}).assertSuccessful().returnedValue(); }).assertSuccessful().returnedValue();
} }

View File

@ -5,7 +5,7 @@ import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse; import io.restassured.response.ValidatableResponse;
import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.ContextBasedTest; import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRvEntity; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity; import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository; import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository;
@ -361,11 +361,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
this(currentUser, ""); this(currentUser, "");
} }
GrantFixture grantsRole(final RbacRoleRvEntity givenOwnPackageAdminRole) { GrantFixture grantsRole(final RbacRoleEntity givenOwnPackageAdminRole) {
return new GrantFixture(givenOwnPackageAdminRole); return new GrantFixture(givenOwnPackageAdminRole);
} }
RevokeFixture revokesRole(final RbacRoleRvEntity givenOwnPackageAdminRole) { RevokeFixture revokesRole(final RbacRoleEntity givenOwnPackageAdminRole) {
return new RevokeFixture(givenOwnPackageAdminRole); return new RevokeFixture(givenOwnPackageAdminRole);
} }
@ -376,11 +376,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
class GrantFixture { class GrantFixture {
private Subject grantingSubject = Subject.this; private Subject grantingSubject = Subject.this;
private final RbacRoleRvEntity grantedRole; private final RbacRoleEntity grantedRole;
private boolean assumed; private boolean assumed;
private RbacUserEntity granteeUser; private RbacUserEntity granteeUser;
public GrantFixture(final RbacRoleRvEntity roleToGrant) { public GrantFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant; this.grantedRole = roleToGrant;
} }
@ -417,11 +417,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
class RevokeFixture { class RevokeFixture {
private Subject currentSubject = Subject.this; private Subject currentSubject = Subject.this;
private final RbacRoleRvEntity grantedRole; private final RbacRoleEntity grantedRole;
private boolean assumed; private boolean assumed;
private RbacUserEntity granteeUser; private RbacUserEntity granteeUser;
public RevokeFixture(final RbacRoleRvEntity roleToGrant) { public RevokeFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant; this.grantedRole = roleToGrant;
} }
@ -455,9 +455,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
private class GetGrantByIdFixture { private class GetGrantByIdFixture {
private Subject currentSubject = Subject.this; private Subject currentSubject = Subject.this;
private RbacRoleRvEntity grantedRole; private RbacRoleEntity grantedRole;
GetGrantByIdFixture forGrantedRole(final RbacRoleRvEntity grantedRole) { GetGrantByIdFixture forGrantedRole(final RbacRoleEntity grantedRole) {
this.grantedRole = grantedRole; this.grantedRole = grantedRole;
return this; return this;
} }
@ -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

@ -175,21 +175,21 @@ class RbacRoleRepositoryIntegrationTest {
} }
} }
void exactlyTheseRbacRolesAreReturned(final List<RbacRoleRvEntity> actualResult, final String... expectedRoleNames) { void exactlyTheseRbacRolesAreReturned(final List<RbacRoleEntity> actualResult, final String... expectedRoleNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(RbacRoleRvEntity::getRoleName) .extracting(RbacRoleEntity::getRoleName)
.containsExactlyInAnyOrder(expectedRoleNames); .containsExactlyInAnyOrder(expectedRoleNames);
} }
void allTheseRbacRolesAreReturned(final List<RbacRoleRvEntity> actualResult, final String... expectedRoleNames) { void allTheseRbacRolesAreReturned(final List<RbacRoleEntity> actualResult, final String... expectedRoleNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(RbacRoleRvEntity::getRoleName) .extracting(RbacRoleEntity::getRoleName)
.contains(expectedRoleNames); .contains(expectedRoleNames);
} }
void noneOfTheseRbacRolesIsReturned(final List<RbacRoleRvEntity> actualResult, final String... unexpectedRoleNames) { void noneOfTheseRbacRolesIsReturned(final List<RbacRoleEntity> actualResult, final String... unexpectedRoleNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(RbacRoleRvEntity::getRoleName) .extracting(RbacRoleEntity::getRoleName)
.doesNotContain(unexpectedRoleNames); .doesNotContain(unexpectedRoleNames);
} }

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);
} }
} }