add allGrantsFrom to RbacGrantsMermaidService

This commit is contained in:
Michael Hoennig 2024-02-14 11:10:21 +01:00
parent 188f5677f5
commit 5d9e81630b
3 changed files with 104 additions and 67 deletions

View File

@ -10,4 +10,6 @@ public interface RawRbacGrantRepository extends Repository<RawRbacGrantEntity, U
List<RawRbacGrantEntity> findAll(); List<RawRbacGrantEntity> findAll();
List<RawRbacGrantEntity> findByAscendingUuid(UUID ascendingUuid); List<RawRbacGrantEntity> findByAscendingUuid(UUID ascendingUuid);
List<RawRbacGrantEntity> findByDescendantUuid(UUID refUuid);
} }

View File

@ -4,10 +4,10 @@ import net.hostsharing.hsadminng.context.Context;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.*;
import java.util.EnumSet;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*; import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*;
@ -15,6 +15,7 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.
public class RbacGrantsMermaidService { public class RbacGrantsMermaidService {
public enum Include { public enum Include {
USERS,
PERMISSIONS, PERMISSIONS,
NOT_ASSUMED, NOT_ASSUMED,
TEST_ENTITIES, TEST_ENTITIES,
@ -27,15 +28,18 @@ public class RbacGrantsMermaidService {
@Autowired @Autowired
private RawRbacGrantRepository rawGrantRepo; private RawRbacGrantRepository rawGrantRepo;
@PersistenceContext
private EntityManager em;
public String allGrantsToCurrentUser(final EnumSet<Include> include) { public String allGrantsToCurrentUser(final EnumSet<Include> include) {
final var graph = new ArrayList<String>(); final var graph = new HashSet<String>();
for ( UUID subjectUuid: context.currentSubjectsUuids() ) { for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
traverseGrantsTo(graph, subjectUuid, include); traverseGrantsTo(graph, subjectUuid, include);
} }
return "flowchart TB\n\n" + String.join("\n", graph); return "flowchart TB\n\n" + String.join("\n", graph);
} }
private void traverseGrantsTo(final ArrayList<String> graph, final UUID refUuid, final EnumSet<Include> include) { private void traverseGrantsTo(final Set<String> graph, final UUID refUuid, final EnumSet<Include> include) {
final var grants = rawGrantRepo.findByAscendingUuid(refUuid); final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
grants.forEach(g -> { grants.forEach(g -> {
if (!include.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) { if (!include.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
@ -57,25 +61,51 @@ public class RbacGrantsMermaidService {
}); });
} }
public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> include) {
final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbacpermission WHERE objectuuid=:targetObject AND op=:op")
.setParameter("targetObject", targetObject)
.setParameter("op", op)
.getSingleResult();
final var graph = new HashSet<String>();
traverseGrantsFrom(graph, refUuid, include);
return "flowchart TB\n\n" + String.join("\n", graph);
}
private void traverseGrantsFrom(final Set<String> graph, final UUID refUuid, final EnumSet<Include> include) {
final var grants = rawGrantRepo.findByDescendantUuid(refUuid);
grants.forEach(g -> {
if (!include.contains(USERS) && g.getAscendantIdName().startsWith("user ")) {
return;
}
graph.add(
node(g.getAscendantIdName()) +
(g.isAssumed() ? " --> " : " -.-> ") +
node(g.getDescendantIdName()));
if (include.contains(NOT_ASSUMED) || g.isAssumed()) {
traverseGrantsFrom(graph, g.getAscendingUuid(), include);
}
});
}
private String node(final String idName) { private String node(final String idName) {
if (idName.contains("@")) {
return quoted(idName).replaceAll("@.*", "") + "[" + quoted(idName) + "]";
}
return quoted(idName) + display(idName); return quoted(idName) + display(idName);
} }
private String display(final String idName) { private String display(final String idName) {
// role hs_office_relationship#FirstGmbH-with-REPRESENTATIVE-FirbySusan.admin // role hs_office_relationship#FirstGmbH-with-REPRESENTATIVE-FirbySusan.admin
// TODO: refactor by separate algorithms for perm/role/user
final var refType = idName.split(" ", 2)[0]; final var refType = idName.split(" ", 2)[0];
final var roleType = refType.equals("perm") final var roleType = refType.equals("role")
? idName.substring(idName.lastIndexOf('.') + 1)
: refType.equals("perm")
? idName.split(" ")[1] ? idName.split(" ")[1]
: idName.substring(idName.lastIndexOf('.') + 1); : null;
final var objectName = refType.equals("perm") final var objectName = refType.equals("perm")
? idName.split(" ")[3] ? idName.split(" ")[3]
: idName.substring(refType.length()+1, idName.length()-roleType.length()-1); : idName.substring(refType.length()+1, idName.length() - (roleType == null ? 0 : (roleType.length()-1)) );
final var tableName = objectName.split("#")[0]; final var tableName = objectName.contains("#") ? objectName.split("#")[0] : "";
final var instanceName = objectName.split("#", 2)[1]; final var instanceName = objectName.contains("#") ? objectName.split("#", 2)[1] : objectName;
final var displayName = "\n" + tableName + "\n" + instanceName + "\n" + roleType; final var displayName = "\n" + tableName + "\n" + instanceName + (roleType == null ? "" : ("\n" + roleType));
if (refType.equals("user")) { if (refType.equals("user")) {
return "(" + displayName + ")"; return "(" + displayName + ")";
} }
@ -90,6 +120,6 @@ public class RbacGrantsMermaidService {
@NotNull @NotNull
private static String quoted(final String idName) { private static String quoted(final String idName) {
return idName.replace(" ", ":"); return idName.replace(" ", ":").replaceAll("@.*", "");
} }
} }

View File

@ -16,6 +16,7 @@ import java.io.BufferedWriter;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.UUID;
import static java.lang.String.join; import static java.lang.String.join;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -38,26 +39,26 @@ class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanu
assertThat(graph).isEqualTo(""" assertThat(graph).isEqualTo("""
flowchart TB flowchart TB
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx.t
tenant]
role:test_domain#xxx00-aaaa.owner[ role:test_domain#xxx00-aaaa.owner[
test_domain test_domain
xxx00-aaaa xxx00-aaaa.o
owner] --> role:test_domain#xxx00-aaaa.admin[ owner] --> role:test_domain#xxx00-aaaa.admin[
test_domain test_domain
xxx00-aaaa xxx00-aaaa.a
admin] admin]
role:test_domain#xxx00-aaaa.admin[ role:test_domain#xxx00-aaaa.admin[
test_domain test_domain
xxx00-aaaa xxx00-aaaa.a
admin] --> role:test_package#xxx00.tenant[ admin] --> role:test_package#xxx00.tenant[
test_package test_package
xxx00 xxx00.t
tenant]
role:test_package#xxx00.tenant[
test_package
xxx00
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx
tenant] tenant]
""".trim()); """.trim());
} }
@ -72,53 +73,53 @@ class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanu
role:test_domain#xxx00-aaaa.owner[ role:test_domain#xxx00-aaaa.owner[
test_domain test_domain
xxx00-aaaa xxx00-aaaa.o
owner] --> perm:*:on:test_domain#xxx00-aaaa{{ owner] --> perm:*:on:test_domain#xxx00-aaaa{{
test_domain test_domain
xxx00-aaaa xxx00-aaaa
*}} *}}
role:test_domain#xxx00-aaaa.owner[
test_domain
xxx00-aaaa
owner] --> role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa
admin]
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa
admin] --> perm:edit:on:test_domain#xxx00-aaaa{{
test_domain
xxx00-aaaa
edit}}
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa
admin] --> role:test_package#xxx00.tenant[
test_package
xxx00
tenant]
role:test_package#xxx00.tenant[
test_package
xxx00
tenant] --> perm:view:on:test_package#xxx00{{
test_package
xxx00
view}}
role:test_package#xxx00.tenant[
test_package
xxx00
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx
tenant]
role:test_customer#xxx.tenant[ role:test_customer#xxx.tenant[
test_customer test_customer
xxx xxx.t
tenant] --> perm:view:on:test_customer#xxx{{ tenant] --> perm:view:on:test_customer#xxx{{
test_customer test_customer
xxx xxx
view}} view}}
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin] --> perm:edit:on:test_domain#xxx00-aaaa{{
test_domain
xxx00-aaaa
edit}}
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> role:test_customer#xxx.tenant[
test_customer
xxx.t
tenant]
role:test_domain#xxx00-aaaa.owner[
test_domain
xxx00-aaaa.o
owner] --> role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin]
role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant] --> perm:view:on:test_package#xxx00{{
test_package
xxx00
view}}
role:test_domain#xxx00-aaaa.admin[
test_domain
xxx00-aaaa.a
admin] --> role:test_package#xxx00.tenant[
test_package
xxx00.t
tenant]
""".trim()); """.trim());
} }
@ -126,9 +127,13 @@ class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanu
@Disabled @Disabled
void print() throws IOException { void print() throws IOException {
//context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin"); //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
context("superuser-alex@hostsharing.net", "hs_office_person#FirstGmbH.admin"); context("superuser-alex@hostsharing.net");
//final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS));
final var targetObject = (UUID) em.createNativeQuery("SELECT uuid FROM hs_office_coopassetstransaction WHERE reference='ref 1000101-1'").getSingleResult();
final var graph = grantsMermaidService.allGrantsFrom(targetObject, "view", EnumSet.of(Include.USERS));
final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS));
try (BufferedWriter writer = new BufferedWriter(new FileWriter("doc/all-grants.md"))) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("doc/all-grants.md"))) {
writer.write(""" writer.write("""
### all grants to %s ### all grants to %s