diff --git a/build.gradle b/build.gradle index 41c2f155..2a495ee8 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,8 @@ configurations { extendsFrom annotationProcessor } testCompile { + extendsFrom testAnnotationProcessor + // Only JUNit 5 (Jupiter) should be used at compile time. // For runtime it's still needed by testcontainers, though. exclude group: 'junit', module: 'junit' @@ -60,12 +62,14 @@ dependencies { implementation 'org.modelmapper:modelmapper:3.1.0' compileOnly 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:testcontainers' diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql index 56b176da..9d640165 100644 --- a/src/main/resources/db/changelog/010-context.sql +++ b/src/main/resources/db/changelog/010-context.sql @@ -185,7 +185,6 @@ begin objectIdName := pureIdentifier(objectIdName); sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName); begin - raise notice 'sql: %', sql; execute sql into uuid; exception when others then @@ -205,7 +204,6 @@ begin objectTable := pureIdentifier(objectTable); sql := format('select * from %sIdNameByUuid(%L::uuid);', objectTable, objectUuid); begin - raise notice 'sql: %', sql; execute sql into idName; exception when others then diff --git a/src/main/resources/db/changelog/020-audit-log.sql b/src/main/resources/db/changelog/020-audit-log.sql index 99704894..5965e807 100644 --- a/src/main/resources/db/changelog/020-audit-log.sql +++ b/src/main/resources/db/changelog/020-audit-log.sql @@ -106,7 +106,6 @@ begin createTriggerSQL = 'CREATE TRIGGER ' || targetTable || '_journal' || ' AFTER INSERT OR UPDATE OR DELETE ON ' || targetTable || ' FOR EACH ROW EXECUTE PROCEDURE tx_journal_trigger()'; - raise notice 'sql: %', createTriggerSQL; execute createTriggerSQL; end; $$; --// diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index 7bdb2cd8..f34963ea 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -364,8 +364,8 @@ create table RbacGrants ( uuid uuid primary key default uuid_generate_v4(), grantedByRoleUuid uuid references RbacRole (uuid) on delete cascade, - ascendantUuid uuid references RbacReference (uuid) on delete cascade, - descendantUuid uuid references RbacReference (uuid) on delete cascade, + ascendantUuid uuid references RbacReference (uuid) on delete cascade not null, + descendantUuid uuid references RbacReference (uuid) on delete cascade not null, assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false) unique (ascendantUuid, descendantUuid) ); diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql index ac7f6434..326f8888 100644 --- a/src/main/resources/db/changelog/055-rbac-views.sql +++ b/src/main/resources/db/changelog/055-rbac-views.sql @@ -55,22 +55,45 @@ grant all privileges on rbacrole_rv to restricted; drop view if exists rbacgrants_ev; create or replace view rbacgrants_ev as -- @formatter:off - select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || '.' || r.roletype as grantedByRoleIdName, - g.objectTable || '#' || g.objectIdName || '.' || g.roletype as grantedRoleIdName, g.userName, g.assumed, - g.grantedByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid, - g.objectTable, g.objectUuid, g.objectIdName, g.roleType as grantedRoleType + select x.grantUuid as uuid, + go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || '.' || r.roletype as grantedByRoleIdName, + x.ascendingIdName as ascendantIdName, + x.descendingIdName as descendantIdName, + x.grantedByRoleUuid, + x.ascendantUuid as ascendantUuid, + x.descendantUuid as descenantUuid, + x.assumed from ( - select g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed, - u.name as userName, o.objecttable, r.objectuuid, r.roletype, - findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName - from rbacgrants as g - join rbacrole as r on r.uuid = g.descendantUuid - join rbacobject o on o.uuid = r.objectuuid - right outer join rbacuser u on u.uuid = g.ascendantuuid - ) as g - join RbacRole as r on r.uuid = grantedByRoleUuid - join RbacObject as o on o.uuid = r.objectUuid - order by grantedRoleIdName; + select g.uuid as grantUuid, + g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed, + + coalesce( + 'user ' || au.name, + 'role ' || aro.objectTable || '#' || findIdNameByObjectUuid(aro.objectTable, aro.uuid) || '.' || ar.roletype + ) as ascendingIdName, + aro.objectTable, aro.uuid, + + coalesce( + 'role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype, + 'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid) + ) as descendingIdName, + dro.objectTable, dro.uuid + from rbacgrants as g + + left outer join rbacrole as ar on ar.uuid = g.ascendantUuid + left outer join rbacobject as aro on aro.uuid = ar.objectuuid + left outer join rbacuser as au on au.uuid = g.ascendantUuid + + left outer join rbacrole as dr on dr.uuid = g.descendantUuid + left outer join rbacobject as dro on dro.uuid = dr.objectuuid + left outer join rbacpermission dp on dp.uuid = g.descendantUuid + left outer join rbacobject as dpo on dpo.uuid = dp.objectUuid + ) as x + left outer join rbacrole as r on r.uuid = grantedByRoleUuid + left outer join rbacuser u on u.uuid = x.ascendantuuid + left outer join rbacobject go on go.uuid = r.objectuuid + + order by x.ascendingIdName, x.descendingIdName; -- @formatter:on --// @@ -96,7 +119,7 @@ select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || from rbacgrants as g join rbacrole as r on r.uuid = g.descendantUuid join rbacobject o on o.uuid = r.objectuuid - join rbacuser u on u.uuid = g.ascendantuuid + left outer join rbacuser u on u.uuid = g.ascendantuuid where isGranted(currentSubjectsUuids(), r.uuid) ) as g join RbacRole as r on r.uuid = grantedByRoleUuid diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantDisplayExtractor.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantDisplayExtractor.java new file mode 100644 index 00000000..1e362e84 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantDisplayExtractor.java @@ -0,0 +1,14 @@ +package net.hostsharing.hsadminng.rbac.rbacgrant; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class RawRbacGrantDisplayExtractor { + + @NotNull + public static List grantDisplaysOf(final List roles) { + return roles.stream().map(RawRbacGrantEntity::toDisplay).collect(Collectors.toList()); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java new file mode 100644 index 00000000..6effb682 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java @@ -0,0 +1,55 @@ +package net.hostsharing.hsadminng.rbac.rbacgrant; + +import lombok.*; +import org.springframework.data.annotation.Immutable; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "rbacgrants_ev") +@Getter +@Setter +@Builder +@ToString +@Immutable +@NoArgsConstructor +@AllArgsConstructor +public class RawRbacGrantEntity { + + @Id + private UUID uuid; + + @Column(name = "grantedbyroleidname", updatable = false, insertable = false) + private String grantedByRoleIdName; + + @Column(name = "grantedbyroleuuid", updatable = false, insertable = false) + private UUID grantedByRoleUuid; + + @Column(name = "ascendantidname", updatable = false, insertable = false) + private String ascendantIdName; + + @Column(name = "ascendantuuid", updatable = false, insertable = false) + private UUID ascendingUuid; + + @Column(name = "descendantidname", updatable = false, insertable = false) + private String descendantIdName; + + @Column(name = "descenantuuid", updatable = false, insertable = false) + private UUID descendantUuid; + + @Column(name = "assumed", updatable = false, insertable = false) + private boolean assumed; + + public String toDisplay() { + // @formatter:off + return "{ grant " + descendantIdName + + " to " + ascendantIdName + + " by " + ( grantedByRoleUuid == null + ? "system" + : grantedByRoleIdName ) + + ( assumed ? " and assume" : "") + + " }"; + // @formatter:on + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java new file mode 100644 index 00000000..c7ac60ab --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java @@ -0,0 +1,11 @@ +package net.hostsharing.hsadminng.rbac.rbacgrant; + +import org.springframework.data.repository.Repository; + +import java.util.List; +import java.util.UUID; + +public interface RawRbacGrantRepository extends Repository { + + List findAll(); +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java new file mode 100644 index 00000000..ed35a474 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java @@ -0,0 +1,38 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import lombok.*; +import org.hibernate.annotations.Formula; +import org.springframework.data.annotation.Immutable; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "rbacrole_ev") +@Getter +@Setter +@ToString +@Immutable +@NoArgsConstructor +@AllArgsConstructor +public class RawRbacRoleEntity { + + @Id + private UUID uuid; + + @Column(name="objectuuid") + private UUID objectUuid; + + @Column(name="objecttable") + private String objectTable; + + @Column(name="objectidname") + private String objectIdName; + + @Column(name="roletype") + @Enumerated(EnumType.STRING) + private RbacRoleType roleType; + + @Formula("objectTable||'#'||objectIdName||'.'||roleType") + private String roleName; +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleNameExtractor.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleNameExtractor.java new file mode 100644 index 00000000..949f3f37 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleNameExtractor.java @@ -0,0 +1,15 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class RawRbacRoleNameExtractor { + + @NotNull + public static List roleNamesOf(@NotNull final List roles) { + return roles.stream().map(RawRbacRoleEntity::getRoleName).collect(Collectors.toList()); + } + +} diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleRepository.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleRepository.java new file mode 100644 index 00000000..c86f88a7 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleRepository.java @@ -0,0 +1,11 @@ +package net.hostsharing.hsadminng.rbac.rbacrole; + +import org.springframework.data.repository.Repository; + +import java.util.List; +import java.util.UUID; + +public interface RawRbacRoleRepository extends Repository { + + List findAll(); +}