Skip to content

Permissions and privacy

Permissions control who can read and write values in SKDB. Privacy is the use of permissions, and other SKDB features, to achieve the data privacy that users need. SKDB's permission model is carefully designed to integrate with SKDB's other features to allow users to define different notions of privacy suitable to their purposes. Crucially, SKDB is "private by default": users must explicitly grant others the ability to read or manipulate data.

The basics

From the perspective of permissions, SKDB contains two components: the core SQL engine; and a replication layer. Non-root users are only able to access the replication layer, which then communicates with the SQL engine. Permissions are enforced in the replication layer. This allows the replication layer to make use of normal SQL concepts without adding any complexity to the SQL engine itself. Meta-tables (i.e. tables whose names have special meaning to SKDB) and meta-columns (i.e. columns whose names have special meaning to SKDB) allow SKDB users to specify various kinds of permissions with ease.

Permissions

Concepts: users, groups, and permissions

The core concepts in SKDB's permissions models are:

Concept Meta-name Description
Users userID A single user
Groups groupID A group of users
Permissions Various { Delete, Insert, Read }

As a first approximation, users, groups, and permissions can be thought of as similar to their namesakes in file systems (for example, users can appear in zero more groups). However, as we will see later, permissions are looked up with a level of indirection that makes SKDB's permissions model more powerful than found in file systems.

permissions is a bit field whose values can be ORed together:

Mnemonic Bit Meaning if set to 1
d 0 User can delete the associated value(s).
i 1 User can insert the associated value(s).
r 2 User can read the associated value(s).

You can use the skdb_permission('...') convenience function to create this bit field. For example, skdb_permission('dir') is equivalent to the value 7, and allows users full access. For convenience, an additional non-primitive mnemonic w is defined, which is equivalent to di (i.e. w maps to the intuitive notion of write).

Meta-columns

skdb_access

Tables and reactive views that define a column named skdb_access with type STRING can control who can read/write/delete existing rows in that column. The value of this column for any given row can reference either a userID or a groupID. To make life easier, SKDB provides several pre-defined groups whose names map to their intuitive meaning: read-only, read-write, and write-only.

Tables and reactive views that no not define a column with a name of skdb_access are implicitly invisible to remote users. In other words, SKDB makes sure that you can't accidentally expose data to users: you have to explicitly use skdb_access to do so.

skdb_author

Tables that define a column named skdb_author with type STRING have the userID of the creator of each row recorded. Users can only record their own userID in that column — attempting to record another user's userID will be rejected by the server. Thus skdb_author provides an assurance of data integrity: you always know who created a given row.

Meta-tables

skdb_groups and skdb_group_permissions

SKDB's permissions system rests on group IDs defined in the meta-tables skdb_groups and skdb_group_permissions. In essence, a group has one or more administrators (note that root is implicitly a member of every group) who are able to add/remove other users to that group. Each user in that group can have their default permissions customised. The per-language APIs provide convenient ways of manipulating groups.

User-defined privacy

Users can use the permissions model to define privacy of their choosing. For example, for a sensitive table, one may wish to prevent all users from accessing it except those explicitly desired. Using the JavaScript API one can define groups and permissions to model this:

// Create a new group `g`.
let g = await localDb.createGroup();
// By default prevent users from reading or writing to the group.
g.setDefaultPermission("");
// Add the user "Alice", whose uid is in `alice_uid`, to the group with
// read-write permissions.
group.setMemberPermission(alice_uid, "rw");
// Add the user "John", whose `uid` is stored in `john_uid`, to the group, with
// read-only permissions.
g.setMemberPermission(john_uid, "r");

setDefaultPermission("") makes the group private by default. The subsequent calls to setMemberPermission give Alice read-write permissions, and Bob read-only permissions.

Once these permissions are activated, the system ensures they're strictly enforced:

  • For example, Bob won't be able to write a row where skdb_access matches our group ID, for any table, both on the client and on the server. That's because Bob was only granted reading access.

  • On the other hand, Alice will be able to write rows that set skdb_access to the group ID we just defined. That's because Alice was granted write access. When she writes such a row, the result is visible by both Bob and John, because they have reading access.