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.
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:
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).
Built-in permissions
- sys::perm::data_modification
-
Required for using
insert
,update
ordelete
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
orcreate 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
andRESET
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
andquery_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
Custom permissions can be defined in schema, to fit the security model of each application.
module default {
permission data_export;
}
These permissions can be assigned to roles, similar to built-in permissions:
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.
select global default::data_export;
In combination with access policies, permissions can be used to limit read or write access of any type:
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).
Common patterns
Public readonly database
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:
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:
create role api_server {
set password := 'strong_password';
set permissions := {sys::perm::dml, default::server_access};
};
Public partially writable database
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:
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.
Restricting branches
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
.
create role dev {
set password := 'strong_password';
set branches := {'staging'};
};
For more about this, see Roles.
Superuser permissions
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