1
0

add @Version property to all RBAC-Entities (#34)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/34
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-04-08 11:21:22 +02:00
parent 44ff30c54a
commit ec1deb8903
29 changed files with 65 additions and 38 deletions
+2 -1
View File
@@ -6,7 +6,8 @@ Permissions, Rollen und Grants werden in den INSERT/UPDATE/DELETE-Triggern von G
Das folgende Schema soll dabei unterstützen, die richtigen Permissions, Rollen und Grants festzulegen.
An einigen Stellen ist vom *Initiator* die Rede. Als *Initiator* gilt derjenige User, der die Operation (INSERT oder UPDATE) durchführt bzw. dessen primary assumed Rol. (TODO: bisher gibt es nur assumed roles, das Konzept einer primary assumed Role müsste noch eingeführt werden, derzeit nehmen wir dafür immer den `globalAdmin()`. Bevor Kunden aber selbst Objekte anlegen können, muss das geklärt sein.)
An einigen Stellen ist vom *Initiator* die Rede. Als *Initiator* gilt derjenige User, der die Operation (INSERT oder UPDATE) durchführt bzw. eine explizit anzugebende Rolle des Users.
Wird keine solche explizite Rolle angegeben, gilt die granted Rolle als diejenige, als der das Grant erfolgt.
#### Typ Root: Objekte, welche nur eine Spezialisierung bzw. Zusatzdaten für andere Objekte bereitstellen (z.B. Partner für Relations vom Typ Partner oder Partner Details für Partner)
@@ -8,10 +8,7 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import java.io.IOException;
import java.util.UUID;
@@ -41,6 +38,9 @@ public class HsOfficeBankAccountEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
@Version
private int version;
private String holder;
private String iban;
@@ -40,6 +40,11 @@ public class HsOfficeContactEntity implements Stringifyable, RbacObject {
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;
@Version
private int version;
@Column(name = "label")
private String label;
@Column(name = "postaladdress")
@@ -14,15 +14,7 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -65,6 +57,9 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinColumn(name = "membershipuuid")
private HsOfficeMembershipEntity membership;
@@ -13,15 +13,7 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
@@ -61,6 +53,9 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
@GeneratedValue
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinColumn(name = "membershipuuid")
private HsOfficeMembershipEntity membership;
@@ -71,6 +71,9 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinFormula(
referencedColumnName = "uuid",
@@ -57,6 +57,9 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner;
@@ -43,6 +43,7 @@ public class HsOfficePartnerDetailsEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
private @Version int version;
private @Column(name = "registrationoffice") String registrationOffice;
private @Column(name = "registrationnumber") String registrationNumber;
private @Column(name = "birthname") String birthName;
@@ -17,13 +17,7 @@ import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import java.io.IOException;
import java.util.UUID;
@@ -66,6 +60,9 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber;
@@ -44,6 +44,9 @@ public class HsOfficePersonEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@Column(name = "persontype")
private HsOfficePersonType personType;
@@ -52,6 +52,9 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinColumn(name = "anchoruuid")
private HsOfficePersonEntity anchor;
@@ -50,6 +50,9 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@ManyToOne
@JoinColumn(name = "debitoruuid")
private HsOfficeDebitorEntity debitor;
@@ -426,8 +426,7 @@ public class RbacView {
private void verifyVersionColumnExists() {
if (stream(rootEntityAlias.entityClass.getDeclaredFields())
.noneMatch(f -> f.getAnnotation(Version.class) != null)) {
// TODO: convert this into throw Exception once RbacEntity is a base class with @Version field
System.err.println("@Version field required in updatable entity " + rootEntityAlias.entityClass);
throw new IllegalArgumentException("@Version field required in updatable entity " + rootEntityAlias.entityClass);
}
}
@@ -5,4 +5,6 @@ import java.util.UUID;
public interface RbacObject {
UUID getUuid();
int getVersion();
}
@@ -30,6 +30,9 @@ public class TestCustomerEntity implements RbacObject {
@GeneratedValue
private UUID uuid;
@Version
private int version;
private String prefix;
private int reference;
@@ -7,6 +7,7 @@
create table if not exists test_customer
(
uuid uuid unique references RbacObject (uuid),
version int not null default 0,
reference int not null unique check (reference between 10000 and 99999),
prefix character(3) unique,
adminUserName varchar(63)
@@ -7,6 +7,7 @@
create table if not exists hs_office_contact
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
label varchar(128) not null,
postalAddress text,
emailAddresses text, -- TODO.feat: change to json
@@ -17,6 +17,7 @@ CREATE CAST (character varying as HsOfficePersonType) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_person
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
personType HsOfficePersonType not null,
tradeName varchar(96),
salutation varchar(30),
@@ -19,6 +19,7 @@ CREATE CAST (character varying as HsOfficeRelationType) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_relation
(
uuid uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade
version int not null default 0,
anchorUuid uuid not null references hs_office_person(uuid),
holderUuid uuid not null references hs_office_person(uuid),
contactUuid uuid references hs_office_contact(uuid),
@@ -8,6 +8,7 @@
create table hs_office_partner_details
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
registrationOffice varchar(96),
registrationNumber varchar(96),
birthPlace varchar(96),
@@ -32,6 +33,7 @@ call create_journal('hs_office_partner_details');
create table hs_office_partner
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
partnerNumber numeric(5) unique not null,
partnerRelUuid uuid not null references hs_office_relation(uuid), -- deleted in after delete trigger
detailsUuid uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger
@@ -6,6 +6,7 @@
create table hs_office_bankaccount
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
holder varchar(64) not null,
iban varchar(34) not null,
bic varchar(11) not null
@@ -7,6 +7,7 @@
create table hs_office_debitor
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
debitorNumberSuffix char(2) not null check (debitorNumberSuffix::text ~ '^[0-9][0-9]$'),
debitorRelUuid uuid not null references hs_office_relation(uuid),
billable boolean not null default true,
@@ -7,6 +7,7 @@
create table if not exists hs_office_sepamandate
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
debitorUuid uuid not null references hs_office_debitor(uuid),
bankAccountUuid uuid not null references hs_office_bankaccount(uuid),
reference varchar(96) not null,
@@ -11,6 +11,7 @@ CREATE CAST (character varying as HsOfficeReasonForTermination) WITH INOUT AS IM
create table if not exists hs_office_membership
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
partnerUuid uuid not null references hs_office_partner(uuid),
memberNumberSuffix char(2) not null check (memberNumberSuffix::text ~ '^[0-9][0-9]$'),
validity daterange not null,
@@ -11,6 +11,7 @@ CREATE CAST (character varying as HsOfficeCoopSharesTransactionType) WITH INOUT
create table if not exists hs_office_coopsharestransaction
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
membershipUuid uuid not null references hs_office_membership(uuid),
transactionType HsOfficeCoopSharesTransactionType not null,
valueDate date not null,
@@ -18,6 +18,7 @@ CREATE CAST (character varying as HsOfficeCoopAssetsTransactionType) WITH INOUT
create table if not exists hs_office_coopassetstransaction
(
uuid uuid unique references RbacObject (uuid) initially deferred,
version int not null default 0,
membershipUuid uuid not null references hs_office_membership(uuid),
transactionType HsOfficeCoopAssetsTransactionType not null,
valueDate date not null,
@@ -295,7 +295,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
private void assertThatRelationActuallyInDatabase(final HsOfficeRelationEntity saved) {
final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved);
assertThat(found).isNotEmpty().get()
.isNotSameAs(saved)
.usingRecursiveComparison().ignoringFields("version").isEqualTo(saved);
}
private void assertThatRelationIsVisibleForUserWithRole(
@@ -7,6 +7,6 @@ public class TestCustomer {
static final TestCustomerEntity yyy = hsCustomer("yyy", 10002, "yyy@example.com");
static public TestCustomerEntity hsCustomer(final String prefix, final int reference, final String adminName) {
return new TestCustomerEntity(null, prefix, reference, adminName);
return new TestCustomerEntity(null, 0, prefix, reference, adminName);
}
}
@@ -40,7 +40,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// when
final var result = attempt(em, () -> {
final var newCustomer = new TestCustomerEntity(
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
UUID.randomUUID(), 0, "www", 90001, "customer-admin@www.example.com");
return testCustomerRepository.save(newCustomer);
});
@@ -59,7 +59,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// when
final var result = attempt(em, () -> {
final var newCustomer = new TestCustomerEntity(
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
UUID.randomUUID(), 0, "www", 90001, "customer-admin@www.example.com");
return testCustomerRepository.save(newCustomer);
});
@@ -77,7 +77,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// when
final var result = attempt(em, () -> {
final var newCustomer = new TestCustomerEntity(
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
UUID.randomUUID(), 0, "www", 90001, "customer-admin@www.example.com");
return testCustomerRepository.save(newCustomer);
});