Protecting data

Core has four ways to protect data. Each answers a different question.

Mechanism Question it answers Decided Server code API User
Code access Can code set this property? Compile time Restricted Restricted Restricted
Rule access Is this property locked or hidden for this record right now? Runtime, per record ReadOnly and Hidden restrict writes Restricted Hidden is not rendered
API exposure Can API clients touch this entity or property at all? Compile time Not restricted Restricted Restricted
Role access Is this user allowed? Runtime, per user Not restricted Restricted per role Restricted per role

"Restricts server code" means your own business logic. Only code access and rule access stop it. Role access and API exposure apply to API requests only.

Choosing a mechanism

You want to... Use
Stop all code from assigning a property Code access
Lock properties after a record is posted, closed, or approved Rule access (ReadOnly)
Hide a field that does not apply to the record's current state Rule access (Hidden)
Only allow the client to read a property but server code can set it API exposure (ReadOnly)
Keep an internal entity or property out of the API completely API exposure (None or Hide)
Let some users see or edit a field and not others Role access
Keep a value private from some users Role access (don't grant the field)

1. Code access

Plain C# decides who can assign a property. Core generates the property, so use the setter's accessibility.

[Property<DataTypes.Currency>("Balance")]
public partial decimal Balance { get; } //No setter
  • Server code, the API, and the UI cannot set the property from outside the class.
  • These properties can be dirtied and computed based on changes of another property or running a method. See Computes.

See Properties.

2. Business logic rule access

Business logic decides at runtime, per record, whether a property is read-only or hidden.

[Logic]
public class InvoiceBL(Invoice.Logic invoice)
{
    [RegisterLogic]
    public void ProtectInvoice()
    {
        invoice.ReadOnly(i => i.Amount).If(i => i.IsPosted);
        invoice.Hidden(i => i.RejectionReason).If(i => i.Status != InvoiceStatus.Rejected);
    }
}
Rule Effect
ReadOnly Writes are rejected, from server code and from the API. The UI disables the field.
Hidden Same as ReadOnly. The UI also does not render the field, and API responses mask its value.
  • If any condition on a property is true, the rule applies.
  • Server code can still read the real value of a hidden property.
  • Hidden masks values. It is not security. $filter, $orderby, and $apply still use real values. Use role access when a value must stay private.
  • Use property groups to apply one condition to many properties.

See ReadOnly and Hidden.

3. API exposure

Attributes on the entity and its properties limit what API clients can do. The limit is fixed at compile time and is the same for every user, including administrators.

Prefer no setter. If no code should set a property, remove the setter (see Code access). [ApiAccess(PropertyAccess.ReadOnly)] only blocks API clients, so server code can still set the property. Use it when server code must set the value but clients must not.

[ApiEntity]
[ApiAccess(EntityAccess.Read | EntityAccess.Update)]
public partial class Transaction
{
    [Property<DataTypes.Currency>("Amount")]
    [ApiAccess(PropertyAccess.ReadOnly)]
    public partial decimal Amount { get; set; }
}
  • Clients can read and update Transaction, but cannot create or delete it, or change Amount.
  • PropertyAccess.Hide removes the property from API reads and writes.
  • Server code is not restricted. Your business logic can still set Amount.
  • Hidden entities and properties are not included in the meta data.

See API access.

4. Role access

Role-based access control (RBAC) decides per user. Responsibilities grant permissions. Roles hold responsibilities. Users hold roles.

access.AddResponsibility("View transactions", "Read-only access to transactions", grant =>
{
    grant.Entity<Transaction>()
        .View()
        .Read(t => t.Amount)
        .Write(t => t.Clear);
});
  • Entities, properties, and methods are restricted until a responsibility grants them.
  • When a user has several responsibilities, the most permissive setting wins.
  • A property is hidden when no responsibility grants it. There is no Hide grant. Read and Write apply to API requests and to the UI.
  • Hidden entities and properties are not listed in the metadata. OData requests with hidden elements are refused by the API.
  • Row filters (FilterRows) also limit which rows a user sees. Server logic still sees every row. See Row-level filtering.
  • The UI needs no extra wiring. It hides or disables what the user cannot use.
  • Connected apps (OAuth) add one more layer. A read-only app only gets its user's reads.
  • Server code is not restricted.

See RBAC.