RBAC Diagram+PostgreSQL Generator #21
@ -16,6 +16,7 @@ import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
|
||||
import net.hostsharing.hsadminng.test.dom.TestDomainEntity;
|
||||
import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
|
||||
|
||||
import jakarta.persistence.Table;
|
||||
@ -596,6 +597,9 @@ public class RbacView {
|
||||
}
|
||||
|
||||
String getRawTableName() {
|
||||
if ( aliasName.equals("global")) {
|
||||
return "global"; // TODO: maybe we should introduce a GlobalEntity class?
|
||||
}
|
||||
return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||
}
|
||||
|
||||
@ -784,6 +788,7 @@ public class RbacView {
|
||||
Stream.of(
|
||||
TestCustomerEntity.class,
|
||||
TestPackageEntity.class,
|
||||
TestDomainEntity.class,
|
||||
HsOfficePersonEntity.class,
|
||||
HsOfficePartnerEntity.class,
|
||||
HsOfficePartnerDetailsEntity.class,
|
||||
|
@ -41,6 +41,8 @@ public class TestCustomerEntity implements HasUuid {
|
||||
.withIdentityView(SQL.projection("prefix"))
|
||||
.withRestrictedViewOrderBy(SQL.expression("reference"))
|
||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||
// TODO: do we want explicit specification of parent-indenpendent insert permissions?
|
||||
// .toRole("global", ADMIN).grantPermission("customer", INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR).unassumed();
|
||||
|
@ -0,0 +1,73 @@
|
||||
package net.hostsharing.hsadminng.test.dom;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "test_domain_rv")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TestDomainEntity implements HasUuid {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID uuid;
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "packageuuid")
|
||||
private TestPackageEntity pac;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("domain", TestDomainEntity.class)
|
||||
.withIdentityView(SQL.projection("name"))
|
||||
.withUpdatableColumns("version", "packageUuid", "description")
|
||||
|
||||
.importEntityAlias("package", TestPackageEntity.class,
|
||||
dependsOnColumn("packageUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= ${ref}.packageUuid
|
||||
"""))
|
||||
.toRole("package", ADMIN).grantPermission("domain", INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.incomingSuperRole("package", ADMIN);
|
||||
with.outgoingSubRole("package", TENANT);
|
||||
with.permission(DELETE);
|
||||
with.permission(UPDATE);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.outgoingSubRole("package", TENANT);
|
||||
with.permission(SELECT);
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("133-test-domain-rbac");
|
||||
}
|
||||
}
|
@ -337,10 +337,8 @@ grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RES
|
||||
/*
|
||||
Returns all permissions granted to the given user,
|
||||
which are also visible to the current user or assumed roles.
|
||||
|
||||
|
||||
*/
|
||||
create or replace function grantedPermissions(targetUserUuid uuid)
|
||||
*/
|
||||
create or replace function grantedPermissionsRaw(targetUserUuid uuid)
|
||||
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid uuid)
|
||||
returns null on null input
|
||||
language plpgsql as $$
|
||||
@ -375,4 +373,15 @@ begin
|
||||
) xp;
|
||||
-- @formatter:on
|
||||
end; $$;
|
||||
|
||||
create or replace function grantedPermissions(targetUserUuid uuid)
|
||||
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid uuid)
|
||||
returns null on null input
|
||||
language sql as $$
|
||||
select * from grantedPermissionsRaw(targetUserUuid)
|
||||
union all
|
||||
select roleUuid, roleName, permissionUuid, 'SELECT'::RbacOp, opTableName, objectTable, objectIdName, objectUuid
|
||||
from grantedPermissionsRaw(targetUserUuid)
|
||||
where op <> 'SELECT'::RbacOp;
|
||||
$$;
|
||||
--//
|
||||
|
@ -1,4 +1,4 @@
|
||||
### rbac customer 2024-03-08T13:03:39.397294085
|
||||
### rbac customer 2024-03-09T08:56:16.396142507
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
|
@ -1,5 +1,5 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-08T13:03:39.428165899.
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-09T08:56:16.421821997.
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||
|
@ -1,4 +1,4 @@
|
||||
### rbac package 2024-03-08T13:03:39.472333368
|
||||
### rbac package 2024-03-09T08:56:16.449886471
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
|
@ -1,5 +1,5 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-08T13:03:39.473061981.
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-09T08:56:16.450322125.
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||
|
@ -1,4 +1,5 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-09T08:56:16.469632602.
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -11,91 +12,200 @@ call generateRelatedRbacObject('test_domain');
|
||||
--changeset test-domain-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('testDomain', 'test_domain');
|
||||
|
||||
create or replace function createTestDomainTenantRoleIfNotExists(domain test_domain)
|
||||
returns uuid
|
||||
returns null on null input
|
||||
language plpgsql as $$
|
||||
declare
|
||||
domainTenantRoleDesc RbacRoleDescriptor;
|
||||
domainTenantRoleUuid uuid;
|
||||
begin
|
||||
domainTenantRoleDesc = testdomainTenant(domain);
|
||||
domainTenantRoleUuid = findRoleId(domainTenantRoleDesc);
|
||||
if domainTenantRoleUuid is not null then
|
||||
return domainTenantRoleUuid;
|
||||
end if;
|
||||
|
||||
return createRoleWithGrants(
|
||||
domainTenantRoleDesc,
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[testdomainAdmin(domain)]
|
||||
);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset test-domain-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles and their assignments for a new domain for the AFTER INSERT TRIGGER.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
|
||||
create or replace function createRbacRulesForTestDomain()
|
||||
create or replace procedure buildRbacSystemForTestDomain(
|
||||
NEW test_domain
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
newPackage test_package;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= NEW.packageUuid
|
||||
into newPackage;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
testDomainOwner(NEW),
|
||||
permissions => array['DELETE', 'UPDATE'],
|
||||
incomingSuperRoles => array[testPackageAdmin(newPackage)],
|
||||
outgoingSubRoles => array[testPackageTenant(newPackage)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
testDomainAdmin(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[testDomainOwner(NEW)],
|
||||
outgoingSubRoles => array[testPackageTenant(newPackage)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new test_domain row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForTestDomain_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
parentPackage test_package;
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
select * from test_package where uuid = NEW.packageUuid into parentPackage;
|
||||
|
||||
-- an owner role is created and assigned to the package's admin group
|
||||
perform createRoleWithGrants(
|
||||
testDomainOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[testPackageAdmin(parentPackage)]
|
||||
);
|
||||
|
||||
-- and a domain admin role is created and assigned to the domain owner as well
|
||||
perform createRoleWithGrants(
|
||||
testDomainAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[testDomainOwner(NEW)],
|
||||
outgoingSubRoles => array[testPackageTenant(parentPackage)]
|
||||
);
|
||||
|
||||
-- a tenent role is only created on demand
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
call buildRbacSystemForTestDomain(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new domain.
|
||||
*/
|
||||
drop trigger if exists createRbacRulesForTestDomain_Trigger on test_domain;
|
||||
create trigger createRbacRulesForTestDomain_Trigger
|
||||
after insert
|
||||
on test_domain
|
||||
create trigger insertTriggerForTestDomain_tg
|
||||
after insert on test_domain
|
||||
for each row
|
||||
execute procedure createRbacRulesForTestDomain();
|
||||
execute procedure insertTriggerForTestDomain_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||
*/
|
||||
|
||||
create or replace procedure updateRbacRulesForTestDomain(
|
||||
OLD test_domain,
|
||||
NEW test_domain
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
oldPackage test_package;
|
||||
newPackage test_package;
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= OLD.packageUuid
|
||||
into oldPackage;
|
||||
SELECT * FROM test_package p
|
||||
WHERE p.uuid= NEW.packageUuid
|
||||
into newPackage;
|
||||
|
||||
if NEW.packageUuid <> OLD.packageUuid then
|
||||
|
||||
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
|
||||
|
||||
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
|
||||
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
|
||||
|
||||
call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainOwner(OLD));
|
||||
call grantRoleToRole(testPackageTenant(newPackage), testDomainOwner(NEW));
|
||||
|
||||
call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainAdmin(OLD));
|
||||
call grantRoleToRole(testPackageTenant(newPackage), testDomainAdmin(NEW));
|
||||
|
||||
end if;
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to re-wire the grant structure for a new test_domain row.
|
||||
*/
|
||||
|
||||
create or replace function updateTriggerForTestDomain_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call updateRbacRulesForTestDomain(OLD, NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger updateTriggerForTestDomain_tg
|
||||
after update on test_domain
|
||||
for each row
|
||||
execute procedure updateTriggerForTestDomain_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO test_domain permissions for the related test_package rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row test_package;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO test_domain permissions for the related test_package rows');
|
||||
|
||||
FOR row IN SELECT * FROM test_package
|
||||
LOOP
|
||||
roleUuid := findRoleId(testPackageAdmin(row));
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds test_domain INSERT permission to specified role of new test_package rows.
|
||||
*/
|
||||
create or replace function test_domain_test_package_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
testPackageAdmin(NEW),
|
||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger test_domain_test_package_insert_tg
|
||||
after insert on test_package
|
||||
for each row
|
||||
execute procedure test_domain_test_package_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to test_domain.
|
||||
*/
|
||||
create or replace function test_domain_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into test_domain not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
create trigger test_domain_insert_permission_check_tg
|
||||
before insert on test_domain
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
|
||||
execute procedure test_domain_insert_permission_missing_tf();
|
||||
|
||||
--//
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityView('test_domain', $idName$
|
||||
target.name
|
||||
name
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
@ -103,15 +213,13 @@ call generateRbacIdentityView('test_domain', $idName$
|
||||
-- ============================================================================
|
||||
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates a view to the customer main table which maps the identifying name
|
||||
(in this case, the prefix) to the objectUuid.
|
||||
*/
|
||||
drop view if exists test_domain_rv;
|
||||
create or replace view test_domain_rv as
|
||||
select target.*
|
||||
from test_domain as target
|
||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('SELECT', 'domain', currentSubjectsUuids()));
|
||||
grant all privileges on test_domain_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
||||
call generateRbacRestrictedView('test_domain',
|
||||
'name',
|
||||
$updates$
|
||||
version = new.version,
|
||||
packageUuid = new.packageUuid,
|
||||
description = new.description
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
||||
|
@ -42,151 +42,3 @@ subgraph hsOfficeRelationship
|
||||
end
|
||||
```
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- the owner role with full access for admins of the relAnchor global admins
|
||||
ownerRole = createRole(
|
||||
hsOfficeRelationshipOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRoles(array[
|
||||
globalAdmin(),
|
||||
hsOfficePersonAdmin(newRelAnchor)])
|
||||
);
|
||||
|
||||
-- the admin role with full access for the owner
|
||||
adminRole = createRole(
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
hsOfficeRelationshipTenant,
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRoles(array[
|
||||
hsOfficePersonAdmin(newRelAnchor),
|
||||
hsOfficePersonAdmin(newRelHolder),
|
||||
hsOfficeContactAdmin(newContact)]),
|
||||
withSubRoles(array[
|
||||
hsOfficePersonTenant(newRelAnchor),
|
||||
hsOfficePersonTenant(newRelHolder),
|
||||
hsOfficeContactTenant(newContact)])
|
||||
);
|
||||
|
||||
-- anchor and holder admin roles need each others tenant role
|
||||
-- to be able to see the joined relationship
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
|
||||
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
-- nothing but the contact can be updated,
|
||||
-- in other cases, a new relationship needs to be created and the old updated
|
||||
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
|
||||
|
||||
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
|
||||
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeRelationship_Trigger
|
||||
after insert
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
execute procedure hsOfficeRelationshipRbacRolesTrigger();
|
||||
|
||||
/*
|
||||
An AFTER UPDATE TRIGGER which updates the role structure of a customer.
|
||||
*/
|
||||
create trigger updateRbacRolesForHsOfficeRelationship_Trigger
|
||||
after update
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
execute procedure hsOfficeRelationshipRbacRolesTrigger();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityView('hs_office_relationship', $idName$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
||||
|| '-with-' || target.relType || '-' ||
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relationship',
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
-- TODO: exception if one tries to amend any other column
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-relationship and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-relationship permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-relationship not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_relationship_insert_trigger
|
||||
before insert
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new relationships
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -28,6 +28,7 @@ public class ArchitectureTest {
|
||||
"..test",
|
||||
"..test.cust",
|
||||
"..test.pac",
|
||||
"..test.dom",
|
||||
"..context",
|
||||
"..generated..",
|
||||
"..persistence..",
|
||||
|
@ -62,6 +62,7 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
|
||||
|
||||
role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
|
||||
role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
|
||||
role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
|
||||
role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
|
||||
""".trim());
|
||||
}
|
||||
@ -75,10 +76,12 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
|
||||
flowchart TB
|
||||
|
||||
role:test_customer#xxx.tenant --> perm:SELECT:on:test_customer#xxx
|
||||
role:test_domain#xxx00-aaaa.admin --> perm:UPDATE:on:test_domain#xxx00-aaaa
|
||||
role:test_domain#xxx00-aaaa.admin --> perm:SELECT:on:test_domain#xxx00-aaaa
|
||||
role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
|
||||
role:test_domain#xxx00-aaaa.owner --> perm:DELETE:on:test_domain#xxx00-aaaa
|
||||
role:test_domain#xxx00-aaaa.owner --> perm:UPDATE:on:test_domain#xxx00-aaaa
|
||||
role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
|
||||
role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
|
||||
role:test_package#xxx00.tenant --> perm:SELECT:on:test_package#xxx00
|
||||
role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
|
||||
""".trim());
|
||||
|
@ -295,7 +295,8 @@ class RbacUserControllerAcceptanceTest {
|
||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "DELETE"))
|
||||
))
|
||||
.body("size()", is(6));
|
||||
// actual content tested in integration test, so this is enough for here:
|
||||
.body("size()", greaterThanOrEqualTo(6));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@ -325,7 +326,8 @@ class RbacUserControllerAcceptanceTest {
|
||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "DELETE"))
|
||||
))
|
||||
.body("size()", is(6));
|
||||
// actual content tested in integration test, so this is enough for here:
|
||||
.body("size()", greaterThanOrEqualTo(6));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@ -354,7 +356,8 @@ class RbacUserControllerAcceptanceTest {
|
||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||
hasEntry("op", "DELETE"))
|
||||
))
|
||||
.body("size()", is(6));
|
||||
// actual content tested in integration test, so this is enough for here:
|
||||
.body("size()", greaterThanOrEqualTo(6));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static net.hostsharing.test.JpaAttempt.attempt;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ -181,50 +182,48 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
|
||||
private static final String[] ALL_USER_PERMISSIONS = Array.of(
|
||||
// @formatter:off
|
||||
"global#global.admin -> global#global: add-customer",
|
||||
"test_customer#xxx.admin -> test_customer#xxx: SELECT",
|
||||
"test_customer#xxx.owner -> test_customer#xxx: DELETE",
|
||||
"test_customer#xxx.tenant -> test_customer#xxx: SELECT",
|
||||
"test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
|
||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
||||
"test_package#xxx00.tenant -> test_package#xxx00: SELECT",
|
||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
||||
"test_package#xxx01.tenant -> test_package#xxx01: SELECT",
|
||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
||||
"test_package#xxx02.tenant -> test_package#xxx02: SELECT",
|
||||
|
||||
"test_customer#xxx.admin -> test_customer#xxx: SELECT",
|
||||
"test_customer#xxx.owner -> test_customer#xxx: DELETE",
|
||||
"test_customer#xxx.tenant -> test_customer#xxx: SELECT",
|
||||
"test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
|
||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
||||
"test_package#xxx00.tenant -> test_package#xxx00: SELECT",
|
||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
||||
"test_package#xxx01.tenant -> test_package#xxx01: SELECT",
|
||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
||||
"test_package#xxx02.tenant -> test_package#xxx02: SELECT",
|
||||
"test_customer#yyy.admin -> test_customer#yyy: SELECT",
|
||||
"test_customer#yyy.owner -> test_customer#yyy: DELETE",
|
||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT",
|
||||
"test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
|
||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
||||
"test_package#yyy00.tenant -> test_package#yyy00: SELECT",
|
||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
||||
"test_package#yyy01.tenant -> test_package#yyy01: SELECT",
|
||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
||||
"test_package#yyy02.tenant -> test_package#yyy02: SELECT",
|
||||
|
||||
"test_customer#yyy.admin -> test_customer#yyy: SELECT",
|
||||
"test_customer#yyy.owner -> test_customer#yyy: DELETE",
|
||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT",
|
||||
"test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
|
||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
||||
"test_package#yyy00.tenant -> test_package#yyy00: SELECT",
|
||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
||||
"test_package#yyy01.tenant -> test_package#yyy01: SELECT",
|
||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
||||
"test_package#yyy02.tenant -> test_package#yyy02: SELECT",
|
||||
|
||||
"test_customer#zzz.admin -> test_customer#zzz: SELECT",
|
||||
"test_customer#zzz.owner -> test_customer#zzz: DELETE",
|
||||
"test_customer#zzz.tenant -> test_customer#zzz: SELECT",
|
||||
"test_customer#zzz.admin -> test_customer#zzz: INSERT:test_package",
|
||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
||||
"test_package#zzz00.tenant -> test_package#zzz00: SELECT",
|
||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
||||
"test_package#zzz01.tenant -> test_package#zzz01: SELECT",
|
||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
||||
"test_package#zzz02.tenant -> test_package#zzz02: SELECT"
|
||||
// @formatter:on
|
||||
"test_customer#zzz.admin -> test_customer#zzz: SELECT",
|
||||
"test_customer#zzz.owner -> test_customer#zzz: DELETE",
|
||||
"test_customer#zzz.tenant -> test_customer#zzz: SELECT",
|
||||
"test_customer#zzz.admin -> test_customer#zzz: INSERT:test_package",
|
||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
||||
"test_package#zzz00.tenant -> test_package#zzz00: SELECT",
|
||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
||||
"test_package#zzz01.tenant -> test_package#zzz01: SELECT",
|
||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
||||
"test_package#zzz02.tenant -> test_package#zzz02: SELECT"
|
||||
// @formatter:on
|
||||
);
|
||||
|
||||
@Test
|
||||
@ -233,7 +232,9 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
||||
context("superuser-alex@hostsharing.net");
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-alex@hostsharing.net"));
|
||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-fran@hostsharing.net"))
|
||||
.stream().filter(p -> p.getObjectTable().contains("test_"))
|
||||
.sorted(comparing(RbacUserPermission::toString)).toList();
|
||||
|
||||
// then
|
||||
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
|
||||
|
Loading…
Reference in New Issue
Block a user