Skip to main content
Version: Current

Security

Overview

CasualOS supports various kinds of permanent storage. Key/Value storage is supported by data records, and blob storage is supported by file records. There are also event records, and more kinds of functionality will be coming in the future. Collectively, it is useful for think about all these kinds as "resources". Resources are stored in records, and access to resources is governed by three things:

  • Resource Markers
  • Permissions
  • Roles

Let's start with the first:

Resource Markers

Resource Markers (also known as Record Markers) are indicators that are used to indentify which policies govern the usage of which resources. For now, markers are stored alongside the resource and are simply the a that is used to govern access to the resource.

If a resource marker is assigned to a resource, then the permissions for that marker name controls how access to that resource is managed. A resource can be assigned multiple resource markers, and when such a case arises, then any permission from either of the markers can be used to grant access to the resource.

Permissions

Permissions are rules that grant access to resources. By default, CasualOS denies all access to resources, so permissions need to be used to grant any sort of ability to read, update, create, or delete resources. All permissions grant access to a subject, which is either an identity (user or inst), or a role. See the section below on subjects for more information. Permissions can also limit the scope of their grant by specifying a specific resource kind, or action that may be performed.

As mentioned above, permissions control access to resources. For any request, the system searches for a permission that matches the current identity(s) and resource. If a permission is found, then the request is allowed. If none is found, then the request is denied. Some requests have multiple identities. For example, a request may be coming from inside an inst. In such a case, the system checks if both the user and the inst have the necessary permissions to access the resource. If either of them do not, then the request is denied.

There are two kinds of permissions:

  • Marker Permissions
  • Resource Permissions

Marker permissions are used to grant access to resources that have a specific resource marker applied to them. For example, if a data record has the secret resource marker applied to it, then the marker permissions for secret control how access is granted.

They have the following structure:

let markerPermission: {
/**
* The name of the resource marker that this permission grants access to.
*/
marker: string,

/**
* The kind of the resource.
* If null, then the permission grants access to every resource that has the marker.
*/
resourceKind: 'data' | 'file' | 'event' | 'inst' | null,

/**
* The action that the permission grants access to.
* If null, then the permission grants access to every action.
*/
action: 'create' | 'read' | 'update' | 'delete' | 'list' | 'increment' | 'count' | null;

/**
* The type of the subject that the permission grants access to.
* If 'role', then the permission grants access to any identity that has the role.
* If 'user', then the permission grants access to a specific user.
* If 'inst', then the permission grants access to a specific inst.
*/
subjectType: 'role' | 'user' | 'inst';

/**
* The ID of the subject.
* If the subjectType is 'role', then this is the name of the role.
* If the subjectType is 'user', then this is the ID of the user.
* If the subjectType is 'inst', then this is the ID of the inst.
*/
subjectId: string;

/**
* The expiration time of the permission in unix time in milliseconds.
* If null, then the permission does not expire.
*/
expireTimeMs: number | null;

/**
* Additional options for the permission.
* This is always an empty object for now.
*/
options: {}
}

Resource permissions are used to grant access to specific resources.

They have the following structure:

let resourcePermission: {
/**
* The kind of the resource.
*/
resourceKind: 'data' | 'file' | 'event' | 'inst',

/**
* The ID of the resource.
* For data records, this is the address of the record.
* For file records, this is the name of the file.
* For event records, this is the name of the event.
* For insts, this is the ID of the inst.
*/
resourceId: string;

/**
* The action that the permission grants access to.
* If null, then the permission grants access to every action.
*/
action: 'create' | 'read' | 'update' | 'delete' | 'list' | 'increment' | 'count' | null;

/**
* The type of the subject that the permission grants access to.
* If 'role', then the permission grants access to any identity that has the role.
* If 'user', then the permission grants access to a specific user.
* If 'inst', then the permission grants access to a specific inst.
*/
subjectType: 'role' | 'user' | 'inst';

/**
* The ID of the subject.
* If the subjectType is 'role', then this is the name of the role.
* If the subjectType is 'user', then this is the ID of the user.
* If the subjectType is 'inst', then this is the ID of the inst.
*/
subjectId: string;

/**
* The expiration time of the permission in unix time in milliseconds.
* If null, then the permission does not expire.
*/
expireTimeMs: number | null;

/**
* Additional options for the permission.
* This is always an empty object for now.
*/
options: {}
}

Permissions can be created by calling os.grantPermission().

Roles

Roles are like resource markers, but they are applied to identites. Identities in this case are users and instances, but more will come in the future.

Generally, any system that is able to act on its own is an identity. Users are able to act on their own, so they are identities. Insts are also able to act (via code), so they also get their own identites.

Like resource markers, users and insts can be assigned multiple roles and they are then used to associate users & insts with specific permissions from a policy.

Unlike resource markers, roles can have additional rules that determine when a user can impersonate them. For example, a role can be set to expire after a set time so that a user only has limited access to a resource.

Many permissions require that both the user and the inst be authorized to access the resource. As a result, if the user needs to use a role to access the resource, but the inst does not, then the request is rejected. This is to help prevent the Confused Deputy Problem, where the user is logged in but the inst tries to access something that is shouldn't have access to.

Users can be granted roles by calling os.grantUserRole(). Insts can be granted roles by calling os.grantInstRole().

Subjects

Subjects are the entities that permissions can grant access to. There are three kinds of subjects:

  • Roles
  • Users
  • Insts

Roles are a way of grouping multiple identities together and granting the same permissions to them. For example, if user1 and user2 both have the developer role, then they both have all the permissions that are granted to the developer role.

Users are the people who use the system. They are the ones who log in and interact with the system. Permissions can grant access to a specific user by using their ID. You can find the ID of the current user by getting the ID of the authBot.

Insts are user scripts that run inside CasualOS. They can perform actions on behalf of the user, and can be granted permissions to access resources. As such, insts cannot grant permissions by themselves, but must always be accompanied by a user. This means that resources can only be accessed by an inst if the user also has been granted permissions.

Permissions can grant access to a specific inst by using their ID. Inst IDs are formatted like this: {recordName}/{inst}, where {recordName} is the name of the record that the inst resides in, and {inst} is the name of the inst. For public/free insts and static/local insts, the {recordName} portion is omitted and the ID is just {inst} or /{inst}.

Default Permissions & Roles

By default, CasualOS grants some permissions by default to make usage easier and prevent users from getting locked out.

  • Record owners and studio admins full access to all resources in the record.
  • Any user or inst that is granted the admin role has full access to all resources in the record.
  • Studio members have read/write access to all resources in the record, but they cannot change permissions or roles.
  • Private Insts are granted full access to the records that they live inside. This is to allow insts to read and write data without needing to be granted permissions.

CasualOS also provides some default markers:

  • publicRead - This marker can be used to grant read access to any user or inst. It is useful for making resources able to be accessed by anyone, but only updated by authorized users.
  • publicWrite - This marker can be used to grant read/write access to any user or inst. It is useful for making resources able to be accessed and updated by anyone.
  • private - This marker is used for all list actions that do not list resources by a marker. Only admin users have access to these resources by default.
  • account - This marker is used for all role and permission resources. Only admin users have access to these resources by default.

Key Points

Permissions and Roles control who is allowed to access specific resources, and are a powerful tool for managing permissions and access.

  • Marker permissions map resource markers to subjects.
  • Resource permissions map resources to subjects.
  • Roles are a way of grouping multiple identities together.
  • Resource markers are applied to resources and determine which permissions apply to the resource.
  • Roles are assigned to identities and determine which specific permissions an identity can use.