RBAC places restrictions on what actions a user can do based on the privileges granted to that user. PostgreSQL has a rich and well tested RBAC design and implementation, which we will base our implementation off of.
If you want to skim this doc, then skip to the Phase 1 - Attributes section and only look at the subsection headers, tables, and SQL statements.
LOGIN
and SUPERUSER
attributes when creating a role.NOLOGIN
and NOSUPERUSER
attributes, but will fail the query if those are present.CREATE ROLE <role-name> LOGIN SUPERUSER
.DROP ROLE <role-name>
.CREATE USER
as is treated as an alias for CREATE ROLE LOGIN
.mz_roles
: catalog table that stores role names, id, and oid.LOGIN
and SUPERUSER
.CREATE ROLE
DROP ROLE
ALTER ROLE
GRANT
REVOKE
SET ROLE
REASSIGN OWNED
DROP OWNED
acl.h
acl.c
aclchk_internal.h
Note: There may be more, but this is a good starting point.
See Role Attributes for all PostgreSQL attributes.
Attributes belong to a role and describe what that role is allowed to do in the system. They are independent of any particular object. We will support the following attributes:
Name | Option Name | Description | From PostgreSQL |
---|---|---|---|
database creation | CREATEDB |
Can create a database. | Yes |
role creation | CREATEROLE |
Can create, alter, drop, grant membership to, and revoke membership from roles. | Yes |
inheritance of privileges | INHERIT |
Can inherit the privileges of roles that it is a member of. On by default. For this project we can keep this as a mandatory attribute. | Yes |
cluster creation | CREATECLUSTER |
Can create a cluster. | No |
These attributes will be added to the attributes accepted by the CREATE ROLE
statement.
We will add the following SQL statement:
ALTER ROLE <role_name> [ WITH ] <option> [ ... ]
<role_name>
is the name of the role to alter.<option>
: is one of the Option Name’s from above OR one of the option names from above with a NO
prepended (
ex: NOLOGIN
). An option without a NO
grants the attribute, an option with a NO
revokes the attribute. All
unmentioned attributes are left unchanged.CREATEROLE
attribute can run this on any other role
SUPERUSER
attribute is required to change the SUPERUSER
attribute on another role.SUPERUSER
can run this without CREATEROLE
.WITH
is ignored.When a new user logs in, as long as they were successfully authenticated through an external port, we will create a new
role for them with only the INHERIT
attribute. This will also allow us to delete the materialize
role without
breaking local development.
We will also support the following session specific attributes:
Name | Option Name | Description | From PostgreSQL |
---|---|---|---|
login privilege | LOGIN |
Roles with this attribute can establish a database connection. | Yes |
superuser status | SUPERUSER |
Can bypass all permission checks, except login. | Yes |
These attributes are derived everytime a user tries to log in and only lasts as long as the session is active. You
cannot specify these attributes in CREATE ROLE
or ALTER ROLE
. We use the following logic at every login:
LOGIN
attribute for that
session.SUPERUSER
attribute for that session.
SUPERUSER
attribute will be periodically updated as part of the periodic Frontegg re-validation done for
pgwire connections.mz_system
role will always have the SUPERUSER
attribute.This differs from PostgreSQL, which treats these as normal role attributes that persists between sessions and can be
specified in CREATE ROLE
and ALTER ROLE
.
We will add the following read-only session parameter:
IS_SUPERUSER
: True if the current role has superuser privileges.
mz_roles
with boolean values.IN ROLE
, IN GROUP
options for CREATE ROLE
.ROLE
, USER
options for CREATE ROLE
.CREATE ROLE
options:
VALID UNTIL
SYSID
ADMIN
ALTER ROLE RENAME
ALTER ROLE SET
ALTER ROLE RESET
CURRENT_ROLE
, CURRENT_USER
, SESSION_USER
aliases for <role_name>
in ALTER ROLE
.See Role Membership for PostgreSQL role membership.
Role membership involves the ability of one role to be a member of another role. A role inherits all the privileges (not
attributes) of the roles it is a member of, unless NOINHERIT
is set. Even if NOINHERIT
is set, you can still use the
privileges of that role via SET ROLE
. We will add the following SQL commands:
GRANT <group_role> TO [ GROUP ] <role>
<role>
as a member of <group_role>
.CREATEROLE
attribute OR superusers can grant membership to any other role.
CREATEROLE
roles cannot grant roles with SUPERUSER
.WITH ADMIN OPTION
option. We will leave
this as future work.GROUP
is ignored.REVOKE <group_role> FROM [ GROUP ] <role>
<role>
as a member from <group_role>
.CREATEROLE
attribute OR superusers can revoke membership from any other member.
CREATEROLE
roles cannot revoke roles with SUPERUSER
.WITH ADMIN OPTION
option. We will leave
this as future work.GROUP
is ignored.We will modify DROP ROLE <role_name>
so that when <role_name>
is dropped, all other roles have their membership
revoked.
We will add the following functions:
current_role()
current_user()
current_role()
.session_user()
NOTE: Since we won't support SET ROLE
yet, these functions will all behave identically.
mz_auth_members
modeled
after pg-auth-members
, which has the
following columns:
roleid
: RoleId
member
: RoleId
grantor
: RoleId
GRANT
privileges.REVOKE
privileges.PUBLIC
alias for <role>
in GRANT
and REVOKE
.CREATE ROLE
:IN ROLE
,IN GROUP
,ROLE
,USER
.CASCADE
and RESTRICT
options in REVOKE
.CURRENT_ROLE
, CURRENT_USER
, and SESSION_USER
aliases for <role>
in GRANT
and REVOKE
.GRANTED BY
option for GRANT
and REVOKE
.[ WITH ADMIN OPTION ]
option for GRANT
.[ADMIN OPTION FOR ]
option for REVOKE
.SET ROLE
RESET ROLE
PUBLIC
roleSee Grant for PostgreSQL PUBLIC
details (grep for PUBLIC).
PUBLIC
is a special keyword that is accepted in some locations where a role name would be accepted. The key word
PUBLIC indicates that the changes are to be applied to all roles, including those that might be created later. PUBLIC
is not allowed in CREATE ROLE
, ALTER ROLE
, DROP ROLE
, GRANT <group_role> TO <role>
,
and REVOKE <group_role> FROM <role>
.
RoleId
will be added called Public
.PUBLIC
role will be added to the catalog with an id of RoleId::Public
.RoleId::Public
variant in all disallowed operations.See Privileges for PostgreSQL ownership.
When any object is created, the creating role is assigned as the owner of that object. Only the owner (and superusers) can destroy or alter that object.
Below is a summary of the default ownership of all builtin objects:
mz_system
cluster will be owned by the mz_system
role.mz_introspection
cluster will be owned by the mz_system
role.default
cluster will be owned by the mz_system
role.materialize
database will be owned by the mz_system
role.materialize.public
schema will be owned by the mz_system
role.mz_system
role will own all catalog schemas [pg_catalog
, mz_catalog
, mz_internal
, information_schema
].We will add the following SQL commands:
ALTER <object_type> <object_name> OWNER TO <new_owner>
<object_name>
to <new_owner>
.<new_owner>
.The owner of an index is always kept in-sync with the owner of the underlying relation. Trying to alter the owner of an index will return successfully with a warning, but will not actually change the owner of the index. This is for PostgreSQL compatibility reasons.
We will update DROP ROLE
so that roles cannot be dropped unless no objects are owned by the role.
REASSIGN OWNED
DROP OWNED
See Privileges for PostgreSQL privileges.
Roles can be granted and revoked certain privileges on objects, that allow them to perform some action with that object.
We will support the following privileges:
Privilege | Description | Abbreviation | Applicable Object Types | From PostgreSQL |
---|---|---|---|---|
SELECT |
Allows reading rows from an object. | r(”read”) | Table, View, Materialized View, Source | Yes |
INSERT |
Allows inserting into an object. | a(”append”) | Table | Yes |
UPDATE |
Allows updating an object (requires SELECT if a read is necessary). | w(”write”) | Table | Yes |
DELETE |
Allows deleting from an object (requires SELECT if a read is necessary). | d | Table | Yes |
CREATE |
Allows creating a new object within another object. | C | Database, Schema, Cluster | Yes |
USAGE |
Allows using an object or looking up members of an object. | U | Database, Schema, Connection, Secret, Cluster | Yes |
We will support the following object types:
Object Type | All Privileges | From PostgreSQL |
---|---|---|
DATABASE |
UC | Yes |
SCHEMA |
UC | Yes |
TABLE |
arwd | Yes |
VIEW |
r | Yes |
MATERIALIZED VIEW |
r | Yes |
INDEX |
Yes | |
TYPE |
U | Yes |
SOURCE |
r | No |
SINK |
No | |
CONNECTION |
U | No |
SECRET |
U | No |
CLUSTER |
UC | No |
The object owner is given all privileges on an object by default, though these privileges can be revoked.
PostgreSQL allows arwd privileges on all table like objects (view, materialized view, etc.) even though they aren't useful. We remove privileges that don't make sense.
Below is a summary of the default privileges of all builtin objects:
mz_introspection
role will have UC
privileges on the mz_introspection
cluster.PUBLIC
pseudo-role will have U
privileges on the mz_introspection
cluster.PUBLIC
psuedo-role will have U
privileges on the default
cluster.PUBLIC
psuedo-role will have U
privileges on the default materialize
database.PUBLIC
psuedo-role will have U
privileges on all public
schemas (the default schema
created in every database).PUBLIC
pseudo-role will have U
privileges on all catalog schemas.PUBLIC
pseudo-role will have r
privileges on all applicable objects within all catalog
schemas.PUBLIC
psuedo-role will have U
privileges on all types.Here is a summary of all the privileges, attributes, and ownership needed to perform certain actions:
Operation | Privilege, Attribute, and OwnerShip |
---|---|
ALTER <item> |
Ownership of the item, SCHEMA(C) |
ALTER DATABASE |
Ownership of the database, CREATEDB |
ALTER SCHEMA |
Ownership of the schema, DATABASE(C) |
ALTER CLUSTER |
Ownership of the cluster, CREATECLUSTER |
ALTER CLUSTER REPLICA |
Ownership of the replica, CLUSTER(C) |
ALTER ROLE |
CREATEROLE |
CREATE CLUSTER |
CREATECLUSTER |
CREATE CLUSTER REPLICA |
CLUSTER(C) |
CREATE SECRET |
SCHEMA(C) |
CREATE {TABLE, TYPE, VIEW} |
SCHEMA(C) , sometimes TYPE(U) |
CREATE CONNECTION |
SCHEMA(C) , sometimes SECRET(U) , CONNECTION(U) |
CREATE DATABASE |
CREATEDATABASE |
CREATE MATERIALIZED VIEW |
SCHEMA(C) , CLUSTER(C) , sometimes TYPE(U) |
CREATE INDEX |
SCHEMA(C) , CLUSTER(C) , Ownership of relation, sometimes TYPE(U) |
CREATE SOURCE |
SCHEMA(C) sometimes CLUSTER(U) , CONNECTION(U) , CREATECLUSTER |
CREATE SINK |
SCHEMA(C) , OBJECT(r) sometimes CLUSTER(U) , CONNECTION(U) , CREATECLUSTER |
CREATE ROLE |
CREATEROLE |
CREATE SCHEMA |
DATABASE(C) |
DROP <item> |
Ownership of the item, SCHEMA(U) |
DROP DATABASE |
Ownership of the database |
DROP SCHEMA |
Ownership of the schema, DATABASE(U) |
DROP CLUSTER |
Ownership of the cluster |
DROP CLUSTER REPLICA |
Ownership of the replica, CLUSTER(U) |
DROP ROLE |
CREATEROLE |
{INSERT INTO ... VALUES, COPY FROM} |
OBJECT(a) , sometimes TYPE(U) |
INSERT INTO ... SELECT |
OBJECT(a) usually, OBJECT(r) , CLUSTER(U) , SCHEMA(U) , sometimes TYPE(U) |
DELETE |
OBJECT(d) , OBJECT(r) usually CLUSTER(U) , SCHEMA(U) , sometimes TYPE(U) |
UPDATE |
OBJECT(w) , OBJECT(r) usually CLUSTER(U) , SCHEMA(U) , sometimes TYPE(U) |
{SELECT, SHOW, SUBSCRIBE} |
usually CLUSTER(U) , OBJECT(r) , SCHEMA(U) , sometimes TYPE(U) |
EXPLAIN |
usually OBJECT(r) , SCHEMA(U) , sometimes TYPE(U) |
SHOW CREATE |
SCHEMA(U) |
Superusers can do anything in the above table.
In order to execute a read from a view/materialized view, the role needs r
permission on the
view/materialized view and the view owner needs the required permissions to perform the query in
the view definition.
When creating a sink, Materialize will check the users privileges only at the time of sink creation. If the user loses privileges, the sink will keep running an potentially exposes information that the user no longer has access to.
Unlike PostgreSQL, UPDATE
and DELETE
always require SELECT
privileges on the object being
updated.
We will add the following SQL commands:
GRANT <privilege> ON <object-type> <object> TO [ GROUP ] <role>
<privilege>
on <object>
to <role>
.<object>
can grant privileges on it.
WITH GRANT OPTION
option.GROUP
is ignored.TABLE
if not omitted.GRANT ALL [ PRIVILEGES ] ON <object-type> <object> TO [ GROUP ] <role>
PRIVILEGES
is ignored.REVOKE <privilege> ON <object-type> <object> FROM [ GROUP ] <role>
<privilege>
on <object>
from <role>
.<object>
can revoke privileges from it.
WITH GRANT OPTION
option.GROUP
is ignored.TABLE
if not omitted.REVOKE ALL [ PRIVILEGES ] ON <object-type> <object> FROM [ GROUP ] <role>
PRIVILEGES
is ignored.We will update ALTER <object_type> <object_name> OWNER TO <new_owner>
such that it:
CREATE
privilege on the schema where <object_name>
resides if the object resides in a schema.
DROP
then CREATE
.CREATE
privilege on the database where <object_name>
resides if the object is a database.We will update DROP ROLE
so that roles cannot be dropped unless the role contains no privileges.
We will update DROP <object>
so that it revokes all privileges on <object>
.
AclMode
that is a bit flag containing all privileges.
maclitem
in the mz_internal
schema to represent a single privilege.
aclitem
item in PostgreSQL, see
https://github.com/postgres/postgres/blob/3aa961378b4e517908a4400cdc476ca299693de9/src/include/utils/acl.h#L48-L59.grantee: RoleId
grantor: RoleId
privs: AclMode
"<grantee>=<privs>/<grantor>"
"<grantee>"
is the raw RoleId of grantee
.
"<grantor>"
is the raw RoleId of grantor
."<privs>"
is the letter codes of all the granted privileges concatenated together.aclitem
to text using human
read-able names instead of IDs. We are unable to do this because our encoder does not have
access to the catalog. We have the same issue with the regtype
and regproc
types.aclitem
, except
swapping out oid
s for RoleId
s.aclitem
type is because
aclitem
uses oid
types, which Materialize does not use as a persistent identifier.maclitem
elements.maclitem
:
maclitem = maclitem → boolean
: Are maclitems
equal?maclitem[] @> maclitem → boolean
: Does array contain the specified privileges?maclitem
:
maclitem
to text
.maclitem[]
.
that stores all privileges belonging to an object.
GRANTED BY
option for GRANT
and REVOKE
.WITH GRANT OPTION
in GRANT
.GRANT OPTION FOR
in REVOKE
.TRUNCATE
REFERENCES
TRIGGER
CONNECT
TEMPORARY
EXECUTE
SET
ALTER SYSTEM
DOMAIN
FUNCTION
or PROCEDURE
FOREIGN DATA WRAPPER
FOREIGN SERVER
LANGUAGE
LARGE OBJECT
PARAMETER
SEQUENCE
Table column
TABLESPACE
psql
meta-commands.This is an optional phase to add some utility commands and syntactic sugar present in PostgreSQL. We will add the following SQL commands and options to existing commands:
REASSIGN OWNED BY <old_role> TO <new_role>
<old_role>
to <new_role>
.<old_role>
and <new_role>
or a superuser.CREATE
privilege on all schemas and databases where all objects reside.
ALTER
privileges. I will
double-check this.DROP OWNED BY <name> [ CASCADE | RESTRICT]
<name>
.<name>
.<name>
.RESTRICT
.CREATE ROLE
:IN ROLE
,IN GROUP
,ROLE
,USER
.CURRENT_ROLE
, CURRENT_USER
, SESSION_USER
, aliases in GRANT
, REVOKE
, ALTER
, REASSIGN OWNED
and DROP OWNED
.A user role called mz_default_owner
will be created in all existing deployments. A migrations
will assign mz_default_owner
as the owner of all existing unowned objects. Superusers will be
able to re-assign ownership for all of these objects.
As we develop RBAC we will roll out the following three parameters:
They will all default to false initially. Once we have finished implementing everything, we will update them to true for certain candidate customers. Once we are confident in our implementation, we will remove "enable_ld_rbac_checks" and "enable_session_rbac_checks", and change the default of "enable_rbac_checks" to true.
All new SQL commands will be available to all users. The SQL commands will update user privileges, but emit a notice if RBAC is disabled.
SUPERUSER
session attribute.SUPERUSER
session attribute.SUPERUSER
s can do all actions.
SUPERUSER
can only be derived from Frontegg, which is not
available in those types of tests.SELECT
privileges based on if a new dataflow will be spun up or if we can use an existing one?