Add Access Controls to a Type Using AclEnabled
You can use the ConfigAclEnabled or AclEnabled Types to enable access control authorization on a given Type, so that the object maps a path from itself to a user.
See Define Permissions to learn more about access controls in the C3 Agentic AI Platform.
AclEnabled compared to ConfigAclEnabled
Use the AclEnabled Type to add access controls to an entity Type and upsert the configuration in your application database. With the AclEnabled Type, the record is only accessible within the application and suits more frequent updates.
Use the ConfigAclEnabled Type to add access controls to a non-entity Type and store the record as a configuration. With ConfigAclEnabled Type, the record is accessible across applications and suits less frequent updates. To learn how to use the ConfigAclEnabled Type, see Add Access Controls to a Type Using ConfigAclEnabled.
This topic shows you how to use the AclEnabled Type to add access controls to a Type.
Add access controls using AclEnabled
The following Types allow you to enable and manage access control on a user-defined Type:
- AclPrivilege: Use to create an object that defines a Type, access to the object itself, and a path to reference users, roles, or groups.
- EnableAclPrivilege: Use to create an object that enables access controls for a given Type.
- AclEnabled: Mix this Type with the Type you want to add access controls to.
- AclEntry: An implicitly created record that defines various actions an authorized user can perform on the ACL-enabled Type.
- AclEnabledTypes: Use to enable or disable access controls for Types that mix AclEnabled.
To set up access controls in an object-to-user approach, complete the following steps:
- Create an AclPrivilege object.
- Create an EnableAclPrivilege object.
- Enable access control on an entity Type.
After you set up access controls for a user-defined Type, a user can then create an instance of the Type, upsert it, and access the object.
Create an AclPrivilege object
Create an AclPrivilege object and add it to your application package in <pkgname>/seed/AclPrivilege/<object>.json. This object defines a path from a Type to users who are allowed to access it.
Here is an example object Foo_AclPrivilege.json:
{
"id" : "Foo_AclPrivilege",
"typeName" : "Foo",
"canUpdate" : "true",
"canRemove" : "true",
"canModifyAcl" : "false",
"acl" : {
"expr" : "(id == _context.userName)"
}
}This object contains the following fields:
typeName: Name of the Type you are granting access to.canUpdate: Describes who can update this seed data object. In this example, you allow users this permission to be able to update the Foo Type.canRemove: Describes who can remove this seed data object. In this example, you allow users with this permission to remove the Foo Type.canModifyAcl: Describes whether users granted access by this AclPrivilege object can modify the AclEntry objects created by AclEnabled#populateACL. See the following "AclEntry object" section to learn more about AclEntry objects.acl.expr: An expression that defines a path fromtypeNameto one or more users, roles, or groups. In this example,(id == _context.userName), you grant users access to an instance of the Foo Type with anidthat matches the user's username.
Expressions in access controls
You can also use fuctions from ExpressionEngineFunction to define the access control logic. See Use Expression Functions and the the "DataPermissions" section Define Permissions.
Create an EnableAclPrivilege object
Create an EnableAclPrivilege object and add it to your application package in <pkgname>/seed/EnableAclPrivilege/<object>.json. This object enables or disables access controls for a Type.
Here is an example object Foo_EnableAclPrivilege.json:
{
"id": "Foo_EnableAclPrivilege",
"enabled": true,
"typeName": "Foo"
}This object contains the following fields:
"enabled": Describes whether access controls for the given Type are enabled."typeName": Specifies the Type you are configuring access controls for.
The system implicitly creates an AclEntry record
When a user creates new objects using Types that mix AclEnabled, the system implicitly creates an AclEntry record on that object for the current user so they can access it.
The target object maintains a reference to all AclEntry objects in the acl field of the AclEnabled mixin.
For example, a single Foo instance needs an AclEntry object for each authorized user:
[
{
"type": "AclEntry",
"canUpdate": true,
"canRemove": true,
"canModifyAcl": true,
"member": {
"id": "userB"
},
"source": "Default"
}
]This object contains the following fields:
member: Indicates who is authorized to read, update, or remove the object instance. This field can either be User, Role, or UserGroup.canUpdate: Describes who can update this seed data object.canRemove: Describes who can remove this seed data object.canModifyAcl: Describes whether the users authorized by this AclEntry object are able to modify the AclEntry object itself.source: Indicates how the AclEntry object was created. This is a legacy field that you can ignore.
Enable access control on an entity Type
Create a user-defined entity Type as the target object and mix the AclEnabled Type. Add the Type to your application package in <pkgname>/src/<Type>.c3typ. This example uses the Foo Type:
entity type Foo mixes AclEnabled {
id: !string
name: !string
description: string
}Demonstrate access controls on a Type
The following example demonstrates access to the Foo object for your own user and userB.
- Run the following code in C3 AI Console to create an instance of Foo and and upsert it:
Foo.make({
id: '<yourUserId>', // Replace with your username
name: '<name>',
description: '<description>'
}).upsert()This creates an AclEntry object for your user. Your user has access to your own instance of the Foo object according to the expression (id == _context.userName) defined in the AclPrivilege object.
- Create a role for userB and add it to
<pkgname>/metadata/Role/<Role>.json:
{
"id" : "Foo.Role",
"permissions": [
"allow:Foo::*"
]
}- Run the following code in C3 AI Console to create a test user for userB and assign the role
Foo.Role:
TestIdp.createTestUser("userB", "Password1!", ["Foo.Role"])Run the following code in C3 AI Console to create an instance of Foo and upsert it as userB:
TestIdp.executeAsUser("userB", () => Foo.make({
id: 'userB',
name: '<name>',
description: '<description>'
}).upsert())This creates an AclEntry object for userB. userB has access to their own instance of the Foo object according to the expression (id == _context.userName) defined in the AclPrivilege object.
- To demonstrate access controls, run the following code in C3 AI Console as userB to attempt to update your user's instance Foo:
TestIdp.executeAsUser("userB", () => Foo.make({id: '<yourUserID>'}).get().withName("<newname>").upsert())This code throws an authorization error because userB does not have access to update another user's Foo object according to the expression (id == _context.userName) defined in the AclPrivilege object. This code would run successfully if the id field matches the username userB.
Similarly, run the following code in C3 AI Console as userB to attempt to fetch your user's instance of Foo:
TestIdp.executeAsUser("userB", () => Foo.fetch({filter: "id == <yourUserID>"}));This code returns zero Foo objects because userB does not have access to fetch another user's Foo object according to the expression (id == _context.userName) defined in the AclPrivilege object. This code would run successfully if the id field matches the username userB.
Disable access control on a Type
After you configure access controls on a Type, you can disable it.
Run the following code in C3 AI Console to disable access controls for the access control enabled Type, Foo:
EnableAclPrivilege.make({
id: "Foo_AclPrivilege",
enabled: false,
typeName: "Foo"
}).upsert()After you disable access controls on the Foo object, the system ignores the AclEntry expression in the AclPrivilege object, and respects the permission string configuration instead.
In this example, userB and your user can both update or fetch any Foo objects after you disable access controls on the Foo Type.
Alternative to using AclEnabled
Alternatively, you can configure data permissions in a role to define access controls at the user level instead of the object level.
To learn how to configure data permissions, see Add Access Controls to a Role using Data Permissions.