Defining Responsibilities In Business Logic
Responsibilities are code-defined permission bundles. You define them in [Logic] classes and register them with IResponsibilityRegistry.
Basic pattern
Select entity -> Define entity permissions -> Define property permissions
using Benevia.Core.Annotations;
using Benevia.Core.Permissions;
[Logic]
public class ContactBL(Contact.Logic contact, IResponsibilityRegistry access)
{
[RegisterLogic]
public void CreateContactResponsibilities()
{
access.AddResponsibility("Enter individual", "Limited permissions for creating individual contacts", grant =>
{
grant.Entity<Contact>()
.CreateAndDelete()
.Write(c => new {c.FirstName, c.LastName, c.PrimaryEmail, c.PrimaryPhone, c.MailingAddress})
.Read(c => c.Website);
grant.Entity<Address>()
.View()
.Write(c => new {c.StreetAddress, c.City, c.State, c.PostalCode});
});
access.AddResponsibility("View contacts", "View all contact information", grant =>
{
//...
});
}
//...
}
Entity permissions
Use one of these entry points per entity.
CreateAndDelete()Create(): Entities can be created but not deleted.View(): Entities cannot be created or deleted. Depending on property permissions, it can be edited.
Set property permissions
After setting entity-level access, set property permissions:
Read(...): Set a property or properties to haveReadpermissionsWrite(...): Set a property or properties to haveWritepermissionsReadForAllProperties(): Set all properties on this entity to haveReadpermissions. This does not set method permissions.WriteForAllProperties(): Set all properties on this entity to haveWritepermissions. This does not set method permissions.
grant.Entity<Contact>()
.View() //Cannot create or delete an entity, but can read Guid and Title.
.Write(s => new {s.FirstName, s.LastName, s.PrimaryPhone, s.PrimaryEmail}); //Give permissions to read and write
grant.Entity<Contact>()
.CreateAndDelete()
.Write(c => new {c.FirstName, c.LastName, c.PrimaryEmail, c.PrimaryPhone, c.MailingAddress})
.Read(c => c.Website);
grant.Entity<Address>()
.View()
.ReadForAllProperties(); //This makes all properties readable instead of defining each one.
grant.Entity<SalesOrder>()
.CreateAndDelete()
.Write(o => new {o.DocNumber, o.TransactionDate, o.Description, o.Details})
.Write(o => o.SellToCustomer) //Automatically gives me list access to the Customer entity
.Read(o => o.ShippingMethod); //Automatically gives me reference access to the ShippingMethod entity but not list access.
grant.Entity<PriceLevel>()
.View() //Gives reference and list access.
.Read(m => m.Name);
Set method permissions
Control callable methods independently from property permissions:
AllowMethod(...): Gives access to run a method or a list of methods by passing the name of the method.AllowAllMethods(): Gives access to run all methods in this entity.
grant.Entity<Product>()
.AllowMethod(p => p.GetPrice); //Pass name of method -- not p.GetPrice()
Row-level filtering
By default, rows are not restricted. You can constrain visible rows:
grant.Entity<Proposal>()
.View()
.FilterRows((query, userContext) =>
query.Where(c => c.SalesPerson.User.Id == userContext.Id));
Require other responsibilities
Use grant.Requires(...) to declare that one responsibility builds on another. The permissions from the required responsibility are automatically included when evaluating access.
Requires(name): Adds a single named responsibility as a dependency.Requires(name1, name2, ...): Adds multiple responsibilities as dependencies.
access.AddResponsibility("Modify contacts", "Create and modify contact information", grant =>
{
grant.Requires("View contacts"); // Inherits all permissions from "View contacts"
grant.Entity<Contact>()
.CreateAndDelete()
.WriteForAllProperties();
});
This is useful when one responsibility extends another — the user only needs to be assigned the more permissive responsibility, rather than both.