Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/user/content/security/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ menu:
| Guide | Description |
|-------|-------------|
| [User and service accounts](/security/cloud/users-service-accounts/) | Add user/service accounts |
| [Sync identity provider groups](/security/cloud/users-service-accounts/sync-idp-groups/) | Provision groups via SCIM and map them to database roles |
Comment thread
kay-kim marked this conversation as resolved.
| [Access control](/security/cloud/access-control/) | Reference for role-based access management (RBAC) |
| [Manage network policies](/security/cloud/manage-network-policies/) | Set up network policies |

Expand Down
6 changes: 6 additions & 0 deletions doc/user/content/security/cloud/access-control/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ In Materialize, role-based access control (RBAC) governs access to **database
objects** through privileges granted to [database
roles](./manage-roles/).

{{< tip >}}
You can manage database role membership from your identity provider by
[syncing IdP groups to database
roles](/security/cloud/users-service-accounts/sync-idp-groups/).
{{</ tip >}}

## Roles and privileges

{{% include-headless "/headless/rbac-cloud/db-roles" %}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ organization roles, see [Users and service
accounts](/security/cloud/users-service-accounts/).
{{< /annotation >}}

{{< tip >}}
Instead of granting role membership by hand, you can [sync groups from your
identity provider](/security/cloud/users-service-accounts/sync-idp-groups/) to
manage database role membership from your IdP.
{{</ tip >}}

## Required privileges for managing roles

{{< note >}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ all systems in your organization.
To configure SSO for your Materialize organization, follow [this step-by-step
guide](./sso/).

## Group sync

As an **Organization admin**, you can provision groups from your identity
provider via SCIM and map them to existing Materialize database roles, so that
database role membership is managed from your identity provider. Roles you
grant manually are not affected by sync.

To configure group sync for your Materialize organization, see [Sync identity
provider groups to database roles](./sync-idp-groups/).

## See also

- [Role-based access control](/security/cloud/access-control/)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
---
title: "Sync identity provider groups to database roles"
description: "Provision groups from your identity provider via SCIM and map them to Materialize database roles."
menu:
main:
parent: user-service-accounts
weight: 20
---

{{< private-preview >}}
Group-to-role mapping
{{< /private-preview >}}

As an **administrator** of a Materialize organization, you can configure
[SCIM](https://scim.cloud/) to sync groups from your identity provider (IdP)
into Materialize and map them to database roles.

* Groups you forward from your IdP are provisioned into your Materialize
organization via SCIM, along with their memberships.

* When a user authenticates with Materialize, their group memberships are
included in the authentication exchange. Materialize uses this information
to grant and revoke their membership in existing database roles with the
same names.

This means you can manage who belongs to which database role from your IdP.
When a team member joins, leaves, or changes teams, updating their group
membership in the IdP updates their database role memberships, with no manual
`GRANT` or `REVOKE` statements required. Roles you grant manually with `GRANT`
are outside the scope of sync and are never revoked.

{{< important >}}
Group sync only **assigns and unassigns role membership**. It never creates or
drops database roles. You must create the database role yourself before a group
takes effect.
{{</ important >}}

{{< note >}}
Materialize has two separate permission layers, and a group can affect both:

* **Organization roles** (*Organization Admin*, *Organization Member*) control
access to Materialize and organization administration. A group can be
assigned an organization role, and members of the group receive that
organization role automatically. By default, groups are assigned the
*Organization Member* role. Assigning *Organization Admin* to a group makes
its members **superusers** in Materialize.

* **Database roles** control access to database objects via [role-based access
control (RBAC)](/security/cloud/access-control/). Group sync, the subject of
this page, grants and revokes membership in the database role matching each
group's name.
{{</ note >}}

## Before you begin

* Your Materialize organization must have an [SSO
connection](/security/cloud/users-service-accounts/sso/) configured for your
identity provider. Group sync builds on SSO, so set that up first. You can
confirm your connection under **Account** > **Account Settings** > **SSO**.

![SSO connections in the Materialize Console](/images/console/console-account-settings-sso.png "SSO connections in the Materialize Console")

* You must have an identity provider that supports SCIM 2.0 provisioning
(e.g., Okta or Microsoft Entra ID).
* Only users assigned the **Organization Admin** role can manage provisioning
and groups.
* Group-to-role sync applies on **connection**, never mid-session. Materialize
makes a best effort to apply group changes on the user's next connection,
but it may take several minutes for changes to be reflected.

## Step 1. Create a SCIM connection

* [Log in to the Materialize Console](/console/).

* Navigate to **Account** > **Account Settings** > **Provisioning**.

* Click **Add Connection**, name the integration, and select your identity
provider (**Okta**, **Azure**, or **Custom SCIM** for any other SCIM
2.0-compatible provider).

![Setup SCIM connection dialog in the Materialize Console](/images/console/console-account-settings-add-scim.png "Setup SCIM connection dialog in the Materialize Console")

* Follow the in-console guide for your provider. The Console generates a SCIM
endpoint URL and an API token, which you enter into your identity provider's
provisioning settings.

Once your identity provider connects successfully, the connection shows as
**Linked**.

![Provisioning connections in the Materialize Console](/images/console/console-account-settings-provisioning.png "Provisioning connections in the Materialize Console")

## Step 2. Choose which groups to sync

Materialize only syncs the groups you explicitly configure your identity
provider to send. Your other IdP groups are not visible to Materialize.

Comment thread
kay-kim marked this conversation as resolved.
{{< note >}}
Group names that collide with a reserved Materialize role name are skipped and
never mapped to a role. Avoid these names when choosing which groups to push.
See [Limitations](#limitations) for the full list.
{{</ note >}}

{{< tabs >}}
{{< tab "Okta" >}}

* In the Okta Admin Console, open the SCIM application you connected in
[Step 1](#step-1-create-a-scim-connection).

* On the **Assignments** tab, assign the users (or groups) that should be
provisioned into Materialize.

* On the **Push Groups** tab, click **Push Groups** and select the groups to
sync, either by name or by rule.

![Push Groups tab of the Okta SCIM application](/images/console/okta-push-groups.png "Push Groups tab of the Okta SCIM application")

Once pushed, the groups and their memberships appear in the Materialize
Console under **Account** > **Account Settings** > **Groups**, marked with a
SCIM badge. Groups pushed from your IdP are read-only in Materialize and must

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, except for the Org admin/member thing, yes? It's just the members list?

be managed from the IdP.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering ... should we include a step here where people edit to set the organization role? Related to the above question about what happens if someone isn't either an Org Admin/Member ?

{{< /tab >}}
{{< tab "Other providers" >}}

Configure your identity provider's SCIM provisioning to push the users and
groups that should exist in Materialize. Most providers let you scope
provisioning to specific groups, so only those groups and their members are
synced.

{{< /tab >}}
{{< /tabs >}}

## Step 3. Create matching database roles

For each group that should grant access, create a database role with the
**same name** as the group and grant it the privileges the group's members
need. Members of the group get their access through membership in this role:

```mzsql
CREATE ROLE materialize_admins;
GRANT ALL PRIVILEGES ON SCHEMA production TO materialize_admins;
```

The database role must already exist before the user connects. Group sync
only assigns and unassigns membership in existing roles. It never creates
roles, so a group with no matching role is skipped during sync. Group names
must match role names exactly, including case. The roles you create act as an
allowlist for which groups take effect.

To check the mapping status of your groups, navigate to **Account** >
**Account Settings** > **Groups**. Each group indicates whether it has a
corresponding database role and will work with group mapping.

![Groups in the Materialize Console](/images/console/console-account-settings-groups.png "Groups in the Materialize Console")

## Step 4. Verify

Have a user in a synced group connect to Materialize (e.g., via the [SQL
Shell](/console/sql-shell/) or `psql`). On their first login via SSO,
Materialize auto-provisions the user's own database role (named from their
identity claim). Group sync then grants them membership in the database roles
matching their groups. The user's own role is auto-created. The group roles
must already exist.

Role memberships granted by group sync are recorded with the grantor
`mz_jwt_sync`, so you can distinguish them from manual grants:

```mzsql
SELECT r.name AS role, m.name AS member, g.name AS grantor
FROM mz_role_members rm
JOIN mz_roles r ON rm.role_id = r.id
JOIN mz_roles m ON rm.member = m.id
JOIN mz_roles g ON rm.grantor = g.id;
```

All grants and revokes performed by group sync are also recorded in
[`mz_audit_events`](/reference/system-catalog/mz_catalog/#mz_audit_events).

## How sync works

* **Sync happens at connection time.** When a user connects, Materialize
compares their current group memberships against their sync-managed role
memberships and applies the difference. Materialize makes a best effort to
apply changes made in the IdP on the user's next connection, but it may take
several minutes for changes to be reflected. Changes are never applied to a
session that is already connected.

* **Manual grants are never touched.** Group sync only manages memberships it
granted itself (grantor `mz_jwt_sync`). A role granted manually with `GRANT`
is never revoked by sync, even if the user leaves the corresponding group.
Audit manual grants periodically to avoid users retaining access through
stale manual grants.

* **Groups without a matching role are skipped.** The connection proceeds and
Materialize sends the client a `NOTICE` for each unmatched group.

## Limitations

* **Reserved role names are never mapped.** Groups whose names collide with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually mention this point last.

a reserved Materialize role name are always skipped during sync, so IdP
groups cannot grant system-level privileges. Reserved names are:

* Any name beginning with `mz_`, `pg_`, or `external_`.
* The `PUBLIC` role.
* The role-specification keywords `current_user`, `current_role`,
`session_user`, `user`, and `none`.

Matching against reserved names is case-insensitive.
* **Roles are never created or dropped.** Group sync only assigns and
unassigns role membership. A group only takes effect once you [create a
database role with a matching name](#step-3-create-matching-database-roles).
* **Changes are not applied in real time.** Group membership changes are only
applied when a user connects, never to a session that is already connected.
Materialize makes a best effort to apply changes on the next connection, but
it may take several minutes for a change in your identity provider to be
reflected.

## Manage with Terraform

Instead of the Console, you can manage SCIM connections and groups with the
[Materialize Terraform provider](/manage/terraform/):

| Resource | Description |
|----------|-------------|
| [`materialize_scim_config`](https://registry.terraform.io/providers/MaterializeInc/materialize/latest/docs/resources/scim_config) | Manage SCIM provisioning connections. |
| [`materialize_scim_group`](https://registry.terraform.io/providers/MaterializeInc/materialize/latest/docs/resources/scim_group) | Create and manage groups. |
| [`materialize_scim_group_users`](https://registry.terraform.io/providers/MaterializeInc/materialize/latest/docs/resources/scim_group_users) | Manage group membership. |
| [`materialize_scim_group_roles`](https://registry.terraform.io/providers/MaterializeInc/materialize/latest/docs/resources/scim_group_roles) | Assign organization roles to groups. |

## See also

- [Access control (RBAC)](/security/cloud/access-control/)
- [Configure single sign-on (SSO)](/security/cloud/users-service-accounts/sso/)
- [Invite users](/security/cloud/users-service-accounts/invite-users/)
- [Manage with Terraform](/manage/terraform/)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading