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 have Read permissions
  • Write(...): Set a property or properties to have Write permissions
  • ReadForAllProperties(): Set all properties on this entity to have Read permissions. This does not set method permissions.
  • WriteForAllProperties(): Set all properties on this entity to have Write permissions. 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.

<< Back to RBAC

<< Back to home page