diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRbacEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRbacEntity.java index c07c4d02..bc9988c5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRbacEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemRbacEntity.java @@ -5,8 +5,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRbacEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import jakarta.persistence.AttributeOverride; import jakarta.persistence.AttributeOverrides; @@ -15,20 +15,20 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import java.io.IOException; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NULLABLE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.DELETE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.AGENT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.OWNER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.TENANT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "hs_booking", name = "item_rv") @@ -41,7 +41,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; }) public class HsBookingItemRbacEntity extends HsBookingItem { - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("bookingItem", HsBookingItemRbacEntity.class) .withIdentityView(SQL.projection("caption")) .withRestrictedViewOrderBy(SQL.expression("validity")) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectRbacEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectRbacEntity.java index 259fe5fa..90417809 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectRbacEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectRbacEntity.java @@ -6,30 +6,30 @@ import lombok.Setter; import lombok.experimental.SuperBuilder; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import jakarta.persistence.Entity; import jakarta.persistence.Table; import java.io.IOException; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.DELETE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.AGENT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.OWNER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.TENANT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.fetchedBySql; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "hs_booking", name = "project_rv") @@ -39,7 +39,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; @NoArgsConstructor public class HsBookingProjectRbacEntity extends HsBookingProject { - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("project", HsBookingProjectRbacEntity.class) .withIdentityView(SQL.query(""" SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRbacEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRbacEntity.java index a7f48eb3..4c020d8b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRbacEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRbacEntity.java @@ -6,31 +6,31 @@ import lombok.Setter; import lombok.experimental.SuperBuilder; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRbacEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import jakarta.persistence.Entity; import jakarta.persistence.Table; import java.io.IOException; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inCaseOf; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.GUEST; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.REFERRER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef.inCaseOf; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NULLABLE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.DELETE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.AGENT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.GUEST; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.OWNER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.REFERRER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.TENANT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "hs_hosting", name = "asset_rv") @@ -40,7 +40,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; @NoArgsConstructor public class HsHostingAssetRbacEntity extends HsHostingAsset { - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("asset", HsHostingAssetRbacEntity.class) .withIdentityView(SQL.projection("identifier")) .withRestrictedViewOrderBy(SQL.expression("identifier")) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java index edc26010..de7f6782 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java @@ -4,7 +4,7 @@ import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.persistence.BaseEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringifyable; @@ -12,10 +12,10 @@ import jakarta.persistence.*; import java.io.IOException; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; import static net.hostsharing.hsadminng.repr.Stringify.stringify; @Entity @@ -57,7 +57,7 @@ public class HsOfficeBankAccountEntity implements BaseEntity, return getTaggedDebitorNumber(); } - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("debitor", HsOfficeDebitorEntity.class) .withIdentityView(SQL.query(""" SELECT debitor.uuid AS uuid, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java index 1b05a90c..3b0f58de 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java @@ -11,8 +11,8 @@ import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.persistence.BaseEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringifyable; import org.hibernate.annotations.Type; @@ -38,21 +38,21 @@ import static io.hypersistence.utils.hibernate.type.range.Range.emptyRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.DELETE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.AGENT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.OWNER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.TENANT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.fetchedBySql; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; import static net.hostsharing.hsadminng.repr.Stringify.stringify; @Entity @@ -160,7 +160,7 @@ public class HsOfficeMembershipEntity implements BaseEntity { - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("person", HsOfficePersonRbacEntity.class) .withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)")) .withUpdatableColumns("personType", "title", "salutation", "tradeName", "givenName", "familyName") diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRbacEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRbacEntity.java index 062c3702..c0f8476f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRbacEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRbacEntity.java @@ -7,31 +7,31 @@ import lombok.experimental.SuperBuilder; import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRbacEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import jakarta.persistence.Entity; import jakarta.persistence.Table; import java.io.IOException; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inCaseOf; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inOtherCases; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.REFERRER; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef.inCaseOf; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef.inOtherCases; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.DELETE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.SELECT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.AGENT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.OWNER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.REFERRER; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.TENANT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "hs_office", name = "relation_rv") @@ -42,7 +42,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; @DisplayAs("RbacRelation") public class HsOfficeRelationRbacEntity extends HsOfficeRelation { - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("relation", HsOfficeRelationRbacEntity.class) .withIdentityView(SQL.projection(""" (select idName from hs_office.person_iv p where p.uuid = anchorUuid) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index 94ac21e2..e1d8d973 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.persistence.BaseEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringifyable; import org.hibernate.annotations.Type; @@ -20,16 +20,16 @@ import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; import static net.hostsharing.hsadminng.repr.Stringify.stringify; @Entity @@ -100,7 +100,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, BaseEntity cases) { + private String toStringList(final Set cases) { return cases.stream().map(c -> "'" + c.value + "'").collect(joining(", ")); } - private boolean isGrantToADifferentTable(final RbacView.RbacGrantDefinition g) { + private boolean isGrantToADifferentTable(final RbacSpec.RbacGrantDefinition g) { return !rbacDef.getRootEntityAlias().getRawTableNameWithSchema().equals(g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()); } - private Stream getInsertGrants() { + private Stream getInsertGrants() { return rbacDef.getGrantDefs().stream() .filter(g -> g.grantType() == PERM_TO_ROLE) .filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT); @@ -298,14 +298,14 @@ public class InsertTriggerGenerator { g.getSuperRoleDef().getEntityAlias().isGlobal() && g.getSuperRoleDef().getRole() == GUEST); } - private Optional getOptionalInsertGrant() { + private Optional getOptionalInsertGrant() { return getInsertGrants() .reduce(singleton()); } - private Optional getOptionalInsertSuperRole() { + private Optional getOptionalInsertSuperRole() { return getInsertGrants() - .map(RbacView.RbacGrantDefinition::getSuperRoleDef) + .map(RbacSpec.RbacGrantDefinition::getSuperRoleDef) .reduce(singleton()); } @@ -319,12 +319,12 @@ public class InsertTriggerGenerator { }; } - private static String toVar(final RbacView.RbacRoleDefinition roleDef) { + private static String toVar(final RbacSpec.RbacRoleDefinition roleDef) { return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().name()); } - private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) { + private String toRoleDescriptor(final RbacSpec.RbacRoleDefinition roleDef, final String ref) { final var functionName = roleDef.descriptorFunctionName(); if (roleDef.getEntityAlias().isGlobal()) { return functionName + "()"; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacIdentityViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacIdentityViewGenerator.java index 473cc394..617794d5 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacIdentityViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacIdentityViewGenerator.java @@ -3,12 +3,12 @@ package net.hostsharing.hsadminng.rbac.generator; import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; public class RbacIdentityViewGenerator { - private final RbacView rbacDef; + private final RbacSpec rbacDef; private final String liquibaseTagPrefix; private final String simpleEntityVarName; private final String rawTableName; - public RbacIdentityViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { + public RbacIdentityViewGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { this.rbacDef = rbacDef; this.liquibaseTagPrefix = liquibaseTagPrefix; this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacObjectGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacObjectGenerator.java index f641ad99..f8d06730 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacObjectGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacObjectGenerator.java @@ -7,7 +7,7 @@ public class RbacObjectGenerator { private final String liquibaseTagPrefix; private final String rawTableName; - public RbacObjectGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { + public RbacObjectGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { this.liquibaseTagPrefix = liquibaseTagPrefix; this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRbacSystemRebuildGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRbacSystemRebuildGenerator.java new file mode 100644 index 00000000..945cb6c5 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRbacSystemRebuildGenerator.java @@ -0,0 +1,80 @@ +package net.hostsharing.hsadminng.rbac.generator; + +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition; + +import java.util.HashSet; +import java.util.Set; +import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; + +class RbacRbacSystemRebuildGenerator { + + private final RbacSpec rbacDef; + private final Set rbacGrants = new HashSet<>(); + private final String liquibaseTagPrefix; + private final String rawTableName; + + RbacRbacSystemRebuildGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { + this.rbacDef = rbacDef; + this.liquibaseTagPrefix = liquibaseTagPrefix; + this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); + } + + void generateTo(final StringWriter plPgSql) { + plPgSql.writeLn(""" + -- ============================================================================ + --changeset RbacRbacSystemRebuildGenerator:${liquibaseTagPrefix}-rbac-rebuild endDelimiter:--// + -- ---------------------------------------------------------------------------- + + -- HOWTO: Rebuild RBAC-system for table ${rawTableName} after changing its RBAC specification. + -- + -- begin transaction; + -- call base.defineContext('re-creating RBAC for table ${rawTableName}', null, <>); + -- call ${rawTableName}_rebuild_rbac_system(); + -- commit; + -- + -- How it works: + -- 1. All grants previously created from the RBAC specification of this table will be deleted. + -- These grants are identified by `${rawTableName}.grantedByTriggerOf IS NOT NULL`. + -- User-induced grants (`${rawTableName}.grantedByTriggerOf IS NULL`) are NOT deleted. + -- 2. New role types will be created, but existing role types which are not specified anymore, + -- will NOT be deleted! + -- 3. All newly specified grants will be created. + -- + -- IMPORTANT: + -- Make sure not to skip any previously defined role-types or you might break indirect grants! + -- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type + -- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role + -- of this table would be in a dead end. + + create or replace procedure ${rawTableName}_rebuild_rbac_system() + language plpgsql as $$ + DECLARE + DECLARE + row ${rawTableName}; + grantsAfter numeric; + grantsBefore numeric; + BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM ${rawTableName} LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL ${rawTableName}_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; + END; + $$; + --// + + """, + with("liquibaseTagPrefix", liquibaseTagPrefix), + with("rawTableName", rawTableName)); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRestrictedViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRestrictedViewGenerator.java index f493d382..35adfc91 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRestrictedViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRestrictedViewGenerator.java @@ -6,11 +6,11 @@ import static net.hostsharing.hsadminng.rbac.generator.StringWriter.indented; import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; public class RbacRestrictedViewGenerator { - private final RbacView rbacDef; + private final RbacSpec rbacDef; private final String liquibaseTagPrefix; private final String rawTableName; - public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { + public RbacRestrictedViewGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { this.rbacDef = rbacDef; this.liquibaseTagPrefix = liquibaseTagPrefix; this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRoleDescriptorsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRoleDescriptorsGenerator.java index 4d78d0c2..fb242fbf 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRoleDescriptorsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacRoleDescriptorsGenerator.java @@ -8,7 +8,7 @@ public class RbacRoleDescriptorsGenerator { private final String simpleEntityVarName; private final String rawTableName; - public RbacRoleDescriptorsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { + public RbacRoleDescriptorsGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { this.liquibaseTagPrefix = liquibaseTagPrefix; this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacSpec.java similarity index 95% rename from src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacView.java rename to src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacSpec.java index dfdf3821..8dee16b9 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacSpec.java @@ -22,19 +22,18 @@ import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static java.util.Collections.max; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.ROLE_TO_ROLE; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.Part.AUTO_FETCH; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition.GrantType.PERM_TO_ROLE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition.GrantType.ROLE_TO_ROLE; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.Part.AUTO_FETCH; import static org.apache.commons.collections4.SetUtils.hashSet; import static org.apache.commons.lang3.StringUtils.capitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize; @Getter -// TODO.refa: rename to RbacDSL -public class RbacView { +public class RbacSpec { public static final String GLOBAL = "rbac.global"; public static final String OUTPUT_BASEDIR = "src/main/resources/db/changelog"; @@ -90,11 +89,11 @@ public class RbacView { * @param * a JPA entity class extending RbacObject */ - public static > RbacView rbacViewFor(final String alias, final Class entityClass) { - return new RbacView(alias, entityClass); + public static > RbacSpec rbacViewFor(final String alias, final Class entityClass) { + return new RbacSpec(alias, entityClass); } - RbacView(final String alias, final Class> entityClass) { + RbacSpec(final String alias, final Class> entityClass) { rootEntityAlias = new EntityAlias(alias, entityClass); entityAliases.put(alias, rootEntityAlias); new RbacSubjectReference(CREATOR); @@ -110,7 +109,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView withUpdatableColumns(final String... columnNames) { + public RbacSpec withUpdatableColumns(final String... columnNames) { Collections.addAll(updatableColumns, columnNames); verifyVersionColumnExists(); return this; @@ -134,7 +133,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView withIdentityView(final SQL sqlExpression) { + public RbacSpec withIdentityView(final SQL sqlExpression) { this.identityViewSqlQuery = sqlExpression; return this; } @@ -150,7 +149,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) { + public RbacSpec withRestrictedViewOrderBy(final SQL orderBySqlExpression) { this.orderBySqlExpression = orderBySqlExpression; return this; } @@ -166,7 +165,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView createRole(final Role role, final Consumer with) { + public RbacSpec createRole(final Role role, final Consumer with) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); with.accept(newRoleDef); previousRoleDef = newRoleDef; @@ -182,7 +181,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView createSubRole(final Role role) { + public RbacSpec createSubRole(final Role role) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); previousRoleDef = newRoleDef; @@ -202,7 +201,7 @@ public class RbacView { * @return * the `this` instance itself to allow chained calls. */ - public RbacView createSubRole(final Role role, final Consumer with) { + public RbacSpec createSubRole(final Role role, final Consumer with) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); with.accept(newRoleDef); @@ -254,7 +253,7 @@ public class RbacView { .orElseGet(() -> new RbacPermissionDefinition(entityAlias, permission, null, true)); } - public RbacView declarePlaceholderEntityAliases(final String... aliasNames) { + public RbacSpec declarePlaceholderEntityAliases(final String... aliasNames) { for (String alias : aliasNames) { entityAliases.put(alias, new EntityAlias(alias)); } @@ -287,7 +286,7 @@ public class RbacView { * @param * a JPA entity class extending RbacObject */ - public > RbacView importRootEntityAliasProxy( + public > RbacSpec importRootEntityAliasProxy( final String aliasName, final Class> entityClass, final ColumnValue forCase, @@ -312,7 +311,7 @@ public class RbacView { * @param * a JPA entity class extending RbacObject */ - public RbacView importSubEntityAlias( + public RbacSpec importSubEntityAlias( final String aliasName, final Class> entityClass, final SQL fetchSql, final Column dependsOnColum) { importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, true, NOT_NULL); @@ -349,7 +348,7 @@ public class RbacView { * @param * a JPA entity class extending RbacObject */ - public RbacView importEntityAlias( + public RbacSpec importEntityAlias( final String aliasName, final Class> entityClass, final ColumnValue usingCase, final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) { importEntityAliasImpl(aliasName, entityClass, usingCase, fetchSql, dependsOnColum, false, nullable); @@ -379,12 +378,12 @@ public class RbacView { return entityAlias; } - private static RbacView rbacDefinition(final Class entityClass) + private static RbacSpec rbacDefinition(final Class entityClass) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { - return (RbacView) entityClass.getMethod("rbac").invoke(null); + return (RbacSpec) entityClass.getMethod("rbac").invoke(null); } - private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final ColumnValue forCase, final boolean asSubEntity) { + private RbacSpec importAsAlias(final String aliasName, final RbacSpec importedRbacView, final ColumnValue forCase, final boolean asSubEntity) { final var mapper = new AliasNameMapper(importedRbacView, aliasName, asSubEntity ? entityAliases.keySet() : null); copyOf(importedRbacView.getEntityAliases().values()).stream() @@ -416,7 +415,7 @@ public class RbacView { return this; } - public RbacView switchOnColumn(final String discriminatorColumName, final CaseDef... caseDefs) { + public RbacSpec switchOnColumn(final String discriminatorColumName, final CaseDef... caseDefs) { this.discriminatorColumName = discriminatorColumName; allCases.addAll(stream(caseDefs).toList()); @@ -511,7 +510,7 @@ public class RbacView { new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql")); } - public RbacView limitDiagramTo(final String... aliasNames) { + public RbacSpec limitDiagramTo(final String... aliasNames) { this.limitDiagramToAliasNames = Set.of(aliasNames); return this; } @@ -542,15 +541,15 @@ public class RbacView { this.superRoleDef = findRbacRole(entityAlias, role); } - public RbacView grantRole(final String entityAlias, final Role role) { + public RbacSpec grantRole(final String entityAlias, final Role role) { findOrCreateGrantDef(findRbacRole(entityAlias, role), superRoleDef).toCreate(); - return RbacView.this; + return RbacSpec.this; } - public RbacView grantPermission(final Permission perm) { + public RbacSpec grantPermission(final Permission perm) { final var forTable = rootEntityAlias.getRawTableNameWithSchema(); findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate(); - return RbacView.this; + return RbacSpec.this; } } @@ -698,10 +697,10 @@ public class RbacView { this.subRole = role; } - public RbacView wouldBeGrantedTo(final String entityAlias, final Role role) { + public RbacSpec wouldBeGrantedTo(final String entityAlias, final Role role) { this.superRoleEntity = findEntityAlias(entityAlias); this.superRole = role; - return RbacView.this; + return RbacSpec.this; } } @@ -733,9 +732,9 @@ public class RbacView { * @return * The RbacView specification to which this permission definition belongs. */ - public RbacView grantedTo(final String entityAlias, final Role role) { + public RbacSpec grantedTo(final String entityAlias, final Role role) { findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate(); - return RbacView.this; + return RbacSpec.this; } @Override @@ -1186,12 +1185,12 @@ public class RbacView { private static class AliasNameMapper { - private final RbacView importedRbacView; + private final RbacSpec importedRbacView; private final String outerAliasName; private final Set outerAliasNames; - AliasNameMapper(final RbacView importedRbacView, final String outerAliasName, final Set outerAliasNames) { + AliasNameMapper(final RbacSpec importedRbacView, final String outerAliasName, final Set outerAliasNames) { this.importedRbacView = importedRbacView; this.outerAliasName = outerAliasName; this.outerAliasNames = (outerAliasNames == null) ? Collections.emptySet() : outerAliasNames; @@ -1210,19 +1209,19 @@ public class RbacView { public static class CaseDef extends ColumnValue { - final Consumer def; + final Consumer def; - private CaseDef(final String discriminatorColumnValue, final Consumer def) { + private CaseDef(final String discriminatorColumnValue, final Consumer def) { super(discriminatorColumnValue); this.def = def; } - public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer def) { + public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer def) { return new CaseDef(discriminatorColumnValue, def); } - public static CaseDef inOtherCases(final Consumer def) { + public static CaseDef inOtherCases(final Consumer def) { return new CaseDef(null, def); } @@ -1281,7 +1280,7 @@ public class RbacView { .filter(c -> stream(c.getDeclaredMethods()) .anyMatch(m -> m.getName().equals("rbac") && isStatic(m.getModifiers())) ) - .map(RbacView::castToSubclassOfBaseEntity) + .map(RbacSpec::castToSubclassOfBaseEntity) .collect(Collectors.toSet()); return rbacEntityClasses; } @@ -1296,6 +1295,6 @@ public class RbacView { */ public static void main(String[] args) throws Exception { findRbacEntityClasses("net.hostsharing.hsadminng") - .forEach(RbacView::generateRbacView); + .forEach(RbacSpec::generateRbacView); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewMermaidFlowchartGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewMermaidFlowchartGenerator.java index 6a457310..a4c36917 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewMermaidFlowchartGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewMermaidFlowchartGenerator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.generator; import lombok.SneakyThrows; -import net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef; import org.apache.commons.lang3.StringUtils; import java.nio.file.*; @@ -12,7 +12,7 @@ import java.util.stream.Stream; import static java.util.Comparator.comparing; import static java.util.stream.Collectors.joining; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition.GrantType.*; public class RbacViewMermaidFlowchartGenerator { @@ -20,14 +20,14 @@ public class RbacViewMermaidFlowchartGenerator { public static final String HOSTSHARING_LIGHT_ORANGE = "#feb28c"; public static final String HOSTSHARING_DARK_BLUE = "#274d6e"; public static final String HOSTSHARING_LIGHT_BLUE = "#99bcdb"; - private final RbacView rbacDef; + private final RbacSpec rbacDef; - private final List usedEntityAliases; + private final List usedEntityAliases; private final CaseDef forCase; private final StringWriter flowchart = new StringWriter(); - public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef, final CaseDef forCase) { + public RbacViewMermaidFlowchartGenerator(final RbacSpec rbacDef, final CaseDef forCase) { this.rbacDef = rbacDef; this.forCase = forCase; @@ -37,7 +37,7 @@ public class RbacViewMermaidFlowchartGenerator { g.getSubRoleDef() != null ? g.getSubRoleDef().getEntityAlias() : null, g.getPermDef() != null ? g.getPermDef().getEntityAlias() : null)) .filter(Objects::nonNull) - .sorted(comparing(RbacView.EntityAlias::aliasName)) + .sorted(comparing(RbacSpec.EntityAlias::aliasName)) .distinct() .filter(rbacDef::renderInDiagram) .collect(Collectors.toList()); @@ -50,7 +50,7 @@ public class RbacViewMermaidFlowchartGenerator { renderGrants(); } - public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef) { + public RbacViewMermaidFlowchartGenerator(final RbacSpec rbacDef) { this(rbacDef, null); } private void renderEntitySubgraphs() { @@ -61,7 +61,7 @@ public class RbacViewMermaidFlowchartGenerator { .forEach(this::renderEntitySubgraph); } - private void renderEntitySubgraph(final RbacView.EntityAlias entity) { + private void renderEntitySubgraph(final RbacSpec.EntityAlias entity) { if (!rbacDef.renderInDiagram(entity)) { return; } @@ -128,7 +128,7 @@ public class RbacViewMermaidFlowchartGenerator { renderGrants(PERM_TO_ROLE, "%% granting permissions to roles"); } - private void renderGrants(final RbacView.RbacGrantDefinition.GrantType grantType, final String comment) { + private void renderGrants(final RbacSpec.RbacGrantDefinition.GrantType grantType, final String comment) { final var grantsOfRequestedType = rbacDef.getGrantDefs().stream() .filter(g -> g.grantType() == grantType) .filter(rbacDef::renderInDiagram) @@ -141,7 +141,7 @@ public class RbacViewMermaidFlowchartGenerator { } } - private boolean isToBeRenderedForThisCase(final RbacView.RbacGrantDefinition g) { + private boolean isToBeRenderedForThisCase(final RbacSpec.RbacGrantDefinition g) { if ( g.grantType() == ROLE_TO_USER ) return true; if ( forCase == null && !g.isConditional() ) @@ -150,7 +150,7 @@ public class RbacViewMermaidFlowchartGenerator { return isToBeRenderedInThisGraph; } - private String grantDef(final RbacView.RbacGrantDefinition grant) { + private String grantDef(final RbacSpec.RbacGrantDefinition grant) { final var arrow = (grant.isToCreate() ? " ==>" : " -.->") + (grant.isAssumed() ? " " : "|XX| "); final var grantDef = switch (grant.grantType()) { @@ -164,19 +164,19 @@ public class RbacViewMermaidFlowchartGenerator { return grantDef; } - private String permDef(final RbacView.RbacPermissionDefinition perm) { + private String permDef(final RbacSpec.RbacPermissionDefinition perm) { return permId(perm) + "{{" + perm.getEntityAlias().aliasName() + perm.getPermission() + "}}"; } - private static String permId(final RbacView.RbacPermissionDefinition permDef) { + private static String permId(final RbacSpec.RbacPermissionDefinition permDef) { return "perm:" + permDef.getEntityAlias().aliasName() + permDef.getPermission(); } - private String roleDef(final RbacView.RbacRoleDefinition roleDef) { + private String roleDef(final RbacSpec.RbacRoleDefinition roleDef) { return roleId(roleDef) + "[[" + roleDef.getEntityAlias().aliasName() + roleDef.getRole() + "]]"; } - private static String roleId(final RbacView.RbacRoleDefinition r) { + private static String roleId(final RbacSpec.RbacRoleDefinition r) { return "role:" + r.getEntityAlias().aliasName() + r.getRole(); } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewPostgresGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewPostgresGenerator.java index a8a4ba3b..6b23ef1c 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RbacViewPostgresGenerator.java @@ -11,11 +11,11 @@ import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; public class RbacViewPostgresGenerator { - private final RbacView rbacDef; + private final RbacSpec rbacDef; private final String liqibaseTagPrefix; private final StringWriter plPgSql = new StringWriter(); - public RbacViewPostgresGenerator(final RbacView forRbacDef) { + public RbacViewPostgresGenerator(final RbacSpec forRbacDef) { rbacDef = forRbacDef; liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableNameWithSchema().replace("_", "-").replace(".", "-"); plPgSql.writeLn(""" @@ -31,6 +31,7 @@ public class RbacViewPostgresGenerator { new InsertTriggerGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); new RbacIdentityViewGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); new RbacRestrictedViewGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); + new RbacRbacSystemRebuildGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); } @Override diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RolesGrantsAndPermissionsGenerator.java index 6d880144..67adbe72 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/generator/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/generator/RolesGrantsAndPermissionsGenerator.java @@ -1,8 +1,8 @@ package net.hostsharing.hsadminng.rbac.generator; -import net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef; -import net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition; -import net.hostsharing.hsadminng.rbac.generator.RbacView.RbacPermissionDefinition; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.CaseDef; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacPermissionDefinition; import java.util.HashSet; import java.util.List; @@ -15,22 +15,22 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toSet; import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.OLD; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.INSERT; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacGrantDefinition.GrantType.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static org.apache.commons.lang3.StringUtils.capitalize; class RolesGrantsAndPermissionsGenerator { - private final RbacView rbacDef; + private final RbacSpec rbacDef; private final Set rbacGrants = new HashSet<>(); private final String liquibaseTagPrefix; private final String simpleEntityName; private final String simpleEntityVarName; private final String qualifiedRawTableName; - RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { + RolesGrantsAndPermissionsGenerator(final RbacSpec rbacDef, final String liquibaseTagPrefix) { this.rbacDef = rbacDef; this.rbacGrants.addAll(rbacDef.getGrantDefs().stream() .filter(RbacGrantDefinition::isToCreate) @@ -95,7 +95,7 @@ class RolesGrantsAndPermissionsGenerator { private void generateSimplifiedUpdateTriggerFunction(final StringWriter plPgSql) { final var updateConditions = updatableEntityAliases() - .map(RbacView.EntityAlias::dependsOnColumName) + .map(RbacSpec.EntityAlias::dependsOnColumName) .distinct() .map(columnName -> "NEW." + columnName + " is distinct from OLD." + columnName) .collect(joining( "\n or ")); @@ -166,7 +166,7 @@ class RolesGrantsAndPermissionsGenerator { private boolean hasAnyUpdatableAndNullableEntityAliases() { return updatableEntityAliases() - .filter(ea -> ea.nullable() == RbacView.Nullable.NULLABLE) + .filter(ea -> ea.nullable() == RbacSpec.Nullable.NULLABLE) .anyMatch(e -> true); } @@ -210,7 +210,7 @@ class RolesGrantsAndPermissionsGenerator { generateGrants(plPgSql, PERM_TO_ROLE); } - private Stream referencedEntityAliases() { + private Stream referencedEntityAliases() { return rbacDef.getEntityAliases().values().stream() .filter(ea -> !rbacDef.isRootEntityAlias(ea)) .filter(ea -> ea.dependsOnColum() != null) @@ -218,7 +218,7 @@ class RolesGrantsAndPermissionsGenerator { .filter(ea -> ea.fetchSql() != null); } - private Stream updatableEntityAliases() { + private Stream updatableEntityAliases() { return referencedEntityAliases() .filter(ea -> rbacDef.getUpdatableColumns().contains(ea.dependsOnColum().column)); } @@ -234,7 +234,7 @@ class RolesGrantsAndPermissionsGenerator { }); updatableEntityAliases() - .map(RbacView.EntityAlias::dependsOnColum) + .map(RbacSpec.EntityAlias::dependsOnColum) .map(c -> c.column) .sorted() .distinct() @@ -250,18 +250,19 @@ class RolesGrantsAndPermissionsGenerator { private void generateFetchedVars( final StringWriter plPgSql, - final RbacView.EntityAlias ea, + final RbacSpec.EntityAlias ea, final PostgresTriggerReference old) { plPgSql.writeLn( ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";", with("columns", ea.aliasName() + ".*"), with("ref", old.name())); - if (ea.nullable() == RbacView.Nullable.NOT_NULL) { + if (ea.nullable() == RbacSpec.Nullable.NOT_NULL) { plPgSql.writeLn( - "assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s', ${REF}.${dependsOnColumn});", + "assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s of ${rawTable}', ${REF}.${dependsOnColumn});", with("entityRefVar", entityRefVar(old, ea)), with("dependsOnColumn", ea.dependsOnColumName()), - with("ref", old.name())); + with("ref", old.name()), + with("rawTable", qualifiedRawTableName)); plPgSql.writeLn(); } } @@ -352,11 +353,11 @@ class RolesGrantsAndPermissionsGenerator { .replace("${perm}", permDef.permission.name()); } - private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) { + private String refVarName(final PostgresTriggerReference ref, final RbacSpec.EntityAlias entityAlias) { return ref.name().toLowerCase() + capitalize(entityAlias.aliasName()); } - private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) { + private String roleRef(final PostgresTriggerReference rootRefVar, final RbacSpec.RbacRoleDefinition roleDef) { if (roleDef == null) { System.out.println("null"); } @@ -369,17 +370,17 @@ class RolesGrantsAndPermissionsGenerator { private String entityRefVar( final PostgresTriggerReference rootRefVar, - final RbacView.EntityAlias entityAlias) { + final RbacSpec.EntityAlias entityAlias) { return rbacDef.isRootEntityAlias(entityAlias) ? rootRefVar.name() : rootRefVar.name().toLowerCase() + capitalize(entityAlias.aliasName()); } - private void createRolesWithGrantsSql(final StringWriter plPgSql, final RbacView.Role role) { + private void createRolesWithGrantsSql(final StringWriter plPgSql, final RbacSpec.Role role) { final var isToCreate = rbacDef.getRoleDefs().stream() .filter(roleDef -> rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) && roleDef.getRole() == role) - .findFirst().map(RbacView.RbacRoleDefinition::isToCreate).orElse(false); + .findFirst().map(RbacSpec.RbacRoleDefinition::isToCreate).orElse(false); if (!isToCreate) { return; } @@ -403,7 +404,7 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.writeLn(");"); } - private void generateUserGrantsForRole(final StringWriter plPgSql, final RbacView.Role role) { + private void generateUserGrantsForRole(final StringWriter plPgSql, final RbacSpec.Role role) { final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role); if (!grantsToUsers.isEmpty()) { final var arrayElements = grantsToUsers.stream() @@ -416,13 +417,13 @@ class RolesGrantsAndPermissionsGenerator { } } - private void generatePermissionsForRole(final StringWriter plPgSql, final RbacView.Role role) { + private void generatePermissionsForRole(final StringWriter plPgSql, final RbacSpec.Role role) { final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role); if (!permissionGrantsForRole.isEmpty()) { final var arrayElements = permissionGrantsForRole.stream() .map(RbacGrantDefinition::getPermDef) .map(RbacPermissionDefinition::getPermission) - .map(RbacView.Permission::name) + .map(RbacSpec.Permission::name) .map(p -> "'" + p + "'") .sorted() .toList(); @@ -432,7 +433,7 @@ class RolesGrantsAndPermissionsGenerator { } } - private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) { + private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacSpec.Role role) { final var unconditionalIncomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream() .filter(g -> !g.isConditional()) .toList(); @@ -446,7 +447,7 @@ class RolesGrantsAndPermissionsGenerator { } } - private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) { + private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacSpec.Role role) { final var unconditionalOutgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream() .filter(g -> !g.isConditional()) .toList(); @@ -467,8 +468,8 @@ class RolesGrantsAndPermissionsGenerator { } private Set findPermissionsGrantsForRole( - final RbacView.EntityAlias entityAlias, - final RbacView.Role role) { + final RbacSpec.EntityAlias entityAlias, + final RbacSpec.Role role) { final var roleDef = rbacDef.findRbacRole(entityAlias, role); return rbacGrants.stream() .filter(g -> g.grantType() == PERM_TO_ROLE && g.getSuperRoleDef() == roleDef) @@ -476,8 +477,8 @@ class RolesGrantsAndPermissionsGenerator { } private Set findGrantsToUserForRole( - final RbacView.EntityAlias entityAlias, - final RbacView.Role role) { + final RbacSpec.EntityAlias entityAlias, + final RbacSpec.Role role) { final var roleDef = rbacDef.findRbacRole(entityAlias, role); return rbacGrants.stream() .filter(g -> g.grantType() == ROLE_TO_USER && g.getSubRoleDef() == roleDef) @@ -485,8 +486,8 @@ class RolesGrantsAndPermissionsGenerator { } private Set findIncomingSuperRolesForRole( - final RbacView.EntityAlias entityAlias, - final RbacView.Role role) { + final RbacSpec.EntityAlias entityAlias, + final RbacSpec.Role role) { final var roleDef = rbacDef.findRbacRole(entityAlias, role); return rbacGrants.stream() .filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSubRoleDef() == roleDef) @@ -494,8 +495,8 @@ class RolesGrantsAndPermissionsGenerator { } private Set findOutgoingSuperRolesForRole( - final RbacView.EntityAlias entityAlias, - final RbacView.Role role) { + final RbacSpec.EntityAlias entityAlias, + final RbacSpec.Role role) { final var roleDef = rbacDef.findRbacRole(entityAlias, role); return rbacGrants.stream() .filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSuperRoleDef() == roleDef) @@ -579,7 +580,7 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.writeLn(); } - private String toPlPgSqlReference(final RbacView.RbacSubjectReference userRef) { + private String toPlPgSqlReference(final RbacSpec.RbacSubjectReference userRef) { return switch (userRef.role) { case CREATOR -> "rbac.currentSubjectUuid()"; default -> throw new IllegalArgumentException("unknown user role: " + userRef); @@ -588,7 +589,7 @@ class RolesGrantsAndPermissionsGenerator { private String toPlPgSqlReference( final PostgresTriggerReference triggerRef, - final RbacView.RbacRoleDefinition roleDef, + final RbacSpec.RbacRoleDefinition roleDef, final boolean assumed) { final var assumedArg = assumed ? "" : ", rbac.unassumed()"; return roleDef.descriptorFunctionName() + @@ -599,7 +600,7 @@ class RolesGrantsAndPermissionsGenerator { private static String toTriggerReference( final PostgresTriggerReference triggerRef, - final RbacView.EntityAlias entityAlias) { + final RbacSpec.EntityAlias entityAlias) { return triggerRef.name().toLowerCase() + capitalize(entityAlias.aliasName()); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerEntity.java index 86fe4d57..0bf4bcc8 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerEntity.java @@ -6,18 +6,18 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import net.hostsharing.hsadminng.persistence.BaseEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import jakarta.persistence.*; import java.io.IOException; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.GLOBAL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.RbacSubjectReference.UserRole.CREATOR; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "rbactest", name = "customer_rv") @@ -41,7 +41,7 @@ public class TestCustomerEntity implements BaseEntity { @Column(name = "adminusername") private String adminUserName; - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("customer", TestCustomerEntity.class) .withIdentityView(SQL.projection("prefix")) .withRestrictedViewOrderBy(SQL.expression("reference")) diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/test/dom/TestDomainEntity.java index 00a78a20..09fab825 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/test/dom/TestDomainEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/test/dom/TestDomainEntity.java @@ -5,21 +5,21 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.persistence.BaseEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.rbac.test.pac.TestPackageEntity; import jakarta.persistence.*; import java.io.IOException; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.directlyFetchedByDependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "rbactest", name = "domain_rv") @@ -44,7 +44,7 @@ public class TestDomainEntity implements BaseEntity { private String description; - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("domain", TestDomainEntity.class) .withIdentityView(SQL.projection("name")) .withUpdatableColumns("version", "packageUuid", "description") diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageEntity.java index 1efadadc..34a20a07 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageEntity.java @@ -5,21 +5,21 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.persistence.BaseEntity; -import net.hostsharing.hsadminng.rbac.generator.RbacView; -import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec; +import net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL; import net.hostsharing.hsadminng.rbac.test.cust.TestCustomerEntity; import jakarta.persistence.*; import java.io.IOException; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*; -import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.ColumnValue.usingDefaultCase; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Nullable.NOT_NULL; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Permission.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.Role.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.SQL.*; +import static net.hostsharing.hsadminng.rbac.generator.RbacSpec.rbacViewFor; @Entity @Table(schema = "rbactest", name = "package_rv") @@ -45,7 +45,7 @@ public class TestPackageEntity implements BaseEntity { private String description; - public static RbacView rbac() { + public static RbacSpec rbac() { return rbacViewFor("package", TestPackageEntity.class) .withIdentityView(SQL.projection("name")) .withUpdatableColumns("version", "customerUuid", "description") diff --git a/src/main/resources/db/changelog/2-rbactest/201-rbactest-customer/2013-rbactest-customer-rbac.sql b/src/main/resources/db/changelog/2-rbactest/201-rbactest-customer/2013-rbactest-customer-rbac.sql index 6451fd34..16faf552 100644 --- a/src/main/resources/db/changelog/2-rbactest/201-rbactest-customer/2013-rbactest-customer-rbac.sql +++ b/src/main/resources/db/changelog/2-rbactest/201-rbactest-customer/2013-rbactest-customer-rbac.sql @@ -178,3 +178,56 @@ call rbac.generateRbacRestrictedView('rbactest.customer', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:rbactest-customer-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table rbactest.customer after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table rbactest.customer', null, <>); +-- call rbactest.customer_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `rbactest.customer.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`rbactest.customer.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure rbactest.customer_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row rbactest.customer; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM rbactest.customer LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL rbactest.customer_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql b/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql index ac5604eb..bfe9386a 100644 --- a/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql +++ b/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer; - assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid); + assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of rbactest.package', NEW.customerUuid); perform rbac.defineRoleWithGrants( @@ -102,10 +102,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.customer WHERE uuid = OLD.customerUuid INTO oldCustomer; - assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s of package', OLD.customerUuid); + assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s of rbactest.package', OLD.customerUuid); SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer; - assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid); + assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of rbactest.package', NEW.customerUuid); if NEW.customerUuid <> OLD.customerUuid then @@ -243,3 +243,56 @@ call rbac.generateRbacRestrictedView('rbactest.package', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:rbactest-package-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table rbactest.package after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table rbactest.package', null, <>); +-- call rbactest.package_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `rbactest.package.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`rbactest.package.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure rbactest.package_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row rbactest.package; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM rbactest.package LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL rbactest.package_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql b/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql index 2fc0d2a5..3ebe2c33 100644 --- a/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql +++ b/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage; - assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid); + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of rbactest.domain', NEW.packageUuid); perform rbac.defineRoleWithGrants( @@ -98,10 +98,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.package WHERE uuid = OLD.packageUuid INTO oldPackage; - assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s of domain', OLD.packageUuid); + assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s of rbactest.domain', OLD.packageUuid); SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage; - assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid); + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of rbactest.domain', NEW.packageUuid); if NEW.packageUuid <> OLD.packageUuid then @@ -242,3 +242,56 @@ call rbac.generateRbacRestrictedView('rbactest.domain', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:rbactest-domain-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table rbactest.domain after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table rbactest.domain', null, <>); +-- call rbactest.domain_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `rbactest.domain.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`rbactest.domain.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure rbactest.domain_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row rbactest.domain; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM rbactest.domain LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL rbactest.domain_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql index 08bdcfc3..6e34a9b5 100644 --- a/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql @@ -102,3 +102,56 @@ call rbac.generateRbacRestrictedView('hs_office.contact', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-contact-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.contact after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.contact', null, <>); +-- call hs_office.contact_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.contact.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.contact.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.contact_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.contact; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.contact LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.contact_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql b/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql index 2f8df513..fa08479c 100644 --- a/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql @@ -104,3 +104,56 @@ call rbac.generateRbacRestrictedView('hs_office.person', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-person-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.person after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.person', null, <>); +-- call hs_office.person_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.person.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.person.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.person_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.person; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.person LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.person_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql index 08a395e0..ef6d9429 100644 --- a/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql @@ -38,13 +38,13 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); 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 of relation', NEW.holderUuid); + assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s of hs_office.relation', NEW.holderUuid); 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 of relation', NEW.anchorUuid); + assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s of hs_office.relation', NEW.anchorUuid); 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 of relation', NEW.contactUuid); + assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s of hs_office.relation', NEW.contactUuid); perform rbac.defineRoleWithGrants( @@ -252,3 +252,56 @@ call rbac.generateRbacRestrictedView('hs_office.relation', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-relation-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.relation after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.relation', null, <>); +-- call hs_office.relation_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.relation.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.relation.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.relation_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.relation; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.relation LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.relation_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql index bfe295fe..fc11610e 100644 --- a/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql @@ -37,10 +37,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of hs_office.partner', NEW.partnerRelUuid); SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; - assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid); + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of hs_office.partner', NEW.detailsUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'DELETE'), hs_office.relation_OWNER(newPartnerRel)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.relation_TENANT(newPartnerRel)); @@ -96,16 +96,16 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel; - assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s of partner', OLD.partnerRelUuid); + assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s of hs_office.partner', OLD.partnerRelUuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of hs_office.partner', NEW.partnerRelUuid); SELECT * FROM hs_office.partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails; - assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s of partner', OLD.detailsUuid); + assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s of hs_office.partner', OLD.detailsUuid); SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; - assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid); + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of hs_office.partner', NEW.detailsUuid); if NEW.partnerRelUuid <> OLD.partnerRelUuid then @@ -253,3 +253,56 @@ call rbac.generateRbacRestrictedView('hs_office.partner', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-partner-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.partner after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.partner', null, <>); +-- call hs_office.partner_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.partner.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.partner.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.partner_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.partner; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.partner LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.partner_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql index eb1f7fd4..01840910 100644 --- a/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql @@ -165,3 +165,56 @@ call rbac.generateRbacRestrictedView('hs_office.partner_details', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-partner-details-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.partner_details after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.partner_details', null, <>); +-- call hs_office.partner_details_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.partner_details.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.partner_details.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.partner_details_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.partner_details; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.partner_details LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.partner_details_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql index e283c13f..cb268a67 100644 --- a/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql @@ -101,3 +101,56 @@ call rbac.generateRbacRestrictedView('hs_office.bankaccount', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-bankaccount-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.bankaccount after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.bankaccount', null, <>); +-- call hs_office.bankaccount_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.bankaccount.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.bankaccount.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.bankaccount_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.bankaccount; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.bankaccount LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.bankaccount_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql index 6a65dd39..1fd6e130 100644 --- a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql @@ -44,10 +44,10 @@ begin WHERE partnerRel.type = 'PARTNER' AND NEW.debitorRelUuid = debitorRel.uuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s of hs_office.debitor', NEW.debitorRelUuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s of hs_office.debitor', NEW.debitorRelUuid); SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount; @@ -242,3 +242,56 @@ call rbac.generateRbacRestrictedView('hs_office.debitor', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-debitor-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.debitor after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.debitor', null, <>); +-- call hs_office.debitor_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.debitor.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.debitor.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.debitor_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.debitor; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.debitor LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.debitor_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql index f22a826b..cba350a1 100644 --- a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql @@ -37,14 +37,14 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount; - assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s of sepamandate', NEW.bankAccountUuid); + assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s of hs_office.sepamandate', NEW.bankAccountUuid); SELECT debitorRel.* FROM hs_office.relation debitorRel JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = NEW.debitorUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of sepamandate', NEW.debitorUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of hs_office.sepamandate', NEW.debitorUuid); perform rbac.defineRoleWithGrants( @@ -211,3 +211,56 @@ call rbac.generateRbacRestrictedView('hs_office.sepamandate', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-sepamandate-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.sepamandate after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.sepamandate', null, <>); +-- call hs_office.sepamandate_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.sepamandate.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.sepamandate.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.sepamandate_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.sepamandate; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.sepamandate LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.sepamandate_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql index 306dbced..ac82eab3 100644 --- a/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql @@ -40,7 +40,7 @@ begin JOIN hs_office.relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid WHERE partner.uuid = NEW.partnerUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s of membership', NEW.partnerUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s of hs_office.membership', NEW.partnerUuid); perform rbac.defineRoleWithGrants( @@ -193,3 +193,56 @@ call rbac.generateRbacRestrictedView('hs_office.membership', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-membership-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.membership after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.membership', null, <>); +-- call hs_office.membership_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.membership.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.membership.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.membership_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.membership; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.membership LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.membership_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql index e7cc8811..5cf1eed6 100644 --- a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); 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 of coopshares', NEW.membershipUuid); + assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of hs_office.coopsharetx', NEW.membershipUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership)); @@ -164,3 +164,56 @@ call rbac.generateRbacRestrictedView('hs_office.coopsharetx', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-coopsharetx-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.coopsharetx after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.coopsharetx', null, <>); +-- call hs_office.coopsharetx_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.coopsharetx.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.coopsharetx.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.coopsharetx_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.coopsharetx; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.coopsharetx LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.coopsharetx_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql index f5647823..b4b08467 100644 --- a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); 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 of coopasset', NEW.membershipUuid); + assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of hs_office.coopassettx', NEW.membershipUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership)); @@ -164,3 +164,56 @@ call rbac.generateRbacRestrictedView('hs_office.coopassettx', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-office-coopassettx-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_office.coopassettx after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_office.coopassettx', null, <>); +-- call hs_office.coopassettx_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_office.coopassettx.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_office.coopassettx.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_office.coopassettx_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_office.coopassettx; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_office.coopassettx LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_office.coopassettx_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql b/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql index ade16515..207bb740 100644 --- a/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql +++ b/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql @@ -37,14 +37,14 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.debitor WHERE uuid = NEW.debitorUuid INTO newDebitor; - assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s of project', NEW.debitorUuid); + assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s of hs_booking.project', NEW.debitorUuid); SELECT debitorRel.* FROM hs_office.relation debitorRel JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = NEW.debitorUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s or project', NEW.debitorUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of hs_booking.project', NEW.debitorUuid); perform rbac.defineRoleWithGrants( @@ -204,3 +204,56 @@ call rbac.generateRbacRestrictedView('hs_booking.project', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-booking-project-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_booking.project after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_booking.project', null, <>); +-- call hs_booking.project_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_booking.project.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_booking.project.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_booking.project_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_booking.project; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_booking.project LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_booking.project_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/6-hs-booking/630-booking-item/6303-hs-booking-item-rbac.sql b/src/main/resources/db/changelog/6-hs-booking/630-booking-item/6303-hs-booking-item-rbac.sql index 67173247..8010599c 100644 --- a/src/main/resources/db/changelog/6-hs-booking/630-booking-item/6303-hs-booking-item-rbac.sql +++ b/src/main/resources/db/changelog/6-hs-booking/630-booking-item/6303-hs-booking-item-rbac.sql @@ -275,3 +275,56 @@ call rbac.generateRbacRestrictedView('hs_booking.item', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-booking-item-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_booking.item after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_booking.item', null, <>); +-- call hs_booking.item_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_booking.item.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_booking.item.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_booking.item_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_booking.item; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_booking.item LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_booking.item_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql index 4e2137af..358fe7c5 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql @@ -181,3 +181,56 @@ call rbac.generateRbacRestrictedView('hs_hosting.asset', $updates$); --// + +-- ============================================================================ +--changeset RbacRbacSystemRebuildGenerator:hs-hosting-asset-rbac-rebuild endDelimiter:--// +-- ---------------------------------------------------------------------------- + +-- HOWTO: Rebuild RBAC-system for table hs_hosting.asset after changing its RBAC specification. +-- +-- begin transaction; +-- call base.defineContext('re-creating RBAC for table hs_hosting.asset', null, <>); +-- call hs_hosting.asset_rebuild_rbac_system(); +-- commit; +-- +-- How it works: +-- 1. All grants previously created from the RBAC specification of this table will be deleted. +-- These grants are identified by `hs_hosting.asset.grantedByTriggerOf IS NOT NULL`. +-- User-induced grants (`hs_hosting.asset.grantedByTriggerOf IS NULL`) are NOT deleted. +-- 2. New role types will be created, but existing role types which are not specified anymore, +-- will NOT be deleted! +-- 3. All newly specified grants will be created. +-- +-- IMPORTANT: +-- Make sure not to skip any previously defined role-types or you might break indirect grants! +-- E.g. If, in an updated version of the RBAC system for a table, you remove the AGENT role type +-- and now directly grant the TENANT role to the ADMIN role, all external grants to the AGENT role +-- of this table would be in a dead end. + +create or replace procedure hs_hosting.asset_rebuild_rbac_system() + language plpgsql as $$ +DECLARE + DECLARE + row hs_hosting.asset; + grantsAfter numeric; + grantsBefore numeric; +BEGIN + SELECT count(*) INTO grantsBefore FROM rbac.grants; + + FOR row IN SELECT * FROM hs_hosting.asset LOOP + -- first delete all generated grants for this row from the previously defined RBAC system + DELETE FROM rbac.grants g + WHERE g.grantedbytriggerof = row.uuid; + + -- then build the grants according to the currently defined RBAC rules + CALL hs_hosting.asset_build_rbac_system(row); + END LOOP; + + select count(*) into grantsAfter from rbac.grants; + + -- print how the total count of grants has changed + raise notice 'total grant count before -> after: % -> %', grantsBefore, grantsAfter; +END; +$$; +--// + diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantRepositoryIntegrationTest.java index f4df3c6d..05b3bc73 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantRepositoryIntegrationTest.java @@ -290,6 +290,32 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest { } } + @Nested + class RebuildRbacSystem { + + @Test + public void rebuildingTheRbacSystemWitSameRbacSpecDoesNotChangeGrantNorRoleCount() { + final var grantCountBefore = sql("SELECT COUNT(*) FROM rbac.grants"); + final var roleCountBefore = sql("SELECT COUNT(*) FROM rbac.role"); + + jpaAttempt.transacted(() -> + em.createNativeQuery("CALL rbactest.package_rebuild_rbac_system()") + ); + + final var grantCountAfter = sql("SELECT COUNT(*) FROM rbac.grants"); + assertThat(grantCountBefore).as("grant count must not change").isEqualTo(grantCountAfter); + + final var roleCountAfter = sql("SELECT COUNT(*) FROM rbac.role"); + assertThat(roleCountBefore).as("role count must not change").isEqualTo(roleCountAfter); + } + + private Object sql(final String query) { + return jpaAttempt.transacted(() -> + em.createNativeQuery(query).getSingleResult() + ).assumeSuccessful().returnedValue(); + } + } + private RbacSubjectEntity createNewUserTransacted() { return jpaAttempt.transacted(() -> { final var newUserName = "test-user-" + System.currentTimeMillis() + "@example.com";