add customer and outgoing grants to RelationshipEntity
This commit is contained in:
parent
fc1cc5815f
commit
f45f88ba77
@ -4,6 +4,7 @@ import lombok.*;
|
|||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
@ -11,6 +12,10 @@ import org.hibernate.annotations.GenericGenerator;
|
|||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
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.Role.*;
|
||||||
|
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;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -53,4 +58,21 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RbacView rbac() {
|
||||||
|
// @formatter:off
|
||||||
|
return rbacViewFor("contact", HsOfficeContactEntity.class)
|
||||||
|
.withIdentityView(RbacView.SQL.query("target.label"))
|
||||||
|
.withUpdatableColumns("label", "postalAddress", "emailAddresses", "phoneNumbers")
|
||||||
|
.createRole(OWNER)
|
||||||
|
.withPermission(ALL)
|
||||||
|
.withCurrentUserAsOwner()
|
||||||
|
.withIncomingSuperRole(GLOBAL, ADMIN)
|
||||||
|
.createSubRole(ADMIN)
|
||||||
|
.withPermission(EDIT)
|
||||||
|
.createSubRole(REFERRER)
|
||||||
|
.withPermission(VIEW)
|
||||||
|
.pop();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,12 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartne
|
|||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||||
|
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -158,7 +158,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <E extends HasUuid> E ref(final Class<E> entityClass, final UUID uuid) {
|
private <E extends RbacObject> E ref(final Class<E> entityClass, final UUID uuid) {
|
||||||
try {
|
try {
|
||||||
return em.getReference(entityClass, uuid);
|
return em.getReference(entityClass, uuid);
|
||||||
} catch (final Throwable exc) {
|
} catch (final Throwable exc) {
|
||||||
|
@ -91,6 +91,9 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
|
||||||
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid"),
|
fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid"),
|
||||||
dependsOnColumn("relHolderUuid"))
|
dependsOnColumn("relHolderUuid"))
|
||||||
|
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||||
|
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"),
|
||||||
|
dependsOnColumn("contactUuid"))
|
||||||
.createRole(OWNER)
|
.createRole(OWNER)
|
||||||
.withCurrentUserAsOwner()
|
.withCurrentUserAsOwner()
|
||||||
.withPermission(ALL)
|
.withPermission(ALL)
|
||||||
@ -99,9 +102,14 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
.createSubRole(ADMIN)
|
.createSubRole(ADMIN)
|
||||||
.withPermission(EDIT)
|
.withPermission(EDIT)
|
||||||
.createSubRole(AGENT)
|
.createSubRole(AGENT)
|
||||||
.withIncomingSuperRole("holderPerson", ADMIN)
|
|
||||||
.createSubRole(TENANT)
|
.createSubRole(TENANT)
|
||||||
.withPermission(VIEW)
|
.withPermission(VIEW)
|
||||||
|
.withIncomingSuperRole("anchorPerson", ADMIN)
|
||||||
|
.withIncomingSuperRole("holderPerson", ADMIN)
|
||||||
|
.withIncomingSuperRole("contact", ADMIN)
|
||||||
|
.withOutgoingSubRole("anchorPerson", REFERRER)
|
||||||
|
.withOutgoingSubRole("holderPerson", REFERRER)
|
||||||
|
.withOutgoingSubRole("contact", REFERRER)
|
||||||
.pop();
|
.pop();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class RbacView {
|
public class RbacView {
|
||||||
@ -18,9 +19,9 @@ public class RbacView {
|
|||||||
|
|
||||||
private final EntityAlias entityAlias;
|
private final EntityAlias entityAlias;
|
||||||
|
|
||||||
private final Set<RbacUserReference> userDefs = new HashSet<>();
|
private final Set<RbacUserReference> userDefs = new LinkedHashSet<>();
|
||||||
private final Set<RbacRoleDefinition> roleDefs = new HashSet<>();
|
private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>();
|
||||||
private final Set<RbacPermissionDefinition> permDefs = new HashSet<>();
|
private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>();
|
||||||
private final Map<String, EntityAlias> entityAliases = new HashMap<>() {
|
private final Map<String, EntityAlias> entityAliases = new HashMap<>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -31,8 +32,8 @@ public class RbacView {
|
|||||||
return super.put(key, value);
|
return super.put(key, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final Set<String> updatableColumns = new TreeSet<>();
|
private final Set<String> updatableColumns = new LinkedHashSet<>();
|
||||||
private final Set<RbacGrantDefinition> grantDefs = new HashSet<>();
|
private final Set<RbacGrantDefinition> grantDefs = new LinkedHashSet<>();
|
||||||
|
|
||||||
private SQL identityViewSqlQuery;
|
private SQL identityViewSqlQuery;
|
||||||
private EntityAlias entityAliasProxy;
|
private EntityAlias entityAliasProxy;
|
||||||
@ -340,6 +341,12 @@ public class RbacView {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RbacRoleDefinition withOutgoingSubRole(final String entityAlias, final Role role) {
|
||||||
|
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
||||||
|
addGrant(grantSubRoleToSuperRole(outgoingSubRole, this));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public RbacRoleDefinition createSubRole(final Role role) {
|
public RbacRoleDefinition createSubRole(final Role role) {
|
||||||
final var roleDef = findRbacRole(entityAlias, role).toCreate();
|
final var roleDef = findRbacRole(entityAlias, role).toCreate();
|
||||||
new RbacGrantDefinition(roleDef, this).toCreate();
|
new RbacGrantDefinition(roleDef, this).toCreate();
|
||||||
@ -415,6 +422,18 @@ public class RbacView {
|
|||||||
boolean isPlaceholder() {
|
boolean isPlaceholder() {
|
||||||
return entityClass == null;
|
return entityClass == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String withoutEntitySuffix(final String simpleEntityName) {
|
||||||
|
return simpleEntityName.substring(0, simpleEntityName.length()-"Entity".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
String simpleName() {
|
||||||
|
return isGlobal()
|
||||||
|
? aliasName
|
||||||
|
: uncapitalize(withoutEntitySuffix(entityClass.getSimpleName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Role(String roleName) {
|
public record Role(String roleName) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -21,11 +23,9 @@ public class RbacViewMermaidFlowchart {
|
|||||||
flowchart.append("""
|
flowchart.append("""
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
|
||||||
""");
|
""");
|
||||||
renderEntitySubgraphs();
|
renderEntitySubgraphs();
|
||||||
renderGrants();
|
renderGrants();
|
||||||
flowchart.append("```");
|
|
||||||
}
|
}
|
||||||
private void renderEntitySubgraphs() {
|
private void renderEntitySubgraphs() {
|
||||||
rbacDef.getEntityAliases().values().stream()
|
rbacDef.getEntityAliases().values().stream()
|
||||||
@ -40,12 +40,16 @@ public class RbacViewMermaidFlowchart {
|
|||||||
|
|
||||||
subgraph %{aliasName}["`**%{aliasName}**`"]
|
subgraph %{aliasName}["`**%{aliasName}**`"]
|
||||||
direction TB
|
direction TB
|
||||||
style %{aliasName} fill: %{color}
|
style %{aliasName} fill:%{color},stroke:darkblue,stroke-width:8px
|
||||||
|
|
||||||
"""
|
"""
|
||||||
.replace("%{aliasName}", entity.aliasName())
|
.replace("%{aliasName}", entity.aliasName())
|
||||||
.replace("%{color}", color ));
|
.replace("%{color}", color ));
|
||||||
|
|
||||||
|
rbacDef.getEntityAliases().values().stream()
|
||||||
|
.filter(e -> e.aliasName().startsWith(entity.aliasName() + "."))
|
||||||
|
.forEach(this::renderEntitySubgraph);
|
||||||
|
|
||||||
wrapOutputInSubgraph(entity.aliasName() + ":roles", color,
|
wrapOutputInSubgraph(entity.aliasName() + ":roles", color,
|
||||||
rbacDef.getRoleDefs().stream()
|
rbacDef.getRoleDefs().stream()
|
||||||
.filter(r -> r.getEntityAlias() == entity)
|
.filter(r -> r.getEntityAlias() == entity)
|
||||||
@ -118,21 +122,10 @@ public class RbacViewMermaidFlowchart {
|
|||||||
return flowchart.toString();
|
return flowchart.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
void generateToMarkdownFile() throws IOException {
|
||||||
|
final Path path = Paths.get("doc", rbacDef.getEntityAlias().simpleName() + ".md");
|
||||||
// Files.writeString(
|
|
||||||
// Paths.get("doc", "hsOfficeRelationship.md"),
|
|
||||||
// new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).toString(),
|
|
||||||
// StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Files.writeString(
|
|
||||||
// Paths.get("doc", "hsOfficeBankAccount.md"),
|
|
||||||
// new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).toString(),
|
|
||||||
// StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
|
||||||
|
|
||||||
Files.writeString(
|
Files.writeString(
|
||||||
Paths.get("doc", "hsOfficeDebitor.md"),
|
path,
|
||||||
"""
|
"""
|
||||||
### rbac %{entityAlias} %{timestamp}
|
### rbac %{entityAlias} %{timestamp}
|
||||||
|
|
||||||
@ -140,10 +133,16 @@ public class RbacViewMermaidFlowchart {
|
|||||||
%{flowchart}
|
%{flowchart}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
.replace("%{entityAlias}", "contact")
|
.replace("%{entityAlias}", rbacDef.getEntityAlias().aliasName())
|
||||||
.replace("%{timestamp}", LocalDateTime.now().toString())
|
.replace("%{timestamp}", LocalDateTime.now().toString())
|
||||||
.replace("%{flowchart}", new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).toString()),
|
.replace("%{flowchart}", flowchart.toString()),
|
||||||
|
|
||||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile();
|
||||||
|
new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile();
|
||||||
|
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -31,7 +30,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
||||||
|
|
||||||
entityClass = rbacDef.getEntityAlias().entityClass();
|
entityClass = rbacDef.getEntityAlias().entityClass();
|
||||||
simpleEntityName = withoutEntitySuffix(entityClass.getSimpleName());
|
simpleEntityName = entityClass.getSimpleName();
|
||||||
simpleEntityVarName = uncapitalize(simpleEntityName);
|
simpleEntityVarName = uncapitalize(simpleEntityName);
|
||||||
rawTableName = withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
rawTableName = withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
|
||||||
}
|
}
|
||||||
@ -209,10 +208,6 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
return tableName.substring(0, tableName.length()-"_rv".length());
|
return tableName.substring(0, tableName.length()-"_rv".length());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String withoutEntitySuffix(final String simpleEntityName) {
|
|
||||||
return simpleEntityName.substring(0, simpleEntityName.length()-"Entity".length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
|
private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
|
||||||
return switch (userRef.role) {
|
return switch (userRef.role) {
|
||||||
case CREATOR -> "currentUserUuid()";
|
case CREATOR -> "currentUserUuid()";
|
||||||
@ -221,20 +216,18 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String toPlPgSqlReference(final PostgresTriggerReference triggerRef, final RbacView.RbacRoleDefinition roleDef) {
|
private String toPlPgSqlReference(final PostgresTriggerReference triggerRef, final RbacView.RbacRoleDefinition roleDef) {
|
||||||
return toSimpleVarName(roleDef.getEntityAlias()) + StringUtils.capitalize(roleDef.getRole().roleName()) +
|
return toVar(roleDef) +
|
||||||
(roleDef.getEntityAlias().isGlobal() ? "()"
|
(roleDef.getEntityAlias().isGlobal() ? "()"
|
||||||
: rbacDef.isMainEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
|
: rbacDef.isMainEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
|
||||||
: "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + ")");
|
: "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toTriggerReference(final PostgresTriggerReference triggerRef, final RbacView.EntityAlias entityAlias) {
|
private static String toVar(final RbacView.RbacRoleDefinition roleDef) {
|
||||||
return triggerRef.name().toLowerCase() + StringUtils.capitalize(entityAlias.aliasName());
|
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toSimpleVarName(final RbacView.EntityAlias entityAlias) {
|
private static String toTriggerReference(final PostgresTriggerReference triggerRef, final RbacView.EntityAlias entityAlias) {
|
||||||
return entityAlias.isGlobal()
|
return triggerRef.name().toLowerCase() + capitalize(entityAlias.aliasName());
|
||||||
? entityAlias.aliasName()
|
|
||||||
: uncapitalize(withoutEntitySuffix(entityAlias.entityClass().getSimpleName()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String indent(final int tabs) {
|
private String indent(final int tabs) {
|
||||||
|
Loading…
Reference in New Issue
Block a user