Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Access Control (IC)

Note: This is the IC-specific access control guide. Access control is an IC-only feature.

ic-dbms uses a granular Access Control List (ACL) keyed by Principal. Each identity carries an IdentityPerms record:

FieldTypeMeaning
adminboolBypass all per-table checks. Does NOT imply other ops.
manage_aclboolGrant/revoke perms; add/remove identities.
migrateboolRun migrate / pending_migrations / has_drift.
all_tablesTablePermsPer-op bits applied to every table.
per_tableVec<(Table, TablePerms)>Per-table additive grants.

TablePerms is a u8 bitfield over READ, INSERT, UPDATE, DELETE.

admin bypasses table checks but does not silently elevate to manage_acl or migrate — a data admin cannot escalate to ACL/ops roles by accident.

Initialization

#![allow(unused)]
fn main() {
use ic_dbms_api::prelude::IcDbmsCanisterInitArgs;

let args = IcDbmsCanisterInitArgs {
    allowed_principals: Some(vec![operator_principal]),
};
}

Bootstrap rules:

allowed_principalsResult
NoneDeployer principal becomes a full admin.
Some(vec![])Same as None — deployer becomes a full admin.
Some(vec![p, q])Each listed principal becomes a full admin.

A “full admin” carries admin = true, manage_acl = true, migrate = true, and all_tables = TablePerms::all().

Endpoints

Operational flags

EndpointRequired permEffect
grant_adminmanage_aclSet admin on target.
revoke_adminmanage_aclClear admin on target.
grant_manage_aclmanage_aclSet manage_acl on target.
revoke_manage_aclmanage_aclClear manage_acl on target.
grant_migratemanage_aclSet migrate on target.
revoke_migratemanage_aclClear migrate on target.

Table perms

EndpointRequired permEffect
grant_all_tables_permsmanage_aclOR perms into all_tables.
revoke_all_tables_permsmanage_aclMask perms out of all_tables.
grant_table_permsmanage_aclOR perms into per_table[table].
revoke_table_permsmanage_aclMask perms out of per_table[table].

Identity lifecycle

EndpointRequired permEffect
remove_identitymanage_aclDrop the identity entirely.
list_identitiesmanage_aclList every identity with its perms.
my_perms(none)Return the caller’s own perms.

CRUD enforcement

#[derive(DbmsCanister)] injects a granted check before each generated endpoint:

Endpoint kindRequired perm
select_* / aggregate_* / selectTablePerms::READ
insert_*TablePerms::INSERT
update_*TablePerms::UPDATE
delete_*TablePerms::DELETE

Effective check: admin || (all_tables | per_table[table]).contains(required).

select_join enforces READ on the root table only. Joined tables are not checked separately in v1.

Migration

EndpointRequired perm
has_driftmigrate
pending_migrationsmigrate
migratemigrate

Transactions

begin_transaction / commit / rollback are unconditional — per-op CRUD checks gate the data accesses inside the transaction. An identity with no perms can open and commit an empty transaction; the moment it tries to read or write, AccessDenied is returned.

Last-manage_acl guard

revoke(ManageAcl) and remove_identity refuse the operation when it would leave the ACL with zero manage_acl-carrying identities:

DbmsError::Memory(MemoryError::ConstraintViolation(
    "at least one identity must retain manage_acl"
))

admin and migrate carry no such guard — they can be re-granted from any manage_acl holder.

Errors

A failed perm check returns:

#![allow(unused)]
fn main() {
DbmsError::AccessDenied {
    table: Option<TableFingerprint>,
    required: RequiredPerm,
}
}

RequiredPerm enumerates the missing perm class:

  • RequiredPerm::Table(TablePerms) — a table operation.
  • RequiredPerm::Admin — admin bypass missing.
  • RequiredPerm::ManageAcl — ACL management missing.
  • RequiredPerm::Migrate — migration missing.

Recipes

Read-only viewer

#![allow(unused)]
fn main() {
client.grant_all_tables_perms(viewer, TablePerms::READ).await?;
}

Per-table writer

#![allow(unused)]
fn main() {
client.grant_table_perms(svc, "users", TablePerms::INSERT | TablePerms::UPDATE).await?;
}

Migration bot

#![allow(unused)]
fn main() {
client.grant_migrate(bot).await?;
}

ACL deputy

#![allow(unused)]
fn main() {
client.grant_manage_acl(deputy).await?;
}