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.

Updates need View() and Write(...) for each supplied field, without Create(). ApiAccess must also allow the operation, even for administrators.

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.