Cloud Events Field Access Restrictions

Overview

The Field Access Restrictions feature provides granular, user-level control over field visibility and modification rights for data operations performed through the Cloud Events API. This security layer allows administrators to restrict read and/or write access to specific fields on a per-user basis, ensuring sensitive data is protected while maintaining API functionality.

Namespace: Origo.PTE.CloudEvents Main Table: Cloud Events Field Access (Table 65304) Management Page: Cloud Events Field Accesses (Page 65309) Management Codeunit: Cloud Events Field Access (Codeunit 65350)


Architecture

Components

The field access restriction system consists of four primary objects:

  1. Cloud Events Field Access Table (65304) - Stores restriction rules per user/table/field combination
  2. Cloud Events Field Accesses Page (65309) - Administrative UI for managing restrictions
  3. Cloud Events Restriction Type Enum (65320) - Defines restriction types (Both, Read, Write, Bypass)
  4. Cloud Events Field Access Codeunit (65350) - Public API for checking restrictions

Design Principles


Restriction Types

The system supports four types of field access restrictions:

Restriction TypeValueCaptionDescriptionBlocks ReadBlocks Write
Both0BothMost restrictive - blocks both read and write operations
Read1ReadBlocks read operations only - field will not appear in GET responses
Write2WriteBlocks write operations only - field can be read but not modified
Bypass3BypassWrite Guard bypass - field is excluded from all restriction checks; the ChangeLog Write Guard allows writes to this field regardless of Change Log coverage

Restriction Logic

Both Restriction:

Read Restriction:

Write Restriction:

Bypass:


Wildcard Rules

The system supports two wildcard values to apply restrictions broadly without creating per-field or per-table entries:

WildcardMeaningValidation rule
Field No. = 0All fields in the specified tableTable No. must be a valid table (or 0)
Table No. = 0All tables for the userField No. is forced to 0 (cannot specify a field without a table)

Resolution Order (Fallback Chain)

When checking whether a field is restricted, the system evaluates entries in this order:

  1. Specific entry — exact match on User + Table + Field
  2. All-fields wildcard — User + Table + Field No. = 0
  3. All-tables wildcard — User + Table No. = 0 + Field No. = 0

The first match wins. If no entry is found at any level, the field is unrestricted.

Example: A user has:

Resolution:

Validation Constraints

Write Guard Bypass with Wildcards

The IsFieldWriteGuardBypassed check follows the same fallback chain:

  1. Specific table + specific field
  2. Specific table + Field No. = 0 (all fields in that table)
  3. Table No. = 0 + Field No. = 0 (all tables, all fields)

A Bypass entry at the all-tables level exempts every field in every table from the Write Guard.


Setup and Configuration

Accessing the Page

  1. Open Business Central
  2. Search for "Cloud Events Field Accesses"
  3. The page opens with a user filter section at the top

Page Layout

The page is divided into two sections:

User Filter Section (Top)

Restrictions List (Bottom - Repeater)

Adding a Restriction

  1. Select User: If the page opens with no user selected, or to change users:
  1. Create New Line: Click "+ New" or press F3 to create a new restriction
  1. Select Table:
  1. Select Field:
  1. Choose Restriction Type:
  1. Save: The record is automatically saved when you move to the next field or line

User Selection Behavior

Validation Rules


Integration with Message Types

Data.Records.Get Message Type

Integration Points: 3 locations in DataRecordsGetImpl.Codeunit.al

Behavior:

Example Scenario:

Given restrictions:

Request:

{
  "specversion": "1.0",
  "type": "Data.Records.Get",
  "subject": "Customer",
  "data": "{\"fieldNumbers\":[1,2,5]}"
}

Response (without restrictions):

{
  "status": "Success",
  "result": [
    {
      "id": "...",
      "primaryKey": { "No_": "10000" },
      "fields": {
        "No_": "10000",
        "Name": "Adatum Corporation",
        "Address": "192 Market Square"
      }
    }
  ]
}

Response (with restrictions applied):

{
  "status": "Success",
  "result": [
    {
      "id": "...",
      "primaryKey": { "No_": "10000" },
      "fields": {
        "No_": "10000"
      }
    }
  ]
}

Data.Records.Set Message Type

Integration Points:

Behavior:

Example Scenario:

Given restrictions:

Request:

{
  "specversion": "1.0",
  "type": "Data.Records.Set",
  "subject": "Customer",
  "data": {
    "data": [
      {
        "primaryKey": { "No_": "TEST001" },
        "fields": {
          "Name": "New Customer Name",
          "Address": "123 Main Street"
        }
      }
    ]
  }
}

Response (with write restriction):

{
  "status": "Error",
  "message": "Invalid field \"Name\" in fields object. Field does not exist in the target table. Valid field names: No_, Address, City, ..."
}

Note: The error message indicates the field is invalid because it's been filtered out of the valid field list due to the write restriction. The trailing Valid field names: ... enumeration lists the JSON keys actually accepted for the current caller (after both schema normalisation and any field-access restrictions). When the supplied key normalises to a real field name — for example sending "No." for a field BC exposes as "No_" — the error is prefixed with Did you mean "No_"? to make self-correction trivial.


API Reference

Table: Cloud Events Field Access (65304)

Primary Key: User Security ID + Table No. + Field No.

Fields:

Field No.Field NameTypeDescription
1User Security IDGuidIdentifies the user or AAD application
2Table No.IntegerTarget table number
3Field No.IntegerTarget field number
10Restriction TypeEnumType of restriction (Both, Read, Write, Bypass)
20User NameText[250]User's friendly name (auto-populated, read-only)
21Table NameText[250]Table's caption (FlowField, read-only)
22Field NameText[30]Field's name (auto-populated, read-only)

Methods:

procedure IsFieldRestricted(UserSecurityId: Guid; TableNo: Integer; FieldNo: Integer; RestrictType: Enum "Cloud Events Restriction Type"): Boolean

Checks if a specific field is restricted for a user based on restriction type. Uses the three-level fallback chain: specific field → all-fields wildcard (Field 0) → all-tables wildcard (Table 0, Field 0).

procedure GetRestrictedFields(UserSecurityId: Guid; TableNo: Integer; RestrictType: Enum "Cloud Events Restriction Type"): List of [Integer]

Returns a list of all restricted field numbers for a table and restriction type. Includes entries from both the specific table and the all-tables wildcard (Table 0). Bypass entries are excluded from this list.

procedure IsFieldWriteGuardBypassed(TableNo: Integer; FieldNo: Integer): Boolean

Checks whether a Bypass entry exists for the given table/field combination (field-scoped, ignores User Security ID). Uses the three-level fallback: specific field → all-fields wildcard (Field 0) → all-tables wildcard (Table 0, Field 0).

Codeunit: Cloud Events Field Access (65350)

Public Methods:

procedure IsFieldReadRestricted(TableNo: Integer; FieldNo: Integer): Boolean

Checks if a field is read-restricted for the current user (UserSecurityId()). Returns true if restriction type is Read or Both.

procedure IsFieldWriteRestricted(TableNo: Integer; FieldNo: Integer): Boolean

Checks if a field is write-restricted for the current user (UserSecurityId()). Returns true if restriction type is Write or Both.

procedure IsFieldWriteGuardBypassed(TableNo: Integer; FieldNo: Integer): Boolean

Checks whether a Bypass entry exists for the given table/field combination (field-scoped, all users). The ChangeLog Write Guard checks this before evaluating Change Log coverage.


Use Cases and Examples

Use Case 1: Protect Sensitive Customer Data

Scenario: External integration should access customer records but not see credit card information.

Setup:

  1. Create restriction for User: IntegrationAppUser
  2. Table: Customer (18)
  3. Field: Credit Card No.
  4. Restriction Type: Both

Result: Integration can read and write customer data, but Credit Card No. field is never exposed in GET responses and cannot be modified via SET operations.


Use Case 2: Read-Only Financial Fields

Scenario: Integration needs to see customer balances but should never modify them (calculated fields).

Setup:

  1. Create restriction for User: IntegrationAppUser
  2. Table: Customer (18)
  3. Field: Balance (LCY)
  4. Restriction Type: Write

Result: Integration can read Balance (LCY) in GET responses but cannot modify it through SET operations.


Use Case 3: Write-Only Audit Fields

Scenario: Integration should be able to update internal notes but not read them (privacy compliance).

Setup:

  1. Create restriction for User: IntegrationAppUser
  2. Table: Customer (18)
  3. Field: Internal Notes
  4. Restriction Type: Read

Result: Integration cannot see Internal Notes in GET responses but can update them via SET operations.


Use Case 5: Write Guard Bypass — Allow API Writes Without Change Log Coverage

Scenario: An integration needs to write a specific field via Data.Records.Set but activating the Change Log for that field is not feasible or desirable. The ChangeLog Write Guard is set to Blocked.

Setup:

  1. Create a restriction for the field (user can be any active user or app; the bypass is field-scoped)
  2. Table: Customer (18)
  3. Field: External System Key (custom field)
  4. Restriction Type: Bypass

Result: The ChangeLog Write Guard allows writes to this field regardless of whether it is covered by the Change Log. No read or write restrictions are applied — the Bypass entry solely affects the Write Guard evaluation.


Use Case 4: Hide System Fields

Scenario: Hide all SystemModifiedAt/SystemModifiedBy fields from specific integration users.

Setup: Create Both restrictions for multiple tables and fields:

Result: System audit fields are completely hidden from the integration user.


Use Case 6: Block All API Access for a User (All Tables Wildcard)

Scenario: Completely block a user from reading or writing any data via the Cloud Events API.

Setup:

  1. Create restriction for User: TemporaryBlockedUser
  2. Table No.: 0 (all tables)
  3. Field No.: 0 (automatically set when Table No. = 0)
  4. Restriction Type: Both

Result: The user cannot read any field from any table via Data.Records.Get, and all Data.Records.Set operations are rejected. This is equivalent to a full API lockout for the user without removing their BC user account.


Use Case 7: Read-Only API Access Across All Tables

Scenario: An integration user should be able to query data from any table but must never modify records.

Setup:

  1. Create restriction for User: ReadOnlyIntegration
  2. Table No.: 0 (all tables)
  3. Field No.: 0 (automatically set)
  4. Restriction Type: Write

Result: The user can read all fields from all tables via Data.Records.Get but any Data.Records.Set operation is rejected.


Permission Requirements

To Manage Restrictions (Administrators)

Users managing field restrictions need:

To Be Subject to Restrictions (API Users)


Best Practices

Security Strategy

  1. Principle of Least Privilege: Start with the most restrictive setting (Both) and relax as needed
  2. Audit Trail: Keep track of why each restriction was created (consider adding documentation)
  3. Regular Review: Periodically audit restrictions to ensure they're still necessary
  4. Test Integration Impact: Before deploying restrictions, test with sample data to verify integration behavior

Performance Considerations

  1. Minimal Overhead: Restriction checks are simple GUID+Integer primary key lookups (very fast)
  2. Caching Opportunity: Consider caching restriction lists if making many calls for the same user/table
  3. Bulk Operations: Restrictions are checked per-field, not per-record, so bulk operations scale well

Troubleshooting

Symptom: Field not appearing in Data.Records.Get response

Possible Causes:

  1. Read or Both restriction exists for the user/field
  2. Field doesn't exist in the table
  3. Field is not enabled or is obsolete
  4. User lacks read permission on the table

Diagnosis: Check Cloud Events Field Accesses page for the user in question.


Symptom: Data.Records.Set returns error "Invalid field"

Possible Causes:

  1. Write or Both restriction exists for the user/field
  2. Field doesn't exist in the table
  3. Field is not a normal field (FlowField, FlowFilter)
  4. User lacks write permission on the table

Diagnosis: Check Cloud Events Field Accesses page for the user in question.


Administrative Actions

Delete All Restrictions for a User

The page includes an action "Delete All for User":

  1. Select the user using the User Name filter
  2. Click Actions → Delete All for User
  3. Confirm the deletion
  4. All restrictions for that user are removed

Bulk Setup via AL Code

For programmatic setup of restrictions:

var
    FieldAccess: Record "Cloud Events Field Access";
begin
    FieldAccess.Init();
    FieldAccess."User Security ID" := IntegrationUserSecurityId;
    FieldAccess."Table No." := Database::Customer;
    FieldAccess."Field No." := 2; // Name field
    FieldAccess."Restriction Type" := FieldAccess."Restriction Type"::Read;
    FieldAccess.Insert(true); // Validates and populates User Name, Field Name
end;

Technical Implementation Details

How Restrictions Are Enforced

Data.Records.Get (Read Enforcement):

// Pseudo-code from DataRecordsGetImpl
foreach Field in RequestedFields do begin
    if not FieldRestrictionMgt.IsFieldReadRestricted(TableNo, Field."No.") then
        AddFieldToJson(ResponseJson, Field);
    // Restricted fields are silently omitted
end;

Data.Records.Set (Write Enforcement):

// Pseudo-code from DataRecordsSetProcess
local procedure GetFieldsAsList(TableId: Integer; FieldList: List of [Text])
begin
    foreach Field in Table do begin
        if not FieldRestrictionMgt.IsFieldWriteRestricted(TableId, Field."No.") then
            FieldList.Add(Field.FieldName);
    end;
    // Restricted fields are excluded from valid field list
    // Later validation rejects fields not in valid field list
end;

User Identification

Field restrictions use the calling user's Security ID:

Buffer Table Integration

The CE User/App Buffer (Table 65305) combines:

This allows restrictions to be applied to both human users and application service principals.


Extensibility

Extending Restriction Types

The Cloud Events Restriction Type enum is marked as Extensible = true:

enumextension 50100 "My Restriction Types" extends "Cloud Events Restriction Type"
{
    value(50100; "Custom Restriction")
    {
        Caption = 'Custom Restriction';
    }
}

Note: Custom restriction types require custom logic implementation as the core system only recognizes Both, Read, and Write.

Subscribing to Restriction Events

Currently, the system does not publish events when restrictions are applied. Consider adding integration events if you need to:


Related Documentation


Summary

Field Access Restrictions provide fine-grained security control for Cloud Events API operations by:

This feature enables administrators to meet compliance requirements, protect sensitive data, and implement principle of least privilege while maintaining API flexibility.


© Origo – Cloud Events Base Extension