Search
ctrl+/
Ask AI
ctrl+.
Light
Dark
System
Sign in

PermissionsAdded in v7.0

Permissions are the mechanism for limiting access to the database based on provided connection credentials.

Each role has as set of granted permissions.

Copy
create role alice {
    set password := 'wonderland';
    set permissions := {
        sys::perm::data_modifiction,
        default::can_see_secrets
    };
};

Permissions are either built-in (added in 7.0) or defined in schema (added in 7.0).

Some language features or functions require current role to have certain permissions. For example, to use insert, update or delete, current role is required to have sys::perm::data_modification.

Additionally, permissions of current role can be accessed via global variables of the same name:

Copy
select global sys::perm::data_modification;

Note that roles are instance-wide object, which means that they exist independent of branches and their schemas. This means that role's permissions apply to all branches.

Roles that are qualified as superuser are implicitly granted all permissions (added in 7.0).

sys::perm::data_modification

Required for using insert, update or delete statements.

sys::perm::ddl

Required for modification of schema. This includes applying migrations, and issuing bare DDL commands (e.g. create type Post;).

It does not include global instance commands, such as create branch or create role. These are only allowed to superuser roles.

sys::perm::branch_config

Required for issuing configure current branch.

sys::perm::sql_session_config

Required for issuing SET and RESET SQL commands.

sys::perm::analyze

Required for issuing analyze ... queries.

sys::perm::query_stats_read

Required for reading sys::QueryStats.

sys::perm::approximate_count

Required for accessing sys::approximate_count().

cfg::perm::configure_timeout

Required for setting various timeouts, for example session_idle_transaction_timeout and query_execution_timeout.

cfg::perm::configure_apply_access_policies

Required for disabling access policies.

cfg::perm::configure_allow_user_specified_id

Required for setting allow_user_specified_id.

std::net::perm::http_write

Required for issuing HTTP requests.

std::net::perm::http_read

Required for reading status of issued HTTP requests and responses.

Permissions for auth extension:

ext::auth::perm::auth_read

ext::auth::perm::auth_write

ext::auth::perm::auth_read_user

Permissions for ai extension are described in AI extension reference.

Custom permissions can be defined in schema, to fit the security model of each application.

Copy
module default {
    permission data_export;
}

These permissions can be assigned to roles, similar to built-in permissions:

Copy
alter role warehouse {
  set permissions := {default::data_export};
};

Role permissions are instance-wide.

If an unrelated branch defines default::data_export, the warehouse role will receive it as well. This happens even if the unrelated branch adds the permission after alter role.

Additionally, a role may be given permissions which do not yet exist in any schema. This is useful for creating roles before any schemas are applied.

To check if the current database connection's role has a permission, use global variable with the same name as the permission. This global is a boolean and cannot be manually set.

Copy
select global default::data_export;

In combination with access policies, permissions can be used to limit read or write access of any type:

Copy
type AuditLog {
    property event: str;

    access policy only_export_can_read
        allow select
        using (global data_export);

    access policy anyone_can_insert
        allow insert;
}

In this example, we have type AuditLog into which all roles are allowed to insert new log entries. But reading is allowed only to roles that posses data_export permission (or are qualified as a superuser).

Gel server can be exposed to public internet, with clients connecting directy from browsers. Let's assume that only want to grant read access to the public browser client.

In such scenarios, it is recommended to create a separate role that will be used by the JavaScript client (e.g. webapp) and not grant it any permissions.

This way, it will not be able to issue DROP TYPE or DELETE commands, but will be able to read all data in the database. More importantly, it will not be able to configure apply_access_policies to false to bypass our restrictions.

If we want to limit that access further, for example limit read access to type Secrets, we can use such schema:

Copy
permission server_access;

type Secret {
    access policy all_access
        allow select, insert, update, delete
        using (global server_access);
};

Because webapp role will not possess permission server_access it will not be able to read (or modify) Secret. For other, trusted clients, which should be able to access Secrets, we have use superuser role, or some other role with server_access permission:

Copy
create role api_server {
    set password := 'strong_password';
    set permissions := {sys::perm::dml, default::server_access};
};

A similar example to the previous one is a public database, with a JavaScript client that needs write access to some, but not all, object types.

In such scenarios, it is recommended to create a separate role for it (e.g. webapp) and assign it sys::perm::ddl permission.

Such role will be able to connect to the database, read all data and modify all types. For obvious reasons, this is undesirable, since client credentials could be extracted and used to delete all data in the database.

To further limit access, the access policies must be used on every object:

Copy
permission server_access;

type Posts {  # read-only
    access policy everyone_can_read allow select using (true);
    access policy server_can_do_everything
        allow select, insert, update, delete
        using (global server_access);
}

type Events {  # insert-only
    access policy everyone_can_insert allow insert using (true);
    access policy server_can_do_everything
        allow select, insert, update, delete
        using (global server_access);
}

type Secrets {  # no access
    access policy server_can_do_everything
        allow select, insert, update, delete
        using (global server_access);
};

Again, we can then use superuser role for server to fully access the database, or setup a separate role with server_access permission.

To control access by branches instead of by object type, we can use Role.branches setting.

For example, let's assume we have an instance with staging and prod branches. We want the role dev to have full access to staging, but not prod.

Copy
create role dev {
    set password := 'strong_password';
    set branches := {'staging'};
};

For more about this, see Roles.

Roles with superuser status are exempt from permission checks and have full access over the instance.

This includes some commands that are not covered by any permission and are thus allowed only to superuser roles.

These commands include:

  • ROLE commands

  • BRANCH commands

  • EXTENSION PACKAGE commands

  • CONFIGURE INSTANCE command

  • DESCRIBE command

  • ADMINISTER command