WIP: introduce separate database schemas #102

Closed
hsh-michaelhoennig wants to merge 57 commits from introduce-separate-database-schemas into master
60 changed files with 376 additions and 384 deletions
Showing only changes of commit d4b767af0d - Show all commits

View File

@ -29,7 +29,7 @@ skinparam linetype ortho
package RBAC { package RBAC {
' forward declarations ' forward declarations
entity RbacUser entity RbacSubject
together { together {
@ -37,8 +37,8 @@ package RBAC {
entity RbacPermission entity RbacPermission
RbacUser -[hidden]> RbacRole RbacSubject -[hidden]> RbacRole
RbacRole -[hidden]> RbacUser RbacRole -[hidden]> RbacSubject
} }
together { together {
@ -57,11 +57,11 @@ package RBAC {
RbacGrant o-u-> RbacReference RbacGrant o-u-> RbacReference
enum RbacReferenceType { enum RbacReferenceType {
RbacUser RbacSubject
RbacRole RbacRole
RbacPermission RbacPermission
} }
RbacReferenceType ..> RbacUser RbacReferenceType ..> RbacSubject
RbacReferenceType ..> RbacRole RbacReferenceType ..> RbacRole
RbacReferenceType ..> RbacPermission RbacReferenceType ..> RbacPermission
@ -71,12 +71,12 @@ package RBAC {
type : RbacReferenceType type : RbacReferenceType
} }
RbacReference o--> RbacReferenceType RbacReference o--> RbacReferenceType
entity RbacUser { entity RbacSubject {
*uuid : uuid <<generated>> *uuid : uuid <<generated>>
-- --
name : varchar name : varchar
} }
RbacUser o-- RbacReference RbacSubject o-- RbacReference
entity RbacRole { entity RbacRole {
*uuid : uuid(RbacReference) *uuid : uuid(RbacReference)
@ -143,20 +143,20 @@ The primary key of the *RbacReference* and its referred object is always identic
#### RbacReferenceType #### RbacReferenceType
The enum *RbacReferenceType* describes the type of reference. The enum *RbacReferenceType* describes the type of reference.
It's only needed to make it easier to find the referred object in *RbacUser*, *RbacRole* or *RbacPermission*. It's only needed to make it easier to find the referred object in *RbacSubject*, *RbacRole* or *RbacPermission*.
#### RbacUser #### RbacSubject
An *RbacUser* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address). An *RbacSubject* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address).
*RbacUser*s can be assigned to multiple *RbacRole*s, through which they can get permissions to *RbacObject*s. *RbacSubject*s can be assigned to multiple *RbacRole*s, through which they can get permissions to *RbacObject*s.
The primary key of the *RbacUser* is identical to its related *RbacReference*. The primary key of the *RbacSubject* is identical to its related *RbacReference*.
#### RbacRole #### RbacRole
An *RbacRole* represents a collection of directly or indirectly assigned *RbacPermission*s. An *RbacRole* represents a collection of directly or indirectly assigned *RbacPermission*s.
Each *RbacRole* can be assigned to *RbacUser*s or to another *RbacRole*. Each *RbacRole* can be assigned to *RbacSubject*s or to another *RbacRole*.
Both kinds of assignments are represented via *RbacGrant*. Both kinds of assignments are represented via *RbacGrant*.
@ -184,7 +184,7 @@ Only with this rule, the foreign key in *RbacPermission* can be defined as `NOT
#### RbacGrant #### RbacGrant
The *RbacGrant* entities represent the access-rights structure from *RbacUser*s via hierarchical *RbacRoles* down to *RbacPermission*s. The *RbacGrant* entities represent the access-rights structure from *RbacSubject*s via hierarchical *RbacRoles* down to *RbacPermission*s.
The core SQL queries to determine access rights are all recursive queries on the *RbacGrant* table. The core SQL queries to determine access rights are all recursive queries on the *RbacGrant* table.
@ -284,7 +284,7 @@ hide circle
' use right-angled line routing ' use right-angled line routing
' skinparam linetype ortho ' skinparam linetype ortho
package RbacUsers { package RbacSubjects {
object UserMike object UserMike
object UserSuse object UserSuse
object UserPaul object UserPaul
@ -296,7 +296,7 @@ package RbacRoles {
object RoleCustXyz_Admin object RoleCustXyz_Admin
object RolePackXyz00_Owner object RolePackXyz00_Owner
} }
RbacUsers -[hidden]> RbacRoles RbacSubjects -[hidden]> RbacRoles
package RbacPermissions { package RbacPermissions {
object PermCustXyz_SELECT object PermCustXyz_SELECT
@ -365,7 +365,7 @@ This way, each user can only select the data they have 'SELECT'-permission for,
### Current User ### Current User
The current use is taken from the session variable `hsadminng.currentSubject` which contains the name of the user as stored in the The current use is taken from the session variable `hsadminng.currentSubject` which contains the name of the user as stored in the
*RbacUser*s table. Example: *RbacSubject*s table. Example:
SET LOCAL hsadminng.currentSubject = 'mike@hostsharing.net'; SET LOCAL hsadminng.currentSubject = 'mike@hostsharing.net';
@ -671,9 +671,9 @@ Access Control for business objects checked according to the assigned roles.
But we decided not to create such roles and permissions for the RBAC-Objects itself. But we decided not to create such roles and permissions for the RBAC-Objects itself.
It would have overcomplicated the system and the necessary information can easily be added to the RBAC-Objects itself, mostly the `RbacGrant`s. It would have overcomplicated the system and the necessary information can easily be added to the RBAC-Objects itself, mostly the `RbacGrant`s.
### RbacUser ### RbacSubject
Users can self-register, thus to create a new RbacUser entity, no login is required. Users can self-register, thus to create a new RbacSubject entity, no login is required.
But such a user has no access-rights except viewing itself. But such a user has no access-rights except viewing itself.
Users can view themselves. Users can view themselves.

View File

@ -9,21 +9,21 @@ select isGranted(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrat
-- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER')); -- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select count(*) select count(*)
FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacUser('superuser-fran@hostsharing.net'), FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacSubject('superuser-fran@hostsharing.net'),
ARRAY(select uuid from customer where reference < 1100000)); ARRAY(select uuid from customer where reference < 1100000));
select count(*) select count(*)
FROM queryAllPermissionsOfSubjectId(findRbacUser('superuser-fran@hostsharing.net')); FROM queryAllPermissionsOfSubjectId(findRbacSubject('superuser-fran@hostsharing.net'));
select * select *
FROM queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com')); FROM queryAllPermissionsOfSubjectId(findRbacSubject('alex@example.com'));
select * select *
FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com')); FROM queryAllPermissionsOfSubjectId(findRbacSubject('rosa@example.com'));
select * select *
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer', FROM queryAllRbacSubjectsWithPermissionsFor(findEffectivePermissionId('customer',
(SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
'add-package')); 'add-package'));
select * select *
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package', FROM queryAllRbacSubjectsWithPermissionsFor(findEffectivePermissionId('package',
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
'DELETE')); 'DELETE'));
@ -33,7 +33,7 @@ $$
userId uuid; userId uuid;
result bool; result bool;
BEGIN BEGIN
userId = findRbacUser('superuser-alex@hostsharing.net'); userId = findRbacSubject('superuser-alex@hostsharing.net');
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId)); result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId));
IF (result) THEN IF (result) THEN
RAISE EXCEPTION 'expected permission NOT to be granted, but it is'; RAISE EXCEPTION 'expected permission NOT to be granted, but it is';

View File

@ -38,7 +38,7 @@ CREATE OR REPLACE RULE "_RETURN" AS
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), rbac.currentSubjectUuid()); SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), rbac.currentSubjectUuid());
SELECT * from cust_view LIMIT 10; SELECT * from cust_view LIMIT 10;
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net')); select queryAllPermissionsOfSubjectId(findRbacSubject('superuser-alex@hostsharing.net'));
-- access control via view-rule with join to recursive permissions - really fast (38ms for 1 million rows) -- access control via view-rule with join to recursive permissions - really fast (38ms for 1 million rows)
SET SESSION SESSION AUTHORIZATION DEFAULT; SET SESSION SESSION AUTHORIZATION DEFAULT;
@ -80,10 +80,10 @@ SELECT * from cust_view where reference=1144150;
select rr.uuid, rr.type from RbacGrants g select rr.uuid, rr.type from RbacGrants g
join RbacReference RR on g.ascendantUuid = RR.uuid join RbacReference RR on g.ascendantUuid = RR.uuid
where g.descendantUuid in ( where g.descendantUuid in (
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com')) select uuid from queryAllPermissionsOfSubjectId(findRbacSubject('alex@example.com'))
where objectTable='test_customer'); where objectTable='test_customer');
call grantRoleToUser(findRoleId('test_customer#aaa:ADMIN'), findRbacUser('aaaaouq@example.com')); call grantRoleToUser(findRoleId('test_customer#aaa:ADMIN'), findRbacSubject('aaaaouq@example.com'));
select queryAllPermissionsOfSubjectId(findRbacUser('aaaaouq@example.com')); select queryAllPermissionsOfSubjectId(findRbacSubject('aaaaouq@example.com'));

View File

@ -22,7 +22,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; 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.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; 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.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST;

View File

@ -14,7 +14,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;

View File

@ -11,7 +11,7 @@ import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;

View File

@ -45,7 +45,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; 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.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; 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.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;

View File

@ -16,7 +16,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;

View File

@ -24,7 +24,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; 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.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; 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.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;

View File

@ -26,7 +26,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingD
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; 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.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;

View File

@ -27,7 +27,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingD
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.ROLE_TO_ROLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.ROLE_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
import static org.apache.commons.collections4.SetUtils.hashSet; import static org.apache.commons.collections4.SetUtils.hashSet;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -41,7 +41,7 @@ public class RbacView {
private final EntityAlias rootEntityAlias; private final EntityAlias rootEntityAlias;
private final Set<RbacUserReference> userDefs = new LinkedHashSet<>(); private final Set<RbacSubjectReference> userDefs = new LinkedHashSet<>();
private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>(); private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>();
private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>(); private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>();
private final Map<String, EntityAlias> entityAliases = new HashMap<>() { private final Map<String, EntityAlias> entityAliases = new HashMap<>() {
@ -97,7 +97,7 @@ public class RbacView {
RbacView(final String alias, final Class<? extends BaseEntity> entityClass) { RbacView(final String alias, final Class<? extends BaseEntity> entityClass) {
rootEntityAlias = new EntityAlias(alias, entityClass); rootEntityAlias = new EntityAlias(alias, entityClass);
entityAliases.put(alias, rootEntityAlias); entityAliases.put(alias, rootEntityAlias);
new RbacUserReference(CREATOR); new RbacSubjectReference(CREATOR);
entityAliases.put("global", new EntityAlias("global")); entityAliases.put("global", new EntityAlias("global"));
} }
@ -467,7 +467,7 @@ public class RbacView {
return new RbacExampleRole(entityAlias, role); return new RbacExampleRole(entityAlias, role);
} }
private RbacGrantDefinition grantRoleToSubject(final RbacRoleDefinition roleDefinition, final RbacUserReference user) { private RbacGrantDefinition grantRoleToSubject(final RbacRoleDefinition roleDefinition, final RbacSubjectReference user) {
return findOrCreateGrantDef(roleDefinition, user).toCreate(); return findOrCreateGrantDef(roleDefinition, user).toCreate();
} }
@ -564,7 +564,7 @@ public class RbacView {
@EqualsAndHashCode @EqualsAndHashCode
public class RbacGrantDefinition { public class RbacGrantDefinition {
private final RbacUserReference userDef; private final RbacSubjectReference userDef;
private final RbacRoleDefinition superRoleDef; private final RbacRoleDefinition superRoleDef;
private final RbacRoleDefinition subRoleDef; private final RbacRoleDefinition subRoleDef;
private final RbacPermissionDefinition permDef; private final RbacPermissionDefinition permDef;
@ -605,7 +605,7 @@ public class RbacView {
register(this); register(this);
} }
public RbacGrantDefinition(final RbacRoleDefinition roleDef, final RbacUserReference userDef) { public RbacGrantDefinition(final RbacRoleDefinition roleDef, final RbacSubjectReference userDef) {
this.userDef = userDef; this.userDef = userDef;
this.subRoleDef = roleDef; this.subRoleDef = roleDef;
this.superRoleDef = null; this.superRoleDef = null;
@ -770,7 +770,7 @@ public class RbacView {
* @return * @return
* The grant definition for further chained calls. * The grant definition for further chained calls.
*/ */
public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) { public RbacGrantDefinition owningUser(final RbacSubjectReference.UserRole userRole) {
return grantRoleToSubject(this, findUserRef(userRole)); return grantRoleToSubject(this, findUserRef(userRole));
} }
@ -833,12 +833,12 @@ public class RbacView {
} }
} }
public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) { public RbacSubjectReference findUserRef(final RbacSubjectReference.UserRole userRole) {
return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow(); return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow();
} }
@EqualsAndHashCode @EqualsAndHashCode
public class RbacUserReference { public class RbacSubjectReference {
public enum UserRole { public enum UserRole {
GLOBAL_ADMIN, GLOBAL_ADMIN,
@ -847,7 +847,7 @@ public class RbacView {
final UserRole role; final UserRole role;
public RbacUserReference(final UserRole creator) { public RbacSubjectReference(final UserRole creator) {
this.role = creator; this.role = creator;
userDefs.add(this); userDefs.add(this);
} }
@ -885,7 +885,7 @@ public class RbacView {
.orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate .orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate
} }
private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacUserReference user) { private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacSubjectReference user) {
return grantDefs.stream() return grantDefs.stream()
.filter(g -> g.subRoleDef == roleDefinition && g.userDef == user) .filter(g -> g.subRoleDef == roleDefinition && g.userDef == user)
.findFirst() .findFirst()

View File

@ -578,7 +578,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn(); plPgSql.writeLn();
} }
private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) { private String toPlPgSqlReference(final RbacView.RbacSubjectReference userRef) {
return switch (userRef.role) { return switch (userRef.role) {
case CREATOR -> "currentSubjectUuid()"; case CREATOR -> "currentSubjectUuid()";
default -> throw new IllegalArgumentException("unknown user role: " + userRef); default -> throw new IllegalArgumentException("unknown user role: " + userRef);

View File

@ -50,7 +50,7 @@ public class RbacGrantController implements RbacGrantsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacGrantResource>> listUserGrants( public ResponseEntity<List<RbacGrantResource>> listSubjectGrants(
final String currentSubject, final String currentSubject,
final String assumedRoles) { final String assumedRoles) {
@ -97,7 +97,7 @@ public class RbacGrantController implements RbacGrantsApi {
// TODO.feat: implement an endpoint to create a Mermaid flowchart with all grants of a given user // TODO.feat: implement an endpoint to create a Mermaid flowchart with all grants of a given user
// @GetMapping( // @GetMapping(
// path = "/api/rbac/users/{subjectUuid}/grants", // path = "/api/rbac/subjects/{subjectUuid}/grants",
// produces = {"text/vnd.mermaid"}) // produces = {"text/vnd.mermaid"})
// @Transactional(readOnly = true) // @Transactional(readOnly = true)
// public ResponseEntity<String> allGrantsOfUserAsMermaid( // public ResponseEntity<String> allGrantsOfUserAsMermaid(

View File

@ -33,7 +33,7 @@ public class RbacGrantEntity {
private UUID grantedRoleUuid; private UUID grantedRoleUuid;
@Column(name = "username", updatable = false, insertable = false) @Column(name = "username", updatable = false, insertable = false)
private String granteeUserName; private String granteeSubjectName;
@Id @Id
@Column(name = "subjectuuid") @Column(name = "subjectuuid")
@ -60,7 +60,7 @@ public class RbacGrantEntity {
public String toDisplay() { public String toDisplay() {
return "{ grant role:" + grantedRoleIdName + return "{ grant role:" + grantedRoleIdName +
" to user:" + granteeUserName + " to user:" + granteeSubjectName +
" by role:" + grantedByRoleIdName + " by role:" + grantedByRoleIdName +
(assumed ? " and assume" : "") + (assumed ? " and assume" : "") +
" }"; " }";

View File

@ -1,46 +0,0 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.UUID;
public interface RbacUserRepository extends Repository<RbacUserEntity, UUID> {
@Query("""
select u from RbacUserEntity u
where :userName is null or u.name like concat(cast(:userName as text), '%')
order by u.name
""")
List<RbacUserEntity> findByOptionalNameLike(String userName);
// bypasses the restricted view, to be able to grant rights to arbitrary user
@Query(value = "select * from rbac.subject where name=:userName", nativeQuery = true)
RbacUserEntity findByName(String userName);
RbacUserEntity findByUuid(UUID uuid);
@Query(value = "select * from grantedPermissions(:subjectUuid)", nativeQuery = true)
List<RbacUserPermission> findPermissionsOfUserByUuid(UUID subjectUuid);
/*
Can't use save/saveAndFlush from SpringData because the uuid is not generated on the entity level,
but explicitly, and then SpringData check's if it exists using an SQL SELECT.
And SQL SELECT needs a currentSubject which we don't yet have in the case of self registration.
*/
@Modifying
@Query(value = "insert into RBacUser_RV (uuid, name) values( :#{#newUser.uuid}, :#{#newUser.name})", nativeQuery = true)
void insert(final RbacUserEntity newUser);
default RbacUserEntity create(final RbacUserEntity rbacUserEntity) {
if (rbacUserEntity.getUuid() == null) {
rbacUserEntity.setUuid(UUID.randomUUID());
}
insert(rbacUserEntity);
return rbacUserEntity;
}
void deleteByUuid(UUID subjectUuid);
}

View File

@ -1,10 +1,10 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacUsersApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacUserPermissionResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacUserResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -15,7 +15,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
public class RbacUserController implements RbacUsersApi { public class RbacSubjectController implements RbacSubjectsApi {
@Autowired @Autowired
private Context context; private Context context;
@ -24,73 +24,73 @@ public class RbacUserController implements RbacUsersApi {
private Mapper mapper; private Mapper mapper;
@Autowired @Autowired
private RbacUserRepository rbacUserRepository; private RbacSubjectRepository rbacSubjectRepository;
@Override @Override
@Transactional @Transactional
public ResponseEntity<RbacUserResource> createUser( public ResponseEntity<RbacSubjectResource> createSubject(
final RbacUserResource body final RbacSubjectResource body
) { ) {
context.define(null); context.define(null);
if (body.getUuid() == null) { if (body.getUuid() == null) {
body.setUuid(UUID.randomUUID()); body.setUuid(UUID.randomUUID());
} }
final var saved = mapper.map(body, RbacUserEntity.class); final var saved = mapper.map(body, RbacSubjectEntity.class);
rbacUserRepository.create(saved); rbacSubjectRepository.create(saved);
final var uri = final var uri =
MvcUriComponentsBuilder.fromController(getClass()) MvcUriComponentsBuilder.fromController(getClass())
.path("/api/rbac.yaml/users/{id}") .path("/api/rbac.yaml/users/{id}")
.buildAndExpand(saved.getUuid()) .buildAndExpand(saved.getUuid())
.toUri(); .toUri();
return ResponseEntity.created(uri).body(mapper.map(saved, RbacUserResource.class)); return ResponseEntity.created(uri).body(mapper.map(saved, RbacSubjectResource.class));
} }
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteUserByUuid( public ResponseEntity<Void> deleteSubjectByUuid(
final String currentSubject, final String currentSubject,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid final UUID subjectUuid
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
rbacUserRepository.deleteByUuid(subjectUuid); rbacSubjectRepository.deleteByUuid(subjectUuid);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<RbacUserResource> getUserById( public ResponseEntity<RbacSubjectResource> getSubjectById(
final String currentSubject, final String currentSubject,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid) { final UUID subjectUuid) {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
final var result = rbacUserRepository.findByUuid(subjectUuid); final var result = rbacSubjectRepository.findByUuid(subjectUuid);
if (result == null) { if (result == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result, RbacUserResource.class)); return ResponseEntity.ok(mapper.map(result, RbacSubjectResource.class));
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacUserResource>> listUsers( public ResponseEntity<List<RbacSubjectResource>> listSubjects(
final String currentSubject, final String currentSubject,
final String assumedRoles, final String assumedRoles,
final String userName final String userName
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
return ResponseEntity.ok(mapper.mapList(rbacUserRepository.findByOptionalNameLike(userName), RbacUserResource.class)); return ResponseEntity.ok(mapper.mapList(rbacSubjectRepository.findByOptionalNameLike(userName), RbacSubjectResource.class));
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacUserPermissionResource>> listUserPermissions( public ResponseEntity<List<RbacSubjectPermissionResource>> listSubjectPermissions(
final String currentSubject, final String currentSubject,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid final UUID subjectUuid
@ -98,7 +98,7 @@ public class RbacUserController implements RbacUsersApi {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
return ResponseEntity.ok(mapper.mapList( return ResponseEntity.ok(mapper.mapList(
rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid), rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid),
RbacUserPermissionResource.class)); RbacSubjectPermissionResource.class));
} }
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import lombok.*; import lombok.*;
import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.Immutable;
@ -13,14 +13,14 @@ import java.time.temporal.ChronoUnit;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Table(name = "rbacuser_rv") @Table(schema = "rbac", name = "subject_rv")
@Getter @Getter
@Setter @Setter
@ToString @ToString
@Immutable @Immutable
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class RbacUserEntity { public class RbacSubjectEntity {
private static final int MAX_VALIDITY_DAYS = 21; private static final int MAX_VALIDITY_DAYS = 21;
private static DateTimeFormatter DATE_FORMAT_WITH_FULLHOUR = DateTimeFormatter.ofPattern("MM-dd-yyyy HH"); private static DateTimeFormatter DATE_FORMAT_WITH_FULLHOUR = DateTimeFormatter.ofPattern("MM-dd-yyyy HH");

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import java.util.UUID; import java.util.UUID;
public interface RbacUserPermission { public interface RbacSubjectPermission {
UUID getRoleUuid(); UUID getRoleUuid();
String getRoleName(); String getRoleName();

View File

@ -0,0 +1,46 @@
package net.hostsharing.hsadminng.rbac.subject;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.UUID;
public interface RbacSubjectRepository extends Repository<RbacSubjectEntity, UUID> {
@Query("""
select u from RbacSubjectEntity u
where :userName is null or u.name like concat(cast(:userName as text), '%')
order by u.name
""")
List<RbacSubjectEntity> findByOptionalNameLike(String userName);
// bypasses the restricted view, to be able to grant rights to arbitrary user
@Query(value = "select * from rbac.subject where name=:userName", nativeQuery = true)
RbacSubjectEntity findByName(String userName);
RbacSubjectEntity findByUuid(UUID uuid);
@Query(value = "select * from rbac.grantedPermissions(:subjectUuid)", nativeQuery = true)
List<RbacSubjectPermission> findPermissionsOfUserByUuid(UUID subjectUuid);
/*
Can't use save/saveAndFlush from SpringData because the uuid is not generated on the entity level,
but explicitly, and then SpringData check's if it exists using an SQL SELECT.
And SQL SELECT needs a currentSubject which we don't yet have in the case of self registration.
*/
@Modifying
@Query(value = "insert into rbac.subject_rv (uuid, name) values( :#{#newUser.uuid}, :#{#newUser.name})", nativeQuery = true)
void insert(final RbacSubjectEntity newUser);
default RbacSubjectEntity create(final RbacSubjectEntity rbacSubjectEntity) {
if (rbacSubjectEntity.getUuid() == null) {
rbacSubjectEntity.setUuid(UUID.randomUUID());
}
insert(rbacSubjectEntity);
return rbacSubjectEntity;
}
void deleteByUuid(UUID subjectUuid);
}

View File

@ -15,7 +15,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;

View File

@ -9,7 +9,7 @@ components:
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the currently logged in user. description: Identifying name of the current subject (e.g. user).
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current user is unknown or not authorized. description: The current subject is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current user or none of the assumed or roles is granted access to the resource. description: The current subject or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -9,7 +9,7 @@ components:
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the currently logged in user. description: Identifying name of the currently logged in subject.
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current user is unknown or not authorized. description: The current subject is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current user or none of the assumed or roles is granted access to the resource. description: The current subject or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of all booking items for a specified project. summary: Returns a list of all booking items for a specified project.
description: Returns the list of all booking items for a specified project which are visible to the current user or any of it's assumed roles. description: Returns the list of all booking items for a specified project which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-booking-items - hs-booking-items
operationId: listBookingItemsByProjectUuid operationId: listBookingItemsByProjectUuid

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of all booking projects for a specified debitor. summary: Returns a list of all booking projects for a specified debitor.
description: Returns the list of all booking projects for a specified debitor which are visible to the current user or any of it's assumed roles. description: Returns the list of all booking projects for a specified debitor which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-booking-projects - hs-booking-projects
operationId: listBookingProjectsByDebitorUuid operationId: listBookingProjectsByDebitorUuid

View File

@ -9,7 +9,7 @@ components:
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the currently logged in user. description: Identifying name of the currently logged in subject.
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current user is unknown or not authorized. description: The current subject is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current user or none of the assumed or roles is granted access to the resource. description: The current subject or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a filtered list of all hosting assets. summary: Returns a filtered list of all hosting assets.
description: Returns the list of all hosting assets which match the given filters and are visible to the current user or any of it's assumed roles. description: Returns the list of all hosting assets which match the given filters and are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-hosting-assets - hs-hosting-assets
operationId: listAssets operationId: listAssets

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) bankaccounts. summary: Returns a list of (optionally filtered) bankaccounts.
description: Returns the list of (optionally filtered) bankaccounts which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) bankaccounts which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-bank-accounts - hs-office-bank-accounts
operationId: listBankAccounts operationId: listBankAccounts

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) contacts. summary: Returns a list of (optionally filtered) contacts.
description: Returns the list of (optionally filtered) contacts which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) contacts which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-contacts - hs-office-contacts
operationId: listContacts operationId: listContacts

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) cooperative asset transactions. summary: Returns a list of (optionally filtered) cooperative asset transactions.
description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-coopAssets - hs-office-coopAssets
operationId: listCoopAssets operationId: listCoopAssets

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) cooperative share transactions. summary: Returns a list of (optionally filtered) cooperative share transactions.
description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-coopShares - hs-office-coopShares
operationId: listCoopShares operationId: listCoopShares

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) debitors. summary: Returns a list of (optionally filtered) debitors.
description: Returns the list of (optionally filtered) debitors which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) debitors which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-debitors - hs-office-debitors
operationId: listDebitors operationId: listDebitors

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) memberships. summary: Returns a list of (optionally filtered) memberships.
description: Returns the list of memberships which are visible to the current user or any of it's assumed roles. description: Returns the list of memberships which are visible to the current subject or any of it's assumed roles.
The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time. The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time.
tags: tags:
- hs-office-memberships - hs-office-memberships

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) business partners. summary: Returns a list of (optionally filtered) business partners.
description: Returns the list of (optionally filtered) business partners which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) business partners which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-partners - hs-office-partners
operationId: listPartners operationId: listPartners

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) persons. summary: Returns a list of (optionally filtered) persons.
description: Returns the list of (optionally filtered) persons which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) persons which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-persons - hs-office-persons
operationId: listPersons operationId: listPersons

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) person relations for a given person. summary: Returns a list of (optionally filtered) person relations for a given person.
description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-relations - hs-office-relations
operationId: listRelations operationId: listRelations

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) SEPA Mandates. summary: Returns a list of (optionally filtered) SEPA Mandates.
description: Returns the list of (optionally filtered) SEPA Mandates which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) SEPA Mandates which are visible to the current subject or any of it's assumed roles.
tags: tags:
- hs-office-sepaMandates - hs-office-sepaMandates
operationId: listSepaMandatesByIBAN operationId: listSepaMandatesByIBAN

View File

@ -18,7 +18,7 @@ components:
grantedRoleUuid: grantedRoleUuid:
type: string type: string
format: uuid format: uuid
granteeUserName: granteeSubjectName:
type: string type: string
granteeSubjectUuid: granteeSubjectUuid:
type: string type: string

View File

@ -18,7 +18,7 @@ get:
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the user to whom the role was granted. description: UUID of the subject to who the role was granted.
responses: responses:
"200": "200":
description: OK description: OK
@ -53,7 +53,7 @@ delete:
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the user to whom the role was granted. description: UUID of the subject to which the role was granted.
responses: responses:
"204": "204":
description: No Content description: No Content

View File

@ -1,7 +1,7 @@
get: get:
tags: tags:
- rbac-grants - rbac-grants
operationId: listUserGrants operationId: listSubjectGrants
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentSubject'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'

View File

@ -3,7 +3,7 @@ components:
schemas: schemas:
RbacUser: RbacSubject:
type: object type: object
properties: properties:
uuid: uuid:
@ -11,7 +11,7 @@ components:
format: uuid format: uuid
name: name:
type: string type: string
RbacUserPermission: RbacSubjectPermission:
type: object type: object
properties: properties:
objectUuid: objectUuid:

View File

@ -1,8 +1,8 @@
get: get:
tags: tags:
- rbac-users - rbac-subjects
description: 'List all visible permissions granted to the given user; reduced ' description: 'List all visible permissions granted to the given subject; reduced '
operationId: listUserPermissions operationId: listSubjectPermissions
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentSubject'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
@ -20,7 +20,7 @@ get:
schema: schema:
type: array type: array
items: items:
$ref: 'rbac-user-schemas.yaml#/components/schemas/RbacUserPermission' $ref: 'rbac-subject-schemas.yaml#/components/schemas/RbacSubjectPermission'
"401": "401":
$ref: 'error-responses.yaml#/components/responses/Unauthorized' $ref: 'error-responses.yaml#/components/responses/Unauthorized'

View File

@ -1,8 +1,8 @@
get: get:
tags: tags:
- rbac-users - rbac-subjects
description: 'Fetch a single user by its id, if visible for the current subject.' description: 'Fetch a single subject by its id, if visible for the current subject.'
operationId: getUserById operationId: getSubjectById
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentSubject'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
@ -18,7 +18,7 @@ get:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: 'rbac-user-schemas.yaml#/components/schemas/RbacUser' $ref: 'rbac-subject-schemas.yaml#/components/schemas/RbacSubject'
"401": "401":
$ref: 'error-responses.yaml#/components/responses/Unauthorized' $ref: 'error-responses.yaml#/components/responses/Unauthorized'
@ -28,8 +28,8 @@ get:
delete: delete:
tags: tags:
- rbac-users - rbac-subjects
operationId: deleteUserByUuid operationId: deleteSubjectByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentSubject'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
@ -39,7 +39,7 @@ delete:
schema: schema:
type: string type: string
format: uuid format: uuid
description: UUID of the user to delete. description: UUID of the subject to delete.
responses: responses:
"204": "204":
description: No Content description: No Content

View File

@ -1,8 +1,8 @@
get: get:
tags: tags:
- rbac-users - rbac-subjects
description: List accessible RBAC users with optional filter by name. description: List accessible RBAC subjects with optional filter by name.
operationId: listUsers operationId: listSubjects
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentSubject'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
@ -19,7 +19,7 @@ get:
schema: schema:
type: array type: array
items: items:
$ref: 'rbac-user-schemas.yaml#/components/schemas/RbacUser' $ref: 'rbac-subject-schemas.yaml#/components/schemas/RbacSubject'
'401': '401':
$ref: 'error-responses.yaml#/components/responses/Unauthorized' $ref: 'error-responses.yaml#/components/responses/Unauthorized'
'403': '403':
@ -27,22 +27,22 @@ get:
post: post:
tags: tags:
- rbac-users - rbac-subjects
description: Create a new RBAC user. description: Create a new RBAC subject (e.g. user).
operationId: createUser operationId: createSubject
requestBody: requestBody:
required: true required: true
content: content:
application/json: application/json:
schema: schema:
$ref: 'rbac-user-schemas.yaml#/components/schemas/RbacUser' $ref: 'rbac-subject-schemas.yaml#/components/schemas/RbacSubject'
responses: responses:
'201': '201':
description: Created description: Created
content: content:
'application/json': 'application/json':
schema: schema:
$ref: 'rbac-user-schemas.yaml#/components/schemas/RbacUser' $ref: 'rbac-subject-schemas.yaml#/components/schemas/RbacSubject'
'409': '409':
$ref: 'error-responses.yaml#/components/responses/Conflict' $ref: 'error-responses.yaml#/components/responses/Conflict'

View File

@ -8,14 +8,14 @@ servers:
paths: paths:
/api/rbac/users: /api/rbac/subjects:
$ref: 'rbac-users.yaml' $ref: 'rbac-subjects.yaml'
/api/rbac/users/{subjectUuid}/permissions: /api/rbac/subjects/{subjectUuid}/permissions:
$ref: 'rbac-users-with-id-permissions.yaml' $ref: 'rbac-subjects-with-id-permissions.yaml'
/api/rbac/users/{subjectUuid}: /api/rbac/subjects/{subjectUuid}:
$ref: 'rbac-users-with-uuid.yaml' $ref: 'rbac-subjects-with-uuid.yaml'
/api/rbac/roles: /api/rbac/roles:
$ref: 'rbac-roles.yaml' $ref: 'rbac-roles.yaml'

View File

@ -1,6 +1,6 @@
get: get:
summary: Returns a list of (optionally filtered) customers. summary: Returns a list of (optionally filtered) customers.
description: Returns the list of (optionally filtered) customers which are visible to the current user or any of it's assumed roles. description: Returns the list of (optionally filtered) customers which are visible to the current subject or any of it's assumed roles.
tags: tags:
- testCustomers - testCustomers
operationId: listCustomers operationId: listCustomers

View File

@ -204,8 +204,8 @@ execute function rbac.delete_grant_tf();
Creates a view to the users table with additional columns Creates a view to the users table with additional columns
for easier human readability. for easier human readability.
*/ */
drop view if exists RbacUser_ev; drop view if exists rbac.subject_ev;
create or replace view RbacUser_ev as create or replace view rbac.subject_ev as
select distinct * select distinct *
-- @formatter:off -- @formatter:off
from ( from (
@ -229,8 +229,8 @@ select distinct *
Creates a view to the users table with row-level limitation Creates a view to the users table with row-level limitation
based on the grants of the current user or assumed roles. based on the grants of the current user or assumed roles.
*/ */
drop view if exists RbacUser_rv; drop view if exists rbac.subject_rv;
create or replace view RbacUser_rv as create or replace view rbac.subject_rv as
select distinct * select distinct *
-- @formatter:off -- @formatter:off
from ( from (
@ -247,7 +247,7 @@ create or replace view RbacUser_rv as
) as unordered ) as unordered
-- @formatter:on -- @formatter:on
order by unordered.name; order by unordered.name;
grant all privileges on RbacUser_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; grant all privileges on rbac.subject_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
--// --//
-- ============================================================================ -- ============================================================================
@ -255,9 +255,9 @@ grant all privileges on RbacUser_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/** /**
Instead of insert trigger function for RbacUser_rv. Instead of insert trigger function for rbac.subject_rv.
*/ */
create or replace function insertRbacUser() create or replace function rbac.insert_subject_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
declare declare
@ -277,13 +277,13 @@ end;
$$; $$;
/* /*
Creates an instead of insert trigger for the RbacUser_rv view. Creates an instead of insert trigger for the rbac.subject_rv view.
*/ */
create trigger insertRbacUser_Trigger create trigger insert_subject_tg
instead of insert instead of insert
on RbacUser_rv on rbac.subject_rv
for each row for each row
execute function insertRbacUser(); execute function rbac.insert_subject_tf();
--// --//
-- ============================================================================ -- ============================================================================
@ -291,11 +291,11 @@ execute function insertRbacUser();
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/** /**
Instead of delete trigger function for RbacUser_RV. Instead of delete trigger function for rbac.subject_rv.
Checks if the current subject (user / assumed role) has the permission to delete the user. Checks if the current subject (user / assumed role) has the permission to delete the user.
*/ */
create or replace function deleteRbacUser() create or replace function delete_subject_tf()
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
@ -307,13 +307,13 @@ begin
end; $$; end; $$;
/* /*
Creates an instead of delete trigger for the RbacUser_rv view. Creates an instead of delete trigger for the rbac.subject_rv view.
*/ */
create trigger deleteRbacUser_Trigger create trigger delete_subject_tg
instead of delete instead of delete
on RbacUser_rv on rbac.subject_rv
for each row for each row
execute function deleteRbacUser(); execute function delete_subject_tf();
--/ --/
-- ============================================================================ -- ============================================================================
@ -324,8 +324,8 @@ execute function deleteRbacUser();
based on the grants of the current user or assumed roles. based on the grants of the current user or assumed roles.
*/ */
-- @formatter:off -- @formatter:off
drop view if exists RbacOwnGrantedPermissions_rv; drop view if exists rbac.own_granted_permissions_rv;
create or replace view RbacOwnGrantedPermissions_rv as create or replace view rbac.own_granted_permissions_rv as
select r.uuid as roleuuid, p.uuid as permissionUuid, select r.uuid as roleuuid, p.uuid as permissionUuid,
(r.objecttable || ':' || r.objectidname || ':' || r.roletype) as roleName, p.op, (r.objecttable || ':' || r.objectidname || ':' || r.roletype) as roleName, p.op,
o.objecttable, r.objectidname, o.uuid as objectuuid o.objecttable, r.objectidname, o.uuid as objectuuid
@ -333,7 +333,7 @@ select r.uuid as roleuuid, p.uuid as permissionUuid,
join rbacgrants g on g.ascendantuuid = r.uuid join rbacgrants g on g.ascendantuuid = r.uuid
join rbacpermission p on p.uuid = g.descendantuuid join rbacpermission p on p.uuid = g.descendantuuid
join rbac.object o on o.uuid = p.objectuuid; join rbac.object o on o.uuid = p.objectuuid;
grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; grant all privileges on rbac.own_granted_permissions_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
-- @formatter:om -- @formatter:om
-- ============================================================================ -- ============================================================================
@ -343,7 +343,7 @@ grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RES
Returns all permissions granted to the given user, Returns all permissions granted to the given user,
which are also visible to the current user or assumed roles. which are also visible to the current user or assumed roles.
*/ */
create or replace function grantedPermissionsRaw(targetSubjectUuid uuid) create or replace function rbac.grantedPermissionsRaw(targetSubjectUuid uuid)
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid 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 returns null on null input
language plpgsql as $$ language plpgsql as $$
@ -379,14 +379,14 @@ begin
-- @formatter:on -- @formatter:on
end; $$; end; $$;
create or replace function grantedPermissions(targetSubjectUuid uuid) create or replace function rbac.grantedPermissions(targetSubjectUuid uuid)
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid 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 returns null on null input
language sql as $$ language sql as $$
select * from grantedPermissionsRaw(targetSubjectUuid) select * from rbac.grantedPermissionsRaw(targetSubjectUuid)
union all union all
select roleUuid, roleName, permissionUuid, 'SELECT'::RbacOp, opTableName, objectTable, objectIdName, objectUuid select roleUuid, roleName, permissionUuid, 'SELECT'::RbacOp, opTableName, objectTable, objectIdName, objectUuid
from grantedPermissionsRaw(targetSubjectUuid) from rbac.grantedPermissionsRaw(targetSubjectUuid)
where op <> 'SELECT'::RbacOp; where op <> 'SELECT'::RbacOp;
$$; $$;
--// --//

View File

@ -68,7 +68,7 @@ public class ArchitectureTest {
"..mapper", "..mapper",
"..ping", "..ping",
"..rbac", "..rbac",
"..rbac.rbacuser", "..rbac.rbacSubject",
"..rbac.rbacgrant", "..rbac.rbacgrant",
"..rbac.rbacrole", "..rbac.rbacrole",
"..rbac.rbacobject", "..rbac.rbacobject",

View File

@ -298,7 +298,7 @@ public class CsvDataImport extends ContextBasedTest {
protected void deleteFromCommonTables() { protected void deleteFromCommonTables() {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context(rbacSuperuser); context(rbacSuperuser);
em.createNativeQuery("delete from rbacuser_rv where name not like 'superuser-%'").executeUpdate(); em.createNativeQuery("delete from rbac.subject_rv where name not like 'superuser-%'").executeUpdate();
em.createNativeQuery("delete from basis.tx_journal where true").executeUpdate(); em.createNativeQuery("delete from basis.tx_journal where true").executeUpdate();
em.createNativeQuery("delete from basis.tx_context where true").executeUpdate(); em.createNativeQuery("delete from basis.tx_context where true").executeUpdate();
}).assertSuccessful(); }).assertSuccessful();

View File

@ -7,8 +7,8 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@ -43,7 +43,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
EntityManager em; EntityManager em;
@Autowired @Autowired
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Autowired @Autowired
RbacRoleRepository rbacRoleRepository; RbacRoleRepository rbacRoleRepository;
@ -73,7 +73,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// TODO: should there be a grantedByRole or just a grantedByTrigger? // TODO: should there be a grantedByRole or just a grantedByTrigger?
hasEntry("grantedByRoleIdName", "test_customer#xxx:OWNER"), hasEntry("grantedByRoleIdName", "test_customer#xxx:OWNER"),
hasEntry("grantedRoleIdName", "test_customer#xxx:ADMIN"), hasEntry("grantedRoleIdName", "test_customer#xxx:ADMIN"),
hasEntry("granteeUserName", "customer-admin@xxx.example.com") hasEntry("granteeSubjectName", "customer-admin@xxx.example.com")
) )
)) ))
.body("", hasItem( .body("", hasItem(
@ -81,28 +81,28 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// TODO: should there be a grantedByRole or just a grantedByTrigger? // TODO: should there be a grantedByRole or just a grantedByTrigger?
hasEntry("grantedByRoleIdName", "test_customer#yyy:OWNER"), hasEntry("grantedByRoleIdName", "test_customer#yyy:OWNER"),
hasEntry("grantedRoleIdName", "test_customer#yyy:ADMIN"), hasEntry("grantedRoleIdName", "test_customer#yyy:ADMIN"),
hasEntry("granteeUserName", "customer-admin@yyy.example.com") hasEntry("granteeSubjectName", "customer-admin@yyy.example.com")
) )
)) ))
.body("", hasItem( .body("", hasItem(
allOf( allOf(
hasEntry("grantedByRoleIdName", "global#global:ADMIN"), hasEntry("grantedByRoleIdName", "global#global:ADMIN"),
hasEntry("grantedRoleIdName", "global#global:ADMIN"), hasEntry("grantedRoleIdName", "global#global:ADMIN"),
hasEntry("granteeUserName", "superuser-fran@hostsharing.net") hasEntry("granteeSubjectName", "superuser-fran@hostsharing.net")
) )
)) ))
.body("", hasItem( .body("", hasItem(
allOf( allOf(
hasEntry("grantedByRoleIdName", "test_customer#xxx:ADMIN"), hasEntry("grantedByRoleIdName", "test_customer#xxx:ADMIN"),
hasEntry("grantedRoleIdName", "test_package#xxx00:ADMIN"), hasEntry("grantedRoleIdName", "test_package#xxx00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-xxx00@xxx.example.com") hasEntry("granteeSubjectName", "pac-admin-xxx00@xxx.example.com")
) )
)) ))
.body("", hasItem( .body("", hasItem(
allOf( allOf(
hasEntry("grantedByRoleIdName", "test_customer#zzz:ADMIN"), hasEntry("grantedByRoleIdName", "test_customer#zzz:ADMIN"),
hasEntry("grantedRoleIdName", "test_package#zzz02:ADMIN"), hasEntry("grantedRoleIdName", "test_package#zzz02:ADMIN"),
hasEntry("granteeUserName", "pac-admin-zzz02@zzz.example.com") hasEntry("granteeSubjectName", "pac-admin-zzz02@zzz.example.com")
) )
)) ))
.body("size()", greaterThanOrEqualTo(14)); .body("size()", greaterThanOrEqualTo(14));
@ -125,7 +125,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
allOf( allOf(
hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"), hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"),
hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"), hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com") hasEntry("granteeSubjectName", "pac-admin-yyy00@yyy.example.com")
) )
)) ))
.body("size()", is(1)); .body("size()", is(1));
@ -147,12 +147,12 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
allOf( allOf(
hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"), hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"),
hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"), hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com") hasEntry("granteeSubjectName", "pac-admin-yyy00@yyy.example.com")
) )
)) ))
.body("[0].grantedByRoleIdName", is("test_customer#yyy:ADMIN")) .body("[0].grantedByRoleIdName", is("test_customer#yyy:ADMIN"))
.body("[0].grantedRoleIdName", is("test_package#yyy00:ADMIN")) .body("[0].grantedRoleIdName", is("test_package#yyy00:ADMIN"))
.body("[0].granteeUserName", is("pac-admin-yyy00@yyy.example.com")); .body("[0].granteeSubjectName", is("pac-admin-yyy00@yyy.example.com"));
// @formatter:on // @formatter:on
} }
} }
@ -164,7 +164,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
void customerAdmin_withAssumedPacketAdminRole_canReadPacketAdminsGrantById() { void customerAdmin_withAssumedPacketAdminRole_canReadPacketAdminsGrantById() {
// given // given
final var givencurrentSubjectAsPackageAdmin = new Subject("customer-admin@xxx.example.com"); final var givencurrentSubjectAsPackageAdmin = new Subject("customer-admin@xxx.example.com");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenGranteeUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN"); final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when // when
@ -176,14 +176,14 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.statusCode(200) .statusCode(200)
.body("grantedByRoleIdName", is("test_customer#xxx:ADMIN")) .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
.body("grantedRoleIdName", is("test_package#xxx00:ADMIN")) .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com")); .body("granteeSubjectName", is("pac-admin-xxx00@xxx.example.com"));
} }
@Test @Test
void packageAdmin_withoutAssumedRole_canReadItsOwnGrantById() { void packageAdmin_withoutAssumedRole_canReadItsOwnGrantById() {
// given // given
final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com"); final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenGranteeUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN"); final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when // when
@ -195,7 +195,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.statusCode(200) .statusCode(200)
.body("grantedByRoleIdName", is("test_customer#xxx:ADMIN")) .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
.body("grantedRoleIdName", is("test_package#xxx00:ADMIN")) .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com")); .body("granteeSubjectName", is("pac-admin-xxx00@xxx.example.com"));
} }
@Test @Test
@ -204,7 +204,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
final var givencurrentSubjectAsPackageAdmin = new Subject( final var givencurrentSubjectAsPackageAdmin = new Subject(
"pac-admin-xxx00@xxx.example.com", "pac-admin-xxx00@xxx.example.com",
"test_package#xxx00:ADMIN"); "test_package#xxx00:ADMIN");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenGranteeUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN"); final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when // when
@ -216,7 +216,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.statusCode(200) .statusCode(200)
.body("grantedByRoleIdName", is("test_customer#xxx:ADMIN")) .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
.body("grantedRoleIdName", is("test_package#xxx00:ADMIN")) .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com")); .body("granteeSubjectName", is("pac-admin-xxx00@xxx.example.com"));
} }
@Test @Test
@ -226,7 +226,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
final var givencurrentSubjectAsPackageAdmin = new Subject( final var givencurrentSubjectAsPackageAdmin = new Subject(
"pac-admin-xxx00@xxx.example.com", "pac-admin-xxx00@xxx.example.com",
"test_package#xxx00:TENANT"); "test_package#xxx00:TENANT");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenGranteeUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN"); final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
final var grant = givencurrentSubjectAsPackageAdmin.getGrantById() final var grant = givencurrentSubjectAsPackageAdmin.getGrantById()
.forGrantedRole(givenGrantedRole).toGranteeUser(givenGranteeUser); .forGrantedRole(givenGrantedRole).toGranteeUser(givenGranteeUser);
@ -244,7 +244,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
void packageAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() { void packageAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() {
// given // given
final var givenNewUser = createRBacUser(); final var givenNewUser = createRbacSubject();
final var givenRoleToGrant = "test_package#xxx00:ADMIN"; final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant); final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
final var givenOwnPackageAdminRole = final var givenOwnPackageAdminRole =
@ -261,7 +261,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.body("grantedByRoleIdName", is("test_package#xxx00:ADMIN")) .body("grantedByRoleIdName", is("test_package#xxx00:ADMIN"))
.body("assumed", is(true)) .body("assumed", is(true))
.body("grantedRoleIdName", is("test_package#xxx00:ADMIN")) .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is(givenNewUser.getName())); .body("granteeSubjectName", is(givenNewUser.getName()));
assertThat(findAllGrantsOf(givencurrentSubjectAsPackageAdmin)) assertThat(findAllGrantsOf(givencurrentSubjectAsPackageAdmin))
.extracting(RbacGrantEntity::toDisplay) .extracting(RbacGrantEntity::toDisplay)
.contains("{ grant role:" + givenOwnPackageAdminRole.getRoleName() + .contains("{ grant role:" + givenOwnPackageAdminRole.getRoleName() +
@ -273,7 +273,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
void packageAdmin_canNotGrantAlienPackageAdminRole_toArbitraryUser() { void packageAdmin_canNotGrantAlienPackageAdminRole_toArbitraryUser() {
// given // given
final var givenNewUser = createRBacUser(); final var givenNewUser = createRbacSubject();
final var givenRoleToGrant = "test_package#xxx00:ADMIN"; final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant); final var givencurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
final var givenAlienPackageAdminRole = getRbacRoleByName("test_package#yyy00:ADMIN"); final var givenAlienPackageAdminRole = getRbacRoleByName("test_package#yyy00:ADMIN");
@ -289,7 +289,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.body("message", containsString("Access to granted role")) .body("message", containsString("Access to granted role"))
.body("message", containsString("forbidden for test_package#xxx00:ADMIN")); .body("message", containsString("forbidden for test_package#xxx00:ADMIN"));
assertThat(findAllGrantsOf(givencurrentSubjectAsPackageAdmin)) assertThat(findAllGrantsOf(givencurrentSubjectAsPackageAdmin))
.extracting(RbacGrantEntity::getGranteeUserName) .extracting(RbacGrantEntity::getGranteeSubjectName)
.doesNotContain(givenNewUser.getName()); .doesNotContain(givenNewUser.getName());
} }
} }
@ -302,7 +302,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
void packageAdmin_canRevokePackageAdminRole_grantedByPackageAdmin_fromArbitraryUser() { void packageAdmin_canRevokePackageAdminRole_grantedByPackageAdmin_fromArbitraryUser() {
// given // given
final var givenArbitraryUser = createRBacUser(); final var givenArbitraryUser = createRbacSubject();
final var givenRoleToGrant = "test_package#xxx00:ADMIN"; final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givenCurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant); final var givenCurrentSubjectAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
final var givenOwnPackageAdminRole = getRbacRoleByName("test_package#xxx00:ADMIN"); final var givenOwnPackageAdminRole = getRbacRoleByName("test_package#xxx00:ADMIN");
@ -326,7 +326,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// then // then
revokeResponse.assertThat().statusCode(204); revokeResponse.assertThat().statusCode(204);
assertThat(findAllGrantsOf(givenCurrentSubjectAsPackageAdmin)) assertThat(findAllGrantsOf(givenCurrentSubjectAsPackageAdmin))
.extracting(RbacGrantEntity::getGranteeUserName) .extracting(RbacGrantEntity::getGranteeSubjectName)
.doesNotContain(givenArbitraryUser.getName()); .doesNotContain(givenArbitraryUser.getName());
} }
} }
@ -366,7 +366,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
private Subject grantingSubject = Subject.this; private Subject grantingSubject = Subject.this;
private final RbacRoleEntity grantedRole; private final RbacRoleEntity grantedRole;
private boolean assumed; private boolean assumed;
private RbacUserEntity granteeUser; private RbacSubjectEntity granteeUser;
public GrantFixture(final RbacRoleEntity roleToGrant) { public GrantFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant; this.grantedRole = roleToGrant;
@ -377,7 +377,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
return this; return this;
} }
ValidatableResponse toUser(final RbacUserEntity granteeUser) { ValidatableResponse toUser(final RbacSubjectEntity granteeUser) {
this.granteeUser = granteeUser; this.granteeUser = granteeUser;
return RestAssured // @formatter:ff return RestAssured // @formatter:ff
@ -407,13 +407,13 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
private Subject currentSubject = Subject.this; private Subject currentSubject = Subject.this;
private final RbacRoleEntity grantedRole; private final RbacRoleEntity grantedRole;
private boolean assumed; private boolean assumed;
private RbacUserEntity granteeUser; private RbacSubjectEntity granteeUser;
public RevokeFixture(final RbacRoleEntity roleToGrant) { public RevokeFixture(final RbacRoleEntity roleToGrant) {
this.grantedRole = roleToGrant; this.grantedRole = roleToGrant;
} }
ValidatableResponse fromUser(final RbacUserEntity granteeUser) { ValidatableResponse fromUser(final RbacSubjectEntity granteeUser) {
this.granteeUser = granteeUser; this.granteeUser = granteeUser;
return RestAssured // @formatter:ff return RestAssured // @formatter:ff
@ -450,7 +450,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
return this; return this;
} }
ValidatableResponse toGranteeUser(final RbacUserEntity granteeUser) { ValidatableResponse toGranteeUser(final RbacSubjectEntity granteeUser) {
return RestAssured // @formatter:ff return RestAssured // @formatter:ff
.given() .given()
@ -480,18 +480,18 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
}).returnedValue(); }).returnedValue();
} }
RbacUserEntity createRBacUser() { RbacSubjectEntity createRbacSubject() {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
final String newUserName = "test-user-" + RandomStringUtils.randomAlphabetic(8) + "@example.com"; final String newUserName = "test-user-" + RandomStringUtils.randomAlphabetic(8) + "@example.com";
context(null); context(null);
return rbacUserRepository.create(new RbacUserEntity(UUID.randomUUID(), newUserName)); return rbacSubjectRepository.create(new RbacSubjectEntity(UUID.randomUUID(), newUserName));
}).returnedValue(); }).returnedValue();
} }
RbacUserEntity findRbacUserByName(final String userName) { RbacSubjectEntity findRbacSubjectByName(final String userName) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
return rbacUserRepository.findByName(userName); return rbacSubjectRepository.findByName(userName);
}).assertNotNull().returnedValue(); }).assertNotNull().returnedValue();
} }

View File

@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.rbac.rbacgrant;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserEntity; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -42,7 +42,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
RawRbacGrantRepository rawRbacGrantRepository; RawRbacGrantRepository rawRbacGrantRepository;
@Autowired @Autowired
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Autowired @Autowired
RbacRoleRepository rbacRoleRepository; RbacRoleRepository rbacRoleRepository;
@ -109,7 +109,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
public void customerAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() { public void customerAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() {
// given // given
context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN"); context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
final var givenArbitrarySubjectUuid = rbacUserRepository.findByName("pac-admin-zzz00@zzz.example.com").getUuid(); final var givenArbitrarySubjectUuid = rbacSubjectRepository.findByName("pac-admin-zzz00@zzz.example.com").getUuid();
final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName("test_package#xxx00:ADMIN").getUuid(); final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName("test_package#xxx00:ADMIN").getUuid();
// when // when
@ -133,7 +133,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
public void packageAdmin_canNotGrantPackageOwnerRole() { public void packageAdmin_canNotGrantPackageOwnerRole() {
// given // given
record Given(RbacUserEntity arbitraryUser, UUID packageOwnerRoleUuid) {} record Given(RbacSubjectEntity arbitraryUser, UUID packageOwnerRoleUuid) {}
final var given = jpaAttempt.transacted(() -> { final var given = jpaAttempt.transacted(() -> {
// to find the uuids of we need to have access rights to these // to find the uuids of we need to have access rights to these
context("customer-admin@xxx.example.com", null); context("customer-admin@xxx.example.com", null);
@ -188,7 +188,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN"); context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull(); assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull();
assertThat(rbacGrantRepository.findAll()) assertThat(rbacGrantRepository.findAll())
.extracting(RbacGrantEntity::getGranteeUserName) .extracting(RbacGrantEntity::getGranteeSubjectName)
.doesNotContain("pac-admin-zzz00@zzz.example.com"); .doesNotContain("pac-admin-zzz00@zzz.example.com");
} }
@ -209,7 +209,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull(); assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull();
context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN"); context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
assertThat(rbacGrantRepository.findAll()) assertThat(rbacGrantRepository.findAll())
.extracting(RbacGrantEntity::getGranteeUserName) .extracting(RbacGrantEntity::getGranteeSubjectName)
.doesNotContain("pac-admin-zzz00@zzz.example.com"); .doesNotContain("pac-admin-zzz00@zzz.example.com");
} }
@ -236,7 +236,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
private RbacGrantEntity create(GrantBuilder with) { private RbacGrantEntity create(GrantBuilder with) {
context(with.byUserName, with.assumedRole); context(with.byUserName, with.assumedRole);
final var givenArbitrarySubjectUuid = rbacUserRepository.findByName(with.granteeUserName).getUuid(); final var givenArbitrarySubjectUuid = rbacSubjectRepository.findByName(with.granteeSubjectName).getUuid();
final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName(with.grantedRole).getUuid(); final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName(with.grantedRole).getUuid();
final var grant = RbacGrantEntity.builder() final var grant = RbacGrantEntity.builder()
@ -251,7 +251,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
assertThat(rawRbacGrantRepository.findAll()) assertThat(rawRbacGrantRepository.findAll())
.extracting(RawRbacGrantEntity::toDisplay) .extracting(RawRbacGrantEntity::toDisplay)
.contains("{ grant role:%s to user:%s by %s and assume }".formatted( .contains("{ grant role:%s to user:%s by %s and assume }".formatted(
with.grantedRole, with.granteeUserName, with.assumedRole with.grantedRole, with.granteeSubjectName, with.assumedRole
)); ));
return grant; return grant;
@ -266,7 +266,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
String byUserName; String byUserName;
String assumedRole = ""; String assumedRole = "";
String grantedRole; String grantedRole;
String granteeUserName; String granteeSubjectName;
GrantBuilder byUser(final String userName) { GrantBuilder byUser(final String userName) {
byUserName = userName; byUserName = userName;
@ -284,28 +284,28 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
} }
GrantBuilder toUser(final String toUser) { GrantBuilder toUser(final String toUser) {
this.granteeUserName = toUser; this.granteeSubjectName = toUser;
return this; return this;
} }
} }
} }
private RbacUserEntity createNewUserTransacted() { private RbacSubjectEntity createNewUserTransacted() {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
final var newUserName = "test-user-" + System.currentTimeMillis() + "@example.com"; final var newUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
context(null); context(null);
return rbacUserRepository.create(new RbacUserEntity(null, newUserName)); return rbacSubjectRepository.create(new RbacSubjectEntity(null, newUserName));
}).assumeSuccessful().returnedValue(); }).assumeSuccessful().returnedValue();
} }
private RbacUserEntity createNewUser() { private RbacSubjectEntity createNewUser() {
return rbacUserRepository.create( return rbacSubjectRepository.create(
new RbacUserEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com")); new RbacSubjectEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com"));
} }
void exactlyTheseRbacGrantsAreReturned(final List<RbacGrantEntity> actualResult, final String... expectedGrant) { void exactlyTheseRbacGrantsAreReturned(final List<RbacGrantEntity> actualResult, final String... expectedGrant) {
assertThat(actualResult) assertThat(actualResult)
.filteredOn(g -> !g.getGranteeUserName().startsWith("test-user-")) // ignore test-users created by other tests .filteredOn(g -> !g.getGranteeSubjectName().startsWith("test-user-")) // ignore test-users created by other tests
.extracting(RbacGrantEntity::toDisplay) .extracting(RbacGrantEntity::toDisplay)
.containsExactlyInAnyOrder(expectedGrant); .containsExactlyInAnyOrder(expectedGrant);
} }

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.rbac.rbacrole;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@ -24,7 +24,7 @@ class RbacRoleControllerAcceptanceTest {
Context context; Context context;
@Autowired @Autowired
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Autowired @Autowired
RbacRoleRepository rbacRoleRepository; RbacRoleRepository rbacRoleRepository;

View File

@ -1,14 +0,0 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import static java.util.UUID.randomUUID;
public class TestRbacUser {
static final RbacUserEntity userxxx = rbacRole("customer-admin@xxx.example.com");
static final RbacUserEntity userBbb = rbacRole("customer-admin@bbb.example.com");
static public RbacUserEntity rbacRole(final String userName) {
return new RbacUserEntity(randomUUID(), userName);
}
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@ -22,7 +22,7 @@ import static org.hamcrest.Matchers.*;
classes = { HsadminNgApplication.class, JpaAttempt.class } classes = { HsadminNgApplication.class, JpaAttempt.class }
) )
@Transactional @Transactional
class RbacUserControllerAcceptanceTest { class RbacSubjectControllerAcceptanceTest {
@LocalServerPort @LocalServerPort
private Integer port; private Integer port;
@ -34,10 +34,10 @@ class RbacUserControllerAcceptanceTest {
Context context; Context context;
@Autowired @Autowired
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Nested @Nested
class CreateRbacUser { class CreateRbacSubject {
@Test @Test
void anybody_canCreateANewUser() { void anybody_canCreateANewUser() {
@ -53,7 +53,7 @@ class RbacUserControllerAcceptanceTest {
""") """)
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/rbac/users") .post("http://localhost/api/rbac/subjects")
.then().assertThat() .then().assertThat()
.statusCode(201) .statusCode(201)
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
@ -66,17 +66,17 @@ class RbacUserControllerAcceptanceTest {
final var newSubjectUuid = UUID.fromString( final var newSubjectUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1)); location.substring(location.lastIndexOf('/') + 1));
context.define("new-user@example.com"); context.define("new-user@example.com");
assertThat(rbacUserRepository.findByUuid(newSubjectUuid)) assertThat(rbacSubjectRepository.findByUuid(newSubjectUuid))
.extracting(RbacUserEntity::getName).isEqualTo("new-user@example.com"); .extracting(RbacSubjectEntity::getName).isEqualTo("new-user@example.com");
} }
} }
@Nested @Nested
class GetRbacUser { class GetRbacSubject {
@Test @Test
void globalAdmin_withoutAssumedRole_canGetArbitraryUser() { void globalAdmin_withoutAssumedRole_canGetArbitraryUser() {
final var givenUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -84,7 +84,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid()) .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -94,7 +94,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void globalAdmin_withAssumedCustomerAdminRole_canGetUserWithinInItsRealm() { void globalAdmin_withAssumedCustomerAdminRole_canGetUserWithinInItsRealm() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -103,7 +103,7 @@ class RbacUserControllerAcceptanceTest {
.header("assumed-roles", "test_customer#yyy:ADMIN") .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid()) .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -113,7 +113,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void customerAdmin_withoutAssumedRole_canGetUserWithinInItsRealm() { void customerAdmin_withoutAssumedRole_canGetUserWithinInItsRealm() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -121,7 +121,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "customer-admin@yyy.example.com") .header("current-subject", "customer-admin@yyy.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid()) .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -131,7 +131,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void customerAdmin_withoutAssumedRole_canNotGetUserOutsideOfItsRealm() { void customerAdmin_withoutAssumedRole_canNotGetUserOutsideOfItsRealm() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -139,7 +139,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "customer-admin@xxx.example.com") .header("current-subject", "customer-admin@xxx.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid()) .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(404); .statusCode(404);
// @formatter:on // @formatter:on
@ -147,7 +147,7 @@ class RbacUserControllerAcceptanceTest {
} }
@Nested @Nested
class ListRbacUsers { class ListRbacSubjects {
@Test @Test
void globalAdmin_withoutAssumedRole_canViewAllUsers() { void globalAdmin_withoutAssumedRole_canViewAllUsers() {
@ -158,7 +158,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users") .get("http://localhost/api/rbac/subjects")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -183,7 +183,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users?name=pac-admin-zzz0") .get("http://localhost/api/rbac/subjects?name=pac-admin-zzz0")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -204,7 +204,7 @@ class RbacUserControllerAcceptanceTest {
.header("assumed-roles", "test_customer#yyy:ADMIN") .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users") .get("http://localhost/api/rbac/subjects")
.then().assertThat() .then().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -225,7 +225,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "customer-admin@yyy.example.com") .header("current-subject", "customer-admin@yyy.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users") .get("http://localhost/api/rbac/subjects")
.then().assertThat() .then().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -246,7 +246,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "pac-admin-xxx01@xxx.example.com") .header("current-subject", "pac-admin-xxx01@xxx.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users") .get("http://localhost/api/rbac/subjects")
.then().assertThat() .then().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -257,11 +257,11 @@ class RbacUserControllerAcceptanceTest {
} }
@Nested @Nested
class ListRbacUserPermissions { class ListRbacSubjectPermissions {
@Test @Test
void globalAdmin_withoutAssumedRole_canViewArbitraryUsersPermissions() { void globalAdmin_withoutAssumedRole_canViewArbitraryUsersPermissions() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -269,7 +269,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions") .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid() + "/permissions")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -290,7 +290,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void globalAdmin_withAssumedCustomerAdminRole_canViewArbitraryUsersPermissions() { void globalAdmin_withAssumedCustomerAdminRole_canViewArbitraryUsersPermissions() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -299,7 +299,7 @@ class RbacUserControllerAcceptanceTest {
.header("assumed-roles", "test_customer#yyy:ADMIN") .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions") .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid() + "/permissions")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -320,7 +320,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void packageAdmin_withoutAssumedRole_canViewPermissionsOfUsersInItsRealm() { void packageAdmin_withoutAssumedRole_canViewPermissionsOfUsersInItsRealm() {
final var givenUser = findRbacUserByName("pac-admin-yyy00@yyy.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-yyy00@yyy.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -328,7 +328,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "pac-admin-yyy00@yyy.example.com") .header("current-subject", "pac-admin-yyy00@yyy.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions") .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid() + "/permissions")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -349,7 +349,7 @@ class RbacUserControllerAcceptanceTest {
@Test @Test
void packageAdmin_canViewPermissionsOfUsersOutsideOfItsRealm() { void packageAdmin_canViewPermissionsOfUsersOutsideOfItsRealm() {
final var givenUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com"); final var givenUser = findRbacSubjectByName("pac-admin-xxx00@xxx.example.com");
// @formatter:off // @formatter:off
RestAssured RestAssured
@ -357,7 +357,7 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "pac-admin-yyy00@yyy.example.com") .header("current-subject", "pac-admin-yyy00@yyy.example.com")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions") .get("http://localhost/api/rbac/subjects/" + givenUser.getUuid() + "/permissions")
.then().log().body().assertThat() .then().log().body().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -367,7 +367,7 @@ class RbacUserControllerAcceptanceTest {
} }
@Nested @Nested
class DeleteRbacUser { class DeleteRbacSubject {
@Test @Test
void anybody_canDeleteTheirOwnUser() { void anybody_canDeleteTheirOwnUser() {
@ -381,13 +381,13 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", givenUser.getName()) .header("current-subject", givenUser.getName())
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/rbac/users/" + givenUser.getUuid()) .delete("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(204); .statusCode(204);
// @formatter:on // @formatter:on
// finally, the user is actually deleted // finally, the user is actually deleted
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNull();
} }
@Test @Test
@ -402,14 +402,14 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "customer-admin@xxx.example.com") .header("current-subject", "customer-admin@xxx.example.com")
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/rbac/users/" + givenUser.getUuid()) .delete("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().all().assertThat() .then().log().all().assertThat()
// that user cannot even see other users, thus the system won't even try to delete // that user cannot even see other users, thus the system won't even try to delete
.statusCode(204); .statusCode(204);
// @formatter:on // @formatter:on
// finally, the user is still there // finally, the user is still there
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNotNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNotNull();
} }
@Test @Test
@ -424,30 +424,30 @@ class RbacUserControllerAcceptanceTest {
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.delete("http://localhost/api/rbac/users/" + givenUser.getUuid()) .delete("http://localhost/api/rbac/subjects/" + givenUser.getUuid())
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(204); .statusCode(204);
// @formatter:on // @formatter:on
// finally, the user is actually deleted // finally, the user is actually deleted
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNull();
} }
} }
RbacUserEntity findRbacUserByName(final String userName) { RbacSubjectEntity findRbacSubjectByName(final String userName) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
return rbacUserRepository.findByName(userName); return rbacSubjectRepository.findByName(userName);
}).returnedValue(); }).returnedValue();
} }
RbacUserEntity givenANewUser() { RbacSubjectEntity givenANewUser() {
final var givenUserName = "test-user-" + System.currentTimeMillis() + "@example.com"; final var givenUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
final var givenUser = jpaAttempt.transacted(() -> { final var givenUser = jpaAttempt.transacted(() -> {
context.define(null); context.define(null);
return rbacUserRepository.create(new RbacUserEntity(UUID.randomUUID(), givenUserName)); return rbacSubjectRepository.create(new RbacSubjectEntity(UUID.randomUUID(), givenUserName));
}).assumeSuccessful().returnedValue(); }).assumeSuccessful().returnedValue();
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNotNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNotNull();
return givenUser; return givenUser;
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
@ -30,10 +30,10 @@ import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(RbacUserController.class) @WebMvcTest(RbacSubjectController.class)
@Import(Mapper.class) @Import(Mapper.class)
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
class RbacUserControllerRestTest { class RbacSubjectControllerRestTest {
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@ -42,7 +42,7 @@ class RbacUserControllerRestTest {
Context contextMock; Context contextMock;
@MockBean @MockBean
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Mock @Mock
EntityManager em; EntityManager em;
@ -59,13 +59,13 @@ class RbacUserControllerRestTest {
} }
@Test @Test
void createUserUsesGivenUuid() throws Exception { void createSubjectUsesGivenUuid() throws Exception {
// given // given
final var givenUuid = UUID.randomUUID(); final var givenUuid = UUID.randomUUID();
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
.post("/api/rbac/users") .post("/api/rbac/subjects")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content(""" .content("""
{ {
@ -79,14 +79,14 @@ class RbacUserControllerRestTest {
.andExpect(jsonPath("uuid", is(givenUuid.toString()))); .andExpect(jsonPath("uuid", is(givenUuid.toString())));
// then // then
verify(rbacUserRepository).create(argThat(entity -> entity.getUuid().equals(givenUuid))); verify(rbacSubjectRepository).create(argThat(entity -> entity.getUuid().equals(givenUuid)));
} }
@Test @Test
void createUserGeneratesRandomUuidIfNotGiven() throws Exception { void createSubjectGeneratesRandomUuidIfNotGiven() throws Exception {
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
.post("/api/rbac/users") .post("/api/rbac/subjects")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content("{}") .content("{}")
.accept(MediaType.APPLICATION_JSON)) .accept(MediaType.APPLICATION_JSON))
@ -96,6 +96,6 @@ class RbacUserControllerRestTest {
.andExpect(jsonPath("uuid", isUuidValid())); .andExpect(jsonPath("uuid", isUuidValid()));
// then // then
verify(rbacUserRepository).create(argThat(entity -> entity.getUuid() != null)); verify(rbacSubjectRepository).create(argThat(entity -> entity.getUuid() != null));
} }
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -9,9 +9,9 @@ import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
class RbacUserEntityUnitTest { class RbacSubjectEntityUnitTest {
RbacUserEntity givenUser = new RbacUserEntity(UUID.randomUUID(), "test@example.org"); RbacSubjectEntity givenUser = new RbacSubjectEntity(UUID.randomUUID(), "test@example.org");
@Test @Test
void generatedAccessCodeMatchesDefinedPattern() { void generatedAccessCodeMatchesDefinedPattern() {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.rbacuser; package net.hostsharing.hsadminng.rbac.subject;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
@ -26,10 +26,10 @@ import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest @DataJpaTest
@Import( { Context.class, JpaAttempt.class }) @Import( { Context.class, JpaAttempt.class })
class RbacUserRepositoryIntegrationTest extends ContextBasedTest { class RbacSubjectRepositoryIntegrationTest extends ContextBasedTest {
@Autowired @Autowired
RbacUserRepository rbacUserRepository; RbacSubjectRepository rbacSubjectRepository;
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@ -41,7 +41,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested
class CreateUser { class CreateSubject {
@Test @Test
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
@ -54,35 +54,35 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
// when: // when:
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context(null); context(null);
return rbacUserRepository.create(new RbacUserEntity(givenUuid, newUserName)); return rbacSubjectRepository.create(new RbacSubjectEntity(givenUuid, newUserName));
}); });
// then: // then:
assertThat(result.wasSuccessful()).isTrue(); assertThat(result.wasSuccessful()).isTrue();
assertThat(result.returnedValue()).isNotNull() assertThat(result.returnedValue()).isNotNull()
.extracting(RbacUserEntity::getUuid).isEqualTo(givenUuid); .extracting(RbacSubjectEntity::getUuid).isEqualTo(givenUuid);
assertThat(rbacUserRepository.findByName(result.returnedValue().getName())).isNotNull(); assertThat(rbacSubjectRepository.findByName(result.returnedValue().getName())).isNotNull();
} }
} }
@Nested @Nested
class DeleteUser { class DeleteSubject {
@Test @Test
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
public void anyoneCanDeleteTheirOwnUser() { public void anyoneCanDeleteTheirOwnUser() {
// given // given
final RbacUserEntity givenUser = givenANewUser(); final RbacSubjectEntity givenUser = givenANewSubject();
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context(givenUser.getName()); context(givenUser.getName());
rbacUserRepository.deleteByUuid(givenUser.getUuid()); rbacSubjectRepository.deleteByUuid(givenUser.getUuid());
}); });
// then the user is deleted // then the user is deleted
result.assertSuccessful(); result.assertSuccessful();
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNull();
} }
} }
@ -102,27 +102,27 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
); );
@Test @Test
public void globalAdmin_withoutAssumedRole_canViewAllRbacUsers() { public void globalAdmin_withoutAssumedRole_canViewAllRbacSubjects() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
// when // when
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
// then // then
allTheseRbacUsersAreReturned(result, ALL_TEST_DATA_USERS); allTheseRbacSubjectsAreReturned(result, ALL_TEST_DATA_USERS);
} }
@Test @Test
public void globalAdmin_withAssumedglobalAdminRole_canViewAllRbacUsers() { public void globalAdmin_withAssumedglobalAdminRole_canViewAllRbacSubjects() {
given: given:
context("superuser-alex@hostsharing.net", "global#global:ADMIN"); context("superuser-alex@hostsharing.net", "global#global:ADMIN");
// when // when
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
then: then:
allTheseRbacUsersAreReturned(result, ALL_TEST_DATA_USERS); allTheseRbacSubjectsAreReturned(result, ALL_TEST_DATA_USERS);
} }
@Test @Test
@ -131,10 +131,10 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net", "test_customer#xxx:ADMIN"); context("superuser-alex@hostsharing.net", "test_customer#xxx:ADMIN");
// when // when
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
then: then:
exactlyTheseRbacUsersAreReturned( exactlyTheseRbacSubjectsAreReturned(
result, result,
"customer-admin@xxx.example.com", "customer-admin@xxx.example.com",
"pac-admin-xxx00@xxx.example.com", "pac-admin-xxx01@xxx.example.com", "pac-admin-xxx02@xxx.example.com" "pac-admin-xxx00@xxx.example.com", "pac-admin-xxx01@xxx.example.com", "pac-admin-xxx02@xxx.example.com"
@ -147,10 +147,10 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com"); context("customer-admin@xxx.example.com");
// when: // when:
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
// then: // then:
exactlyTheseRbacUsersAreReturned( exactlyTheseRbacSubjectsAreReturned(
result, result,
"customer-admin@xxx.example.com", "customer-admin@xxx.example.com",
"pac-admin-xxx00@xxx.example.com", "pac-admin-xxx01@xxx.example.com", "pac-admin-xxx02@xxx.example.com" "pac-admin-xxx00@xxx.example.com", "pac-admin-xxx01@xxx.example.com", "pac-admin-xxx02@xxx.example.com"
@ -161,24 +161,24 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyUsersHavingRolesInThatPackage() { public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyUsersHavingRolesInThatPackage() {
context("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN"); context("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
exactlyTheseRbacUsersAreReturned(result, "pac-admin-xxx00@xxx.example.com"); exactlyTheseRbacSubjectsAreReturned(result, "pac-admin-xxx00@xxx.example.com");
} }
@Test @Test
public void packageAdmin_withoutAssumedRole_canViewOnlyUsersHavingRolesInThatPackage() { public void packageAdmin_withoutAssumedRole_canViewOnlyUsersHavingRolesInThatPackage() {
context("pac-admin-xxx00@xxx.example.com"); context("pac-admin-xxx00@xxx.example.com");
final var result = rbacUserRepository.findByOptionalNameLike(null); final var result = rbacSubjectRepository.findByOptionalNameLike(null);
exactlyTheseRbacUsersAreReturned(result, "pac-admin-xxx00@xxx.example.com"); exactlyTheseRbacSubjectsAreReturned(result, "pac-admin-xxx00@xxx.example.com");
} }
} }
@Nested @Nested
class ListUserPermissions { class ListSubjectPermissions {
private static final String[] ALL_USER_PERMISSIONS = Array.of( private static final String[] ALL_USER_PERMISSIONS = Array.of(
// @formatter:off // @formatter:off
@ -232,9 +232,9 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
// when // when
final var result = rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid("superuser-fran@hostsharing.net")) final var result = rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid("superuser-fran@hostsharing.net"))
.stream().filter(p -> p.getObjectTable().contains("test_")) .stream().filter(p -> p.getObjectTable().contains("test_"))
.sorted(comparing(RbacUserPermission::toString)).toList(); .sorted(comparing(RbacSubjectPermission::toString)).toList();
// then // then
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS); allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
@ -246,7 +246,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com"); context("customer-admin@xxx.example.com");
// when // when
final var result = rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid("customer-admin@xxx.example.com")); final var result = rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid("customer-admin@xxx.example.com"));
// then // then
allTheseRbacPermissionsAreReturned( allTheseRbacPermissionsAreReturned(
@ -290,7 +290,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
// when // when
final var result = attempt(em, () -> final var result = attempt(em, () ->
rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid) rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid)
); );
// then // then
@ -306,7 +306,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com"); context("customer-admin@xxx.example.com");
// when // when
final var result = rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-xxx00@xxx.example.com")); final var result = rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-xxx00@xxx.example.com"));
// then // then
allTheseRbacPermissionsAreReturned( allTheseRbacPermissionsAreReturned(
@ -342,7 +342,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com"); context("customer-admin@xxx.example.com");
// when // when
final var result = rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-yyy00@yyy.example.com")); final var result = rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-yyy00@yyy.example.com"));
// then // then
noRbacPermissionsAreReturned(result); noRbacPermissionsAreReturned(result);
@ -354,7 +354,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("pac-admin-xxx00@xxx.example.com"); context("pac-admin-xxx00@xxx.example.com");
// when // when
final var result = rbacUserRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-xxx00@xxx.example.com")); final var result = rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid("pac-admin-xxx00@xxx.example.com"));
// then // then
allTheseRbacPermissionsAreReturned( allTheseRbacPermissionsAreReturned(
@ -386,50 +386,42 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
} }
UUID subjectUuid(final String userName) { UUID subjectUuid(final String userName) {
return rbacUserRepository.findByName(userName).getUuid(); return rbacSubjectRepository.findByName(userName).getUuid();
} }
RbacUserEntity givenANewUser() { RbacSubjectEntity givenANewSubject() {
final var givenUserName = "test-user-" + System.currentTimeMillis() + "@example.com"; final var givenUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
final var givenUser = jpaAttempt.transacted(() -> { final var givenUser = jpaAttempt.transacted(() -> {
context(null); context(null);
return rbacUserRepository.create(new RbacUserEntity(UUID.randomUUID(), givenUserName)); return rbacSubjectRepository.create(new RbacSubjectEntity(UUID.randomUUID(), givenUserName));
}).assumeSuccessful().returnedValue(); }).assumeSuccessful().returnedValue();
assertThat(rbacUserRepository.findByName(givenUser.getName())).isNotNull(); assertThat(rbacSubjectRepository.findByName(givenUser.getName())).isNotNull();
return givenUser; return givenUser;
} }
void exactlyTheseRbacUsersAreReturned(final List<RbacUserEntity> actualResult, final String... expectedUserNames) { void exactlyTheseRbacSubjectsAreReturned(final List<RbacSubjectEntity> actualResult, final String... expectedUserNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(RbacUserEntity::getName) .extracting(RbacSubjectEntity::getName)
.filteredOn(n -> !n.startsWith("test-user")) .filteredOn(n -> !n.startsWith("test-user"))
.containsExactlyInAnyOrder(expectedUserNames); .containsExactlyInAnyOrder(expectedUserNames);
} }
void allTheseRbacUsersAreReturned(final List<RbacUserEntity> actualResult, final String... expectedUserNames) { void allTheseRbacSubjectsAreReturned(final List<RbacSubjectEntity> actualResult, final String... expectedUserNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(RbacUserEntity::getName) .extracting(RbacSubjectEntity::getName)
.filteredOn(n -> !n.startsWith("test-user")) .filteredOn(n -> !n.startsWith("test-user"))
.contains(expectedUserNames); .contains(expectedUserNames);
} }
void noRbacPermissionsAreReturned( void noRbacPermissionsAreReturned(
final List<RbacUserPermission> actualResult) { final List<RbacSubjectPermission> actualResult) {
assertThat(actualResult) assertThat(actualResult)
.extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp()) .extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp())
.containsExactlyInAnyOrder(); .containsExactlyInAnyOrder();
} }
void exactlyTheseRbacPermissionsAreReturned(
final List<RbacUserPermission> actualResult,
final String... expectedRoleNames) {
assertThat(actualResult)
.extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp())
.containsExactlyInAnyOrder(expectedRoleNames);
}
void allTheseRbacPermissionsAreReturned( void allTheseRbacPermissionsAreReturned(
final List<RbacUserPermission> actualResult, final List<RbacSubjectPermission> actualResult,
final String... expectedRoleNames) { final String... expectedRoleNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp() .extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp()
@ -438,7 +430,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
} }
void noneOfTheseRbacPermissionsAreReturned( void noneOfTheseRbacPermissionsAreReturned(
final List<RbacUserPermission> actualResult, final List<RbacSubjectPermission> actualResult,
final String... unexpectedRoleNames) { final String... unexpectedRoleNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp()) .extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp())

View File

@ -0,0 +1,14 @@
package net.hostsharing.hsadminng.rbac.subject;
import static java.util.UUID.randomUUID;
public class TestRbacSubject {
static final RbacSubjectEntity userxxx = rbacRole("customer-admin@xxx.example.com");
static final RbacSubjectEntity userBbb = rbacRole("customer-admin@bbb.example.com");
static public RbacSubjectEntity rbacRole(final String userName) {
return new RbacSubjectEntity(randomUUID(), userName);
}
}