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

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #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

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)

View File

@ -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;

View File

@ -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")

View File

@ -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;

View File

@ -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;

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -44,6 +44,9 @@ public class HsOfficePersonEntity implements RbacObject, Stringifyable {
@GeneratedValue
private UUID uuid;
@Version
private int version;
@Column(name = "persontype")
private HsOfficePersonType personType;

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -5,4 +5,6 @@ import java.util.UUID;
public interface RbacObject {
UUID getUuid();
int getVersion();
}

View File

@ -30,6 +30,9 @@ public class TestCustomerEntity implements RbacObject {
@GeneratedValue
private UUID uuid;
@Version
private int version;
private String prefix;
private int reference;

View File

@ -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)

View File

@ -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

View File

@ -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),

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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(

View File

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

View File

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