Compare commits

..

5 Commits

Author SHA1 Message Date
Michael Hoennig
8f8ca75fae aftermaths from merging master 2024-04-02 12:40:07 +02:00
Michael Hoennig
b209b53ca7 Merge remote-tracking branch 'origin/master' into cleanup-todos
# Conflicts:
#	README.md
#	doc/rbac.md
#	src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
#	src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
#	src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
#	src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
#	src/main/resources/db/changelog/1-rbac/1050-rbac-base.sql
#	src/main/resources/db/changelog/1-rbac/1058-rbac-generators.sql
#	src/main/resources/db/changelog/1-rbac/1080-rbac-global.sql
#	src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.md
#	src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql
#	src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.md
#	src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql
#	src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.md
#	src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql
#	src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
#	src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
#	src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
2024-04-02 12:39:23 +02:00
87af20a3a1 structured-liquibase-files (#29)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #29
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 12:29:31 +02:00
7f418c12a1 uniform idnames (#28)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #28
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 12:01:37 +02:00
f8fb273918 generated RBAC for coopshares and -assets (#27)
Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #27
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-04-02 11:04:56 +02:00
25 changed files with 203 additions and 108 deletions

View File

@ -82,7 +82,7 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
curl \
-H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:admin' \
-H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
http://localhost:8080/api/test/packages
# add a new customer

View File

@ -206,7 +206,7 @@ and the *role-stereotype* describes a role relative to a referenced business-obj
#### owner
The owner-role is granted to the subject which created the business object.
E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...:admin'.
E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...:ADMIN'.
Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it.
@ -470,14 +470,14 @@ together {
permCustomerXyzSELECT--> boCustXyz
}
entity "Role customer#xyz:tenant" as roleCustXyzTenant
entity "Role customer#xyz:TENANT" as roleCustXyzTenant
roleCustXyzTenant --> permCustomerXyzSELECT
entity "Role customer#xyz:admin" as roleCustXyzAdmin
entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
roleCustXyzAdmin --> roleCustXyzTenant
roleCustXyzAdmin --> permCustomerXyzINSERT:package
entity "Role customer#xyz:owner" as roleCustXyzOwner
entity "Role customer#xyz:OWNER" as roleCustXyzOwner
roleCustXyzOwner ..> roleCustXyzAdmin
roleCustXyzOwner --> permCustomerXyzDELETE
@ -493,7 +493,7 @@ actorHostmaster --> roleAdmins
```
As you can see, there something special:
From the 'Role customer#xyz:owner' to the 'Role customer#xyz:admin' there is a dashed line, whereas all other lines are solid lines.
From the 'Role customer#xyz:OWNER' to the 'Role customer#xyz:admin' there is a dashed line, whereas all other lines are solid lines.
Solid lines means, that one role is granted to another and automatically assumed in all queries to the restricted views.
The dashed line means that one role is granted to another but not automatically assumed in queries to the restricted views.
@ -541,15 +541,15 @@ together {
}
package {
entity "Role customer#xyz:tenant" as roleCustXyzTenant
entity "Role customer#xyz:admin" as roleCustXyzAdmin
entity "Role customer#xyz:owner" as roleCustXyzOwner
entity "Role customer#xyz:TENANT" as roleCustXyzTenant
entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
entity "Role customer#xyz:OWNER" as roleCustXyzOwner
}
package {
entity "Role package#xyz00:owner" as rolePacXyz00Owner
entity "Role package#xyz00:admin" as rolePacXyz00Admin
entity "Role package#xyz00:tenant" as rolePacXyz00Tenant
entity "Role package#xyz00:OWNER" as rolePacXyz00Owner
entity "Role package#xyz00:ADMIN" as rolePacXyz00Admin
entity "Role package#xyz00:TENANT" as rolePacXyz00Tenant
}
rolePacXyz00Tenant --> permPacXyz00SELECT
@ -694,7 +694,7 @@ Users can view only the roles to which are granted to them.
Grant can be `empowered`, this means that the grantee user can grant the granted role to other users
and revoke grants to that role.
(TODO: access control part not yet implemented, currently all accessible roles can be granted to other users)
(TODO: access control part not yet implemented)
Grants can be `managed`, which means they are created and deleted by system-defined rules.
If a grant is not managed, it was created by an empowered user and can be deleted by empowered users.

View File

@ -1,16 +1,31 @@
package net.hostsharing.hsadminng.hs.office.coopassets;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
import java.io.IOException;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
@ -20,8 +35,11 @@ import java.util.UUID;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -109,7 +127,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
.toRole("membership", ADMIN).grantPermission(INSERT)
.toRole("membership", ADMIN).grantPermission(UPDATE)
.toRole("membership", ADMIN).grantPermission(SELECT);
.toRole("membership", AGENT).grantPermission(SELECT);
}
public static void main(String[] args) throws IOException {

View File

@ -1,15 +1,29 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
@ -17,9 +31,11 @@ import java.util.UUID;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -105,7 +121,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
.toRole("membership", ADMIN).grantPermission(INSERT)
.toRole("membership", ADMIN).grantPermission(UPDATE)
.toRole("membership", ADMIN).grantPermission(SELECT);
.toRole("membership", AGENT).grantPermission(SELECT);
}
public static void main(String[] args) throws IOException {

View File

@ -28,7 +28,8 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@ -148,14 +149,14 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole("partnerRel", ADMIN);
with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("partnerRel", AGENT);
with.incomingSuperRole("partnerRel", ADMIN);
with.permission(DELETE);
with.permission(UPDATE);
})
.createSubRole(REFERRER, (with) -> {
.createSubRole(AGENT, (with) -> {
with.incomingSuperRole("partnerRel", AGENT);
with.outgoingSubRole("partnerRel", TENANT);
with.permission(SELECT);
});

View File

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

View File

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

View File

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

View File

@ -248,7 +248,7 @@ declare
objectUuidOfRole uuid;
roleUuid uuid;
begin
-- TODO.refa: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences
-- TODO.refact: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences
roleParts = overlay(roleIdName placing '#' from length(roleIdName) + 1 - strpos(reverse(roleIdName), ':'));
objectTableFromRoleIdName = split_part(roleParts, '#', 1);
objectNameFromRoleIdName = split_part(roleParts, '#', 2);
@ -356,13 +356,16 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
/*
*/
create domain RbacOp as varchar(6)
create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone
check (
VALUE = 'DELETE'
or VALUE = 'UPDATE'
or VALUE = 'SELECT'
or VALUE = 'INSERT'
or VALUE = 'ASSUME'
-- TODO: all values below are deprecated, use insert with table
or VALUE ~ '^add-[a-z]+$'
or VALUE ~ '^new-[a-z-]+$'
);
create table RbacPermission
@ -414,6 +417,37 @@ begin
return permissionUuid;
end; $$;
-- TODO: deprecated, remove and amend all usages to createPermission
create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[])
returns uuid[]
language plpgsql as $$
declare
refId uuid;
permissionIds uuid[] = array []::uuid[];
begin
if (forObjectUuid is null) then
raise exception 'forObjectUuid must not be null';
end if;
for i in array_lower(permitOps, 1)..array_upper(permitOps, 1)
loop
refId = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = permitOps[i]);
if (refId is null) then
insert
into RbacReference ("type")
values ('RbacPermission')
returning uuid into refId;
insert
into RbacPermission (uuid, objectUuid, op)
values (refId, forObjectUuid, permitOps[i]);
end if;
permissionIds = permissionIds || refId;
end loop;
return permissionIds;
end;
$$;
create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
returns uuid
returns null on null input
@ -615,6 +649,25 @@ begin
end;
$$;
-- TODO: deprecated, remove and use grantPermissionToRole(...)
create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[])
language plpgsql as $$
begin
if cardinality(permissionIds) = 0 then return; end if;
for i in array_lower(permissionIds, 1)..array_upper(permissionIds, 1)
loop
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission');
insert
into RbacGrants (grantedByTriggerOf, ascendantUuid, descendantUuid, assumed)
values (currentTriggerObjectUuid(), roleUuid, permissionIds[i], true)
on conflict do nothing; -- allow granting multiple times
end loop;
end;
$$;
create or replace procedure grantRoleToRole(subRoleId uuid, superRoleId uuid, doAssume bool = true)
language plpgsql as $$
begin
@ -638,7 +691,7 @@ declare
superRoleId uuid;
subRoleId uuid;
begin
-- TODO.refa: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
-- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
if superRole.objectUuid is null or subRole.objectuuid is null then
return;
end if;

View File

@ -73,6 +73,15 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'TENANT', assumed);
end; $f$;
-- TODO: remove guest role
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
return roleDescriptor('%2$s', entity.uuid, 'GUEST', assumed);
end; $f$;
create or replace function %1$sReferrer(entity %2$s)
returns RbacRoleDescriptor
language plpgsql

View File

@ -139,7 +139,7 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'GU
$$;
begin transaction;
call defineContext('creating role:global#loba:guest', null, null, null);
call defineContext('creating role:global#global:guest', null, null, null);
select createRole(globalGuest());
commit;
--//

View File

@ -42,7 +42,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]]
role:membership:AGENT[[membership:AGENT]]
end
subgraph membership:permissions[ ]
@ -105,16 +105,16 @@ role:partnerRel.contact:ADMIN -.-> role:partnerRel:TENANT
role:partnerRel:TENANT -.-> role:partnerRel.anchorPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.holderPerson:REFERRER
role:partnerRel:TENANT -.-> role:partnerRel.contact:REFERRER
role:partnerRel:ADMIN ==> role:membership:OWNER
role:membership:OWNER ==> role:membership:ADMIN
role:partnerRel:AGENT ==> role:membership:ADMIN
role:membership:ADMIN ==> role:membership:REFERRER
role:membership:REFERRER ==> role:partnerRel:TENANT
role:partnerRel:ADMIN ==> role:membership:ADMIN
role:membership:ADMIN ==> role:membership:AGENT
role:partnerRel:AGENT ==> role:membership:AGENT
role:membership:AGENT ==> role:partnerRel:TENANT
%% granting permissions to roles
role:global:ADMIN ==> perm:membership:INSERT
role:membership:OWNER ==> perm:membership:DELETE
role:membership:ADMIN ==> perm:membership:DELETE
role:membership:ADMIN ==> perm:membership:UPDATE
role:membership:REFERRER ==> perm:membership:SELECT
role:membership:AGENT ==> perm:membership:SELECT
```

View File

@ -45,23 +45,23 @@ begin
perform createRoleWithGrants(
hsOfficeMembershipOWNER(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[hsOfficeRelationADMIN(newPartnerRel)],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
hsOfficeMembershipADMIN(NEW),
permissions => array['UPDATE'],
permissions => array['DELETE', 'UPDATE'],
incomingSuperRoles => array[
hsOfficeMembershipOWNER(NEW),
hsOfficeRelationAGENT(newPartnerRel)]
hsOfficeRelationADMIN(newPartnerRel)]
);
perform createRoleWithGrants(
hsOfficeMembershipREFERRER(NEW),
hsOfficeMembershipAGENT(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeMembershipADMIN(NEW)],
incomingSuperRoles => array[
hsOfficeMembershipADMIN(NEW),
hsOfficeRelationAGENT(newPartnerRel)],
outgoingSubRoles => array[hsOfficeRelationTENANT(newPartnerRel)]
);

View File

@ -54,7 +54,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]]
role:membership:AGENT[[membership:AGENT]]
end
end
@ -106,15 +106,15 @@ role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER
role:membership.partnerRel:ADMIN -.-> role:membership:OWNER
role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:AGENT -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:REFERRER
role:membership:REFERRER -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:AGENT
role:membership.partnerRel:AGENT -.-> role:membership:AGENT
role:membership:AGENT -.-> role:membership.partnerRel:TENANT
%% granting permissions to roles
role:membership:ADMIN ==> perm:coopSharesTransaction:INSERT
role:membership:ADMIN ==> perm:coopSharesTransaction:UPDATE
role:membership:ADMIN ==> perm:coopSharesTransaction:SELECT
role:membership:AGENT ==> perm:coopSharesTransaction:SELECT
```

View File

@ -38,7 +38,7 @@ begin
SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership;
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipADMIN(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid);

View File

@ -54,7 +54,7 @@ subgraph membership["`**membership**`"]
role:membership:OWNER[[membership:OWNER]]
role:membership:ADMIN[[membership:ADMIN]]
role:membership:REFERRER[[membership:REFERRER]]
role:membership:AGENT[[membership:AGENT]]
end
end
@ -106,15 +106,15 @@ role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER
role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER
role:membership.partnerRel:ADMIN -.-> role:membership:OWNER
role:membership:OWNER -.-> role:membership:ADMIN
role:membership.partnerRel:AGENT -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:REFERRER
role:membership:REFERRER -.-> role:membership.partnerRel:TENANT
role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN
role:membership:ADMIN -.-> role:membership:AGENT
role:membership.partnerRel:AGENT -.-> role:membership:AGENT
role:membership:AGENT -.-> role:membership.partnerRel:TENANT
%% granting permissions to roles
role:membership:ADMIN ==> perm:coopAssetsTransaction:INSERT
role:membership:ADMIN ==> perm:coopAssetsTransaction:UPDATE
role:membership:ADMIN ==> perm:coopAssetsTransaction:SELECT
role:membership:AGENT ==> perm:coopAssetsTransaction:SELECT
```

View File

@ -38,7 +38,7 @@ begin
SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid INTO newMembership;
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipADMIN(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid);

View File

@ -112,7 +112,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
"{ grant perm:coopassetstransaction#temprefB:SELECT to role:membership#M-1000101:ADMIN by system and assume }",
"{ grant perm:coopassetstransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
"{ grant perm:coopassetstransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null));
}

View File

@ -111,7 +111,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
"{ grant perm:coopsharestransaction#temprefB:SELECT to role:membership#M-1000101:ADMIN by system and assume }",
"{ grant perm:coopsharestransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
"{ grant perm:coopsharestransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null));
}

View File

@ -335,18 +335,18 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
}
@Test
void partnerRelAgent_canPatchValidityOfRelatedMembership() {
void partnerRelAdmin_canPatchValidityOfRelatedMembership() {
// given
final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT";
context.define("superuser-alex@hostsharing.net", givenPartnerAgent);
final var givenPartnerAdmin = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN";
context.define("superuser-alex@hostsharing.net", givenPartnerAdmin);
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
// when
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.header("assumed-roles", givenPartnerAgent)
.header("assumed-roles", givenPartnerAdmin)
.contentType(ContentType.JSON)
.body("""
{

View File

@ -110,9 +110,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
"hs_office_membership#M-1000117:ADMIN",
"hs_office_membership#M-1000117:OWNER",
"hs_office_membership#M-1000117:REFERRER"));
"hs_office_membership#M-1000117:ADMIN",
"hs_office_membership#M-1000117:AGENT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
@ -122,21 +122,20 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
"{ grant perm:membership#M-1000117:INSERT>coopsharestransaction to role:membership#M-1000117:ADMIN by system and assume }",
// owner
"{ grant perm:membership#M-1000117:DELETE to role:membership#M-1000117:OWNER by system and assume }",
"{ grant perm:membership#M-1000117:DELETE to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:OWNER to user:superuser-alex@hostsharing.net by membership#M-1000117:OWNER and assume }",
// admin
"{ grant perm:membership#M-1000117:UPDATE to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:ADMIN to role:membership#M-1000117:OWNER by system and assume }",
"{ grant role:membership#M-1000117:OWNER to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
"{ grant role:membership#M-1000117:OWNER to user:superuser-alex@hostsharing.net by membership#M-1000117:OWNER and assume }",
"{ grant role:membership#M-1000117:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
// agent
"{ grant role:membership#M-1000117:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
"{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:AGENT by system and assume }",
"{ grant role:membership#M-1000117:AGENT to role:membership#M-1000117:ADMIN by system and assume }",
// referrer
"{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:REFERRER by system and assume }",
"{ grant role:membership#M-1000117:REFERRER to role:membership#M-1000117:ADMIN by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:REFERRER by system and assume }",
"{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
"{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:AGENT by system and assume }",
null));
}
@ -224,20 +223,20 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
}
@Test
public void membershipReferrer_canViewButNotUpdateRelatedMembership() {
public void membershipAgent_canViewButNotUpdateRelatedMembership() {
// given
context("superuser-alex@hostsharing.net");
final var givenMembership = givenSomeTemporaryMembership("First", "13");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
assertThatMembershipIsVisibleForRole(
givenMembership,
"hs_office_membership#M-1000113:REFERRER");
"hs_office_membership#M-1000113:AGENT");
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
// TODO: we should test with debitor- and partner-admin as well
context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113:REFERRER");
context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113:AGENT");
givenMembership.setValidity(
Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);

View File

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

View File

@ -5,7 +5,7 @@ import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRvEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository;
@ -361,11 +361,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
this(currentUser, "");
}
GrantFixture grantsRole(final RbacRoleRvEntity givenOwnPackageAdminRole) {
GrantFixture grantsRole(final RbacRoleEntity givenOwnPackageAdminRole) {
return new GrantFixture(givenOwnPackageAdminRole);
}
RevokeFixture revokesRole(final RbacRoleRvEntity givenOwnPackageAdminRole) {
RevokeFixture revokesRole(final RbacRoleEntity givenOwnPackageAdminRole) {
return new RevokeFixture(givenOwnPackageAdminRole);
}
@ -376,11 +376,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
class GrantFixture {
private Subject grantingSubject = Subject.this;
private final RbacRoleRvEntity grantedRole;
private final RbacRoleEntity grantedRole;
private boolean assumed;
private RbacUserEntity granteeUser;
public GrantFixture(final RbacRoleRvEntity roleToGrant) {
public GrantFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant;
}
@ -417,11 +417,11 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
class RevokeFixture {
private Subject currentSubject = Subject.this;
private final RbacRoleRvEntity grantedRole;
private final RbacRoleEntity grantedRole;
private boolean assumed;
private RbacUserEntity granteeUser;
public RevokeFixture(final RbacRoleRvEntity roleToGrant) {
public RevokeFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant;
}
@ -455,9 +455,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
private class GetGrantByIdFixture {
private Subject currentSubject = Subject.this;
private RbacRoleRvEntity grantedRole;
private RbacRoleEntity grantedRole;
GetGrantByIdFixture forGrantedRole(final RbacRoleRvEntity grantedRole) {
GetGrantByIdFixture forGrantedRole(final RbacRoleEntity grantedRole) {
this.grantedRole = grantedRole;
return this;
}
@ -507,7 +507,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
}).assertNotNull().returnedValue();
}
RbacRoleRvEntity getRbacRoleByName(final String roleName) {
RbacRoleEntity getRbacRoleByName(final String roleName) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
return rbacRoleRepository.findByRoleName(roleName);

View File

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

View File

@ -4,11 +4,11 @@ import static java.util.UUID.randomUUID;
public class TestRbacRole {
public static final RbacRoleRvEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.ADMIN);
static final RbacRoleRvEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.OWNER);
static final RbacRoleRvEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.ADMIN);
public static final RbacRoleEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.ADMIN);
static final RbacRoleEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.OWNER);
static final RbacRoleEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.ADMIN);
static public RbacRoleRvEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) {
return new RbacRoleRvEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+':'+roleType);
static public RbacRoleEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) {
return new RbacRoleEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+':'+roleType);
}
}