improve RBAC definition DSL
This commit is contained in:
parent
b4d6930fbe
commit
5ac616e425
@ -16,6 +16,7 @@ import java.util.UUID;
|
||||
|
||||
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.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
@ -56,19 +57,19 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
// @formatter:off
|
||||
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
||||
.withIdentityView(SQL.query("target.iban || ':' || target.holder"))
|
||||
.withUpdatableColumns("holder", "iban", "bic")
|
||||
.createRole(OWNER)
|
||||
.withCurrentUserAsOwner()
|
||||
.withPermission(ALL)
|
||||
.withIncomingSuperRole(GLOBAL, ADMIN)
|
||||
.createSubRole(ADMIN)
|
||||
.withPermission(EDIT)
|
||||
.createSubRole(REFERRER)
|
||||
.withPermission(VIEW)
|
||||
.pop();
|
||||
// @formatter:on
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
with.permission(ALL);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.permission(EDIT);
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.permission(VIEW);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ 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.RbacUserReference.UserRole.CREATOR;
|
||||
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;
|
||||
@ -33,7 +34,6 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
||||
.withProp(Fields.label, HsOfficeContactEntity::getLabel)
|
||||
.withProp(Fields.emailAddresses, HsOfficeContactEntity::getEmailAddresses);
|
||||
|
||||
|
||||
@Id
|
||||
@GeneratedValue(generator = "UUID")
|
||||
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
|
||||
@ -60,19 +60,19 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
||||
}
|
||||
|
||||
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
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
with.permission(ALL);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.permission(EDIT);
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.permission(VIEW);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import lombok.*;
|
||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
@ -18,9 +18,7 @@ import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.VIEW;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
@ -108,7 +106,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
// @formatter:off
|
||||
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
||||
.withIdentityView(RbacView.SQL.query("""
|
||||
SELECT debitor.uuid,
|
||||
@ -169,6 +166,5 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
||||
.forExampleRole("partnerPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN)
|
||||
.forExampleRole("operationalPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN)
|
||||
.forExampleRole("partnerRel", TENANT).wouldBeGrantedTo("partnerPerson", REFERRER);
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ 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.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.query;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
@ -64,19 +65,19 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
// @formatter:off
|
||||
return rbacViewFor("person", HsOfficePersonEntity.class)
|
||||
.withIdentityView(query("concat(target.tradeName, target.familyName, target.givenName)"))
|
||||
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
||||
.createRole(OWNER)
|
||||
.withPermission(ALL)
|
||||
.withCurrentUserAsOwner()
|
||||
.withIncomingSuperRole(GLOBAL, ADMIN)
|
||||
.createSubRole(ADMIN)
|
||||
.withPermission(EDIT)
|
||||
.createSubRole(REFERRER)
|
||||
.withPermission(VIEW)
|
||||
.pop();
|
||||
// @formatter:on
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.permission(ALL);
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.permission(EDIT);
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.permission(VIEW);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.hs.office.relationship;
|
||||
import lombok.*;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
@ -16,6 +16,7 @@ import java.util.UUID;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||
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.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
@ -77,7 +78,6 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
// @formatter:off
|
||||
return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
|
||||
.withIdentityView(SQL.query("""
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
||||
@ -94,23 +94,24 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
||||
.importEntityAlias("contact", HsOfficeContactEntity.class,
|
||||
fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"),
|
||||
dependsOnColumn("contactUuid"))
|
||||
.createRole(OWNER)
|
||||
.withCurrentUserAsOwner()
|
||||
.withPermission(ALL)
|
||||
.withIncomingSuperRole(GLOBAL, ADMIN)
|
||||
.withIncomingSuperRole("anchorPerson", ADMIN)
|
||||
.createSubRole(ADMIN)
|
||||
.withPermission(EDIT)
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||
with.permission(ALL);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.permission(EDIT);
|
||||
})
|
||||
.createSubRole(AGENT)
|
||||
.createSubRole(TENANT)
|
||||
.withPermission(VIEW)
|
||||
.withIncomingSuperRole("anchorPerson", ADMIN)
|
||||
.withIncomingSuperRole("holderPerson", ADMIN)
|
||||
.withIncomingSuperRole("contact", ADMIN)
|
||||
.withOutgoingSubRole("anchorPerson", REFERRER)
|
||||
.withOutgoingSubRole("holderPerson", REFERRER)
|
||||
.withOutgoingSubRole("contact", REFERRER)
|
||||
.pop();
|
||||
// @formatter:on
|
||||
.createSubRole(TENANT, (with) -> {
|
||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||
with.incomingSuperRole("holderPerson", ADMIN);
|
||||
with.incomingSuperRole("contact", ADMIN);
|
||||
with.outgoingSubRole("anchorPerson", REFERRER);
|
||||
with.outgoingSubRole("holderPerson", REFERRER);
|
||||
with.outgoingSubRole("contact", REFERRER);
|
||||
with.permission(VIEW);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||
@ -37,6 +39,7 @@ public class RbacView {
|
||||
|
||||
private SQL identityViewSqlQuery;
|
||||
private EntityAlias entityAliasProxy;
|
||||
private RbacRoleDefinition previousRoleDef;
|
||||
|
||||
public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> entityClass) {
|
||||
return new RbacView(alias, entityClass);
|
||||
@ -59,8 +62,26 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition createRole(final Role role) {
|
||||
return findRbacRole(entityAlias, role).toCreate();
|
||||
public RbacView createRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(entityAlias, role).toCreate();
|
||||
with.accept(newRoleDef);
|
||||
previousRoleDef = newRoleDef;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacView createSubRole(final Role role) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(entityAlias, role).toCreate();
|
||||
new RbacGrantDefinition(newRoleDef, previousRoleDef).toCreate();
|
||||
previousRoleDef = newRoleDef;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacView createSubRole(final Role role, final Consumer<RbacRoleDefinition> with) {
|
||||
final RbacRoleDefinition newRoleDef = findRbacRole(entityAlias, role).toCreate();
|
||||
new RbacGrantDefinition(newRoleDef, previousRoleDef).toCreate();
|
||||
with.accept(newRoleDef);
|
||||
previousRoleDef = newRoleDef;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacPermissionDefinition createPermission(final Permission permission) {
|
||||
@ -143,8 +164,8 @@ public class RbacView {
|
||||
}
|
||||
|
||||
|
||||
private RbacGrantDefinition grantRoleToCurrentUser(final RbacRoleDefinition roleDefinition) {
|
||||
return new RbacGrantDefinition(roleDefinition, currentUser()).toCreate();
|
||||
private RbacGrantDefinition grantRoleToUser(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
|
||||
return new RbacGrantDefinition(roleDefinition, user).toCreate();
|
||||
}
|
||||
|
||||
private RbacGrantDefinition grantPermissionToRole(final RbacPermissionDefinition permDef , final RbacRoleDefinition roleDef) {
|
||||
@ -325,46 +346,36 @@ public class RbacView {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition withCurrentUserAsOwner() {
|
||||
addGrant(grantRoleToCurrentUser(this));
|
||||
public RbacRoleDefinition owningUser(final RbacUserReference.UserRole userRole) {
|
||||
addGrant(grantRoleToUser(this, findUserRef(userRole)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition withPermission(final Permission permission) {
|
||||
public RbacRoleDefinition permission(final Permission permission) {
|
||||
addGrant(grantPermissionToRole( createPermission(entityAlias, permission) , this));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition withIncomingSuperRole(final String entityAlias, final Role role) {
|
||||
public RbacRoleDefinition incomingSuperRole(final String entityAlias, final Role role) {
|
||||
final var incomingSuperRole = findRbacRole(entityAlias, role);
|
||||
addGrant(grantSubRoleToSuperRole(this, incomingSuperRole));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition withOutgoingSubRole(final String entityAlias, final Role role) {
|
||||
public RbacRoleDefinition outgoingSubRole(final String entityAlias, final Role role) {
|
||||
final var outgoingSubRole = findRbacRole(entityAlias, role);
|
||||
addGrant(grantSubRoleToSuperRole(outgoingSubRole, this));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RbacRoleDefinition createSubRole(final Role role) {
|
||||
final var roleDef = findRbacRole(entityAlias, role).toCreate();
|
||||
new RbacGrantDefinition(roleDef, this).toCreate();
|
||||
return roleDef;
|
||||
}
|
||||
|
||||
public RbacView pop() {
|
||||
return RbacView.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "role:" + entityAlias.aliasName + role;
|
||||
}
|
||||
}
|
||||
|
||||
public RbacUserReference currentUser() {
|
||||
return userDefs.stream().filter(u -> u.role == CREATOR).findFirst().orElseThrow();
|
||||
public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) {
|
||||
return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
|
@ -22,6 +22,7 @@ public class RbacViewMermaidFlowchart {
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
""");
|
||||
flowchart.writeLn();
|
||||
renderEntitySubgraphs();
|
||||
renderGrants();
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ import jakarta.persistence.*;
|
||||
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.Permission.ALL;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.VIEW;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||
|
||||
@ -36,19 +38,19 @@ public class TestCustomerEntity implements RbacObject {
|
||||
|
||||
|
||||
public static RbacView rbac() {
|
||||
// @formatter:off
|
||||
return rbacViewFor("contact", TestCustomerEntity.class)
|
||||
.withIdentityView(RbacView.SQL.query("target.prefix"))
|
||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||
.createRole(OWNER)
|
||||
.withPermission(ALL)
|
||||
.withCurrentUserAsOwner()
|
||||
.withIncomingSuperRole(GLOBAL, ADMIN)
|
||||
.createSubRole(ADMIN)
|
||||
.withPermission(RbacView.Permission.custom("add-package"))
|
||||
.createSubRole(TENANT)
|
||||
.withPermission(VIEW)
|
||||
.pop();
|
||||
// @formatter:on
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
with.permission(ALL);
|
||||
})
|
||||
.createSubRole(ADMIN, (with) -> {
|
||||
with.permission(RbacView.Permission.custom("add-package"));
|
||||
})
|
||||
.createSubRole(TENANT, (with) -> {
|
||||
with.permission(VIEW);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,11 @@ class TestCustomerEntityTest {
|
||||
end
|
||||
end
|
||||
|
||||
role:contact:owner ==> perm:contact:*
|
||||
role:contact:owner ==> perm:contact:*
|
||||
user:creator ==> role:contact:owner
|
||||
role:global:admin ==> role:contact:owner
|
||||
role:global:admin ==> role:contact:owner
|
||||
role:contact:owner ==> perm:contact:*
|
||||
role:contact:owner ==> perm:contact:*
|
||||
role:contact:owner ==> role:contact:admin
|
||||
role:contact:admin ==> perm:contact:add-package
|
||||
role:contact:admin ==> perm:contact:add-package
|
||||
|
Loading…
Reference in New Issue
Block a user