The acre Access Control permission system provides fine-grained control over who can do what within your system. This guide explains how to configure, manage, and troubleshoot permissions for users and groups.
Key Concept: Permissions are defined by Operation Rights that specify which Actions (Read, Create, Update, Delete, etc.) are allowed or denied for specific System Items (object types like Person, Controller, AccessLevel).
Before diving into details, here are the most common, very basic, permission patterns:
| Scenario | System | Actions | Grant Type |
|---|---|---|---|
| Read-Only User | All Types | Read | Grant |
| Cardholder Admin | Person | Read, Create, Update, Delete | Grant |
| Hardware Technician | Controller, Reader, Door | Read, Create, Update | Grant |
| Event Viewer | EventMessage | Read | Grant |
| Full Administrator | All Types | All Actions | Grant |
Permissions are configured using OperationRightInfo objects with these key properties:
| Property | Type | Description |
|---|---|---|
System |
string | The object type(s) this permission applies to |
Actions |
string[] | What operations are allowed/denied |
GrantType |
GrantType | Grant (allow) or Deny (block) |
Enabled |
bool | Whether this permission is active |
Tags |
string[] | Optional: limit to tagged objects only |
// Example: Create a permission allowing read access to People
var readPeoplePermission = new OperationRightInfo
{
CommonName = "Read People",
System = "Person",
Actions = new[] { "Read" },
GrantType = GrantTypes.Grant,
Enabled = true
};
System Items define which object types a permission applies to. These names match how objects are stored in MongoDB (without the “Info” suffix).
| System Item | Description | Example Use Case |
|---|---|---|
All Types |
Everything (BaseInfo) | Full administrator access |
Person |
Cardholders | Cardholder management |
AccessLevel |
Access rules | Access configuration |
Schedule |
Time schedules | Schedule management |
Controller |
All controllers | Hardware technicians |
MercuryController |
Mercury controllers only | Vendor-specific access |
EngageReader |
Allegion Engage locks | Wireless lock management |
Door |
All door types | Door operations |
EventMessage |
System events | Event viewing/reporting |
User |
System users | User administration |
Group |
Permission groups | Group management |
Folder |
Organizational folders | Folder organization |
Instance |
Instance management | Instance administrators |
The following system items are available:
AccessLevel, AlarmAction, AlarmDefinition, AreaAssignment, Attendee,
AwsKinesisAlarmAction, AwsKinesisEventDestination, AwsSnsAlarmAction,
AwsSnsEventDestination, AwsSqsAlarmAction, AwsSqsEventDestination,
BadgeType, BadgeTypeField, BoschArea, BoschAuthorityLevel, BoschDevice,
BoschDoor, BoschOutput, BoschPanel, BoschPoint, BoschService, BoschSked,
BoschUser, Camera, CardFormat, Controller, Downstream,
ElevatorAccessLevel, EngageIpGateway, EngageReader, EngageSite,
EventMessage, ExecuteProcedureAction, Floorplan, FloorplanItem, Folder,
Generic, Group, HgProcedure, HgTrigger, Holiday, ImagePointer,
InputScanner, Instance, InstanceSettings, KeepObject, KoneCabinAccess,
LdapAgent, License, LocalArea, MercuryInput, MercuryOutput,
MercuryReader, MessageTemplate, NotificationAction, Person, Schedule,
ServiceContainer, User, Visit, VisitorManagementSettings, WebHook
Tip: Use
All Types(which maps toBaseInfo) to apply permissions to all object types at once.
Actions define what operations a user can perform on the specified system items.
| Action | When Required | Description |
|---|---|---|
Read |
Viewing objects | Required to see any object in lists or details |
Create |
Adding new objects | Required to POST new objects |
Update |
Modifying objects | Required to PUT changes to existing objects |
Delete |
Removing objects | Required to DELETE objects |
Publish |
Creating events | Required to publish custom events |
Linking |
Connecting objects | Required to add/remove object relationships |
Linking permissions control object relationships:
// Allow linking any object type to Access Levels
var linkingPermission = new OperationRightInfo
{
CommonName = "Link to Access Levels",
System = "AccessLevel",
Actions = new[] { "Linking" },
GrantType = GrantTypes.Grant,
Enabled = true
};
// Use wildcard * for system to link any type
var wildcardLinking = new OperationRightInfo
{
CommonName = "Universal Linking",
System = "*",
Actions = new[] { "Linking" },
GrantType = GrantTypes.Grant,
Enabled = true
};
You can define custom action names for specialized permissions:
// Custom action for a specific operation
var customAction = new OperationRightInfo
{
CommonName = "Allow Card Enrollment",
System = "Person",
Actions = new[] { "EnrollCard" }, // Custom action name
GrantType = GrantTypes.Grant,
Enabled = true
};
| Grant Type | Behavior | Precedence |
|---|---|---|
Grant |
Allows the specified action | Lower priority |
Deny |
Blocks the specified action | Higher priority |
Critical Rule: Deny always wins. If a user has both a Grant and Deny for the same system/action, the Deny takes precedence.
// User is in two groups with conflicting permissions:
// Group A: Grants delete on all types
var groupAPermission = new OperationRightInfo
{
System = "All Types",
Actions = new[] { "Delete" },
GrantType = GrantTypes.Grant, // Allow delete
Enabled = true
};
// Group B: Denies delete on Person
var groupBPermission = new OperationRightInfo
{
System = "Person",
Actions = new[] { "Delete" },
GrantType = GrantTypes.Deny, // Block delete
Enabled = true
};
// Result: User CAN delete Controllers, Readers, etc.
// Result: User CANNOT delete People (Deny wins)
The Enabled property provides a quick way to toggle permissions without deleting them:
// Get the operation right by href
var permissionHref = $"/api/f/{instanceKey}/operationrights/{permissionKey}";
var tempPermission = await client.GetByHrefAsync<OperationRightInfo>(permissionHref);
// Temporarily elevate permissions
tempPermission.Enabled = true; // Activate
await client.UpdateOperationRightAsync(tempPermission);
// ... user performs elevated task ...
// Deactivate
tempPermission.Enabled = false;
await client.UpdateOperationRightAsync(tempPermission);
Use Cases:
Limit permissions to objects with specific tags:
// Only allow access to "vip" tagged people
var vipOnlyPermission = new OperationRightInfo
{
CommonName = "VIP Cardholder Management",
System = "Person",
Actions = new[] { "Read", "Update" },
GrantType = GrantTypes.Grant,
Enabled = true,
Tags = new[] { "vip" } // Only applies to tagged objects - must be lowercase
};
How It Works:
person.Tags = new[] { "vip" };Permissions respect the object type hierarchy:
ControllerInfo (base)
├── MercuryController
│ ├── Ep1501Info
│ ├── Lp1502Info
│ └── Lp4502Info
├── EngageSite
└── BoschPanel
Example: A permission on Controller automatically applies to all derived types (MercuryController, EngageSite, BoschPanel, etc.):
// This grants access to ALL controller types
var allControllersPermission = new OperationRightInfo
{
System = "Controller", // Base type
Actions = new[] { "Read", "Create", "Update" },
GrantType = GrantTypes.Grant,
Enabled = true
};
// This grants access ONLY to Mercury controllers
var mercuryOnlyPermission = new OperationRightInfo
{
System = "MercuryController", // Specific type
Actions = new[] { "Read", "Create", "Update" },
GrantType = GrantTypes.Grant,
Enabled = true
};
// Create a group for event viewers
var eventViewer = new GroupInfo
{
CommonName = "Event Viewers"
};
var group = await client.AddGroupAsync(instance, eventViewer);
// Create operation right for viewing events
var viewEventsPermission = new OperationRightInfo
{
CommonName = "View Events Only",
System = "EventMessage",
Actions = new[] { "Read" },
GrantType = GrantTypes.Grant,
Enabled = true
};
// Add the operation right - requires appCode and credentialKey
// These associate the right with the application and user/group credential
await client.AddOperationRightsAsync(instance, "YourAppCode", group.Key, viewEventsPermission);
var cardholderAdminPermissions = new[]
{
new OperationRightInfo
{
CommonName = "Manage People",
System = "Person",
Actions = new[] { "Read", "Create", "Update", "Delete" },
GrantType = GrantTypes.Grant,
Enabled = true
},
new OperationRightInfo
{
CommonName = "View Access Levels",
System = "AccessLevel",
Actions = new[] { "Read" },
GrantType = GrantTypes.Grant,
Enabled = true
},
new OperationRightInfo
{
CommonName = "View Schedules",
System = "Schedule",
Actions = new[] { "Read" },
GrantType = GrantTypes.Grant,
Enabled = true
}
};
// Get the admin group by searching for it
var adminGroup = (await client.SearchAsync(instance, "{\"_t\":\"Group\",\"CommonName\":\"Admins\"}"))
.OfType<GroupInfo>().FirstOrDefault();
// Add each permission for the group
foreach (var permission in cardholderAdminPermissions)
{
await client.AddOperationRightsAsync(instance, "YourAppCode", adminGroup.Key, permission);
}
// Grant hardware access
var hardwarePermissions = new[]
{
new OperationRightInfo
{
System = "Controller",
Actions = new[] { "Read", "Create", "Update" },
GrantType = GrantTypes.Grant
},
new OperationRightInfo
{
System = "Reader",
Actions = new[] { "Read", "Create", "Update" },
GrantType = GrantTypes.Grant
},
new OperationRightInfo
{
System = "Door",
Actions = new[] { "Read", "Create", "Update" },
GrantType = GrantTypes.Grant
}
};
// Explicitly deny access to cardholder data
var denyCardholders = new OperationRightInfo
{
System = "Person",
Actions = new[] { "Read", "Create", "Update", "Delete" },
GrantType = GrantTypes.Deny, // Explicit deny
Enabled = true
};
CommonName valuesCause: User lacks required permission for the operation.
Debug:
// Get user by href
var userHref = $"/api/f/{instanceKey}/users/{userId}";
var user = await client.GetByHrefAsync<UserInfo>(userHref);
// Get all operation rights for the instance
var allRights = await client.GetOperationRightsAsync(instance);
// Filter to rights associated with this user's credential
var userRights = allRights.Where(r =>
r.ObjectLinks.Any(ol => ol.Relation == "Credential" && ol.LinkedObjectKey == user.Key));
foreach (var perm in userRights)
{
Console.WriteLine($"{perm.System}: {string.Join(", ", perm.Actions)} ({perm.GrantType})");
}
Possible Causes:
MercuryController vs Controller)Solution: Ensure user is in a group with at least Read permission on required types:
var minimumReadPermission = new OperationRightInfo
{
CommonName = "Basic Read Access",
System = "All Types",
Actions = new[] { "Read" },
GrantType = GrantTypes.Grant,
Enabled = true
};
A user must be able to Read the Instance they are in, as well as the InstanceSetting.
| Method | Description |
|---|---|
GetOperationRightsAsync(folder) |
Get all operation rights in a folder |
AddOperationRightsAsync(folder, appCode, credentialKey, item, appliesTo) |
Create a new operation right |
UpdateOperationRightsAsync(item) |
Update an operation right |
DeleteOperationRightsAsync(item) |
Delete an operation right |
VerifyOperationRightsAsync(folder, request) |
Verify operation rights for objects |
Note: Operation rights are associated with users/groups through the
credentialKeyparameter when creating them. TheappCodeidentifies the application context.