Group Management Functions¶
Functions for creating and managing user groups, group members, group mappings to external identity providers, and external member synchronization. All functions in the auth schema perform permission checks before executing.
For background on the three group types (internal, external, hybrid), see Group Types.
Source: 021_functions_auth_group.sql
Authorization Helpers¶
auth.is_group_member¶
Checks whether a user is a member of a specific group using the cached group membership list.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User to check membership for |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | null | Group to check |
_tenant_id |
integer | 1 | Tenant context |
Returns: boolean
Permission required: None
Source: 021_functions_auth_group.sql:13
auth.can_manage_user_group¶
Determines whether a user is authorized to manage a group. Authorization is granted if any of the following conditions are met:
- The group has
can_members_manage_othersenabled and the user is a member of that group. - The user is a tenant-level owner.
- The group has a specific owner and the user is that owner.
- The group has no owner and the user holds the specified permission.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User requesting management access |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to manage |
_permission |
text | -- | Permission code to check as fallback (e.g. groups.create_member) |
_tenant_id |
integer | 1 | Tenant context |
Returns: boolean -- always returns true or throws an exception
Permission required: Varies (see authorization logic above)
Source: 021_functions_auth_group.sql:22
Group CRUD¶
auth.create_user_group¶
Creates a new user group. Delegates to unsecure.create_user_group after the permission check.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_title |
text | -- | Display title for the group |
_is_assignable |
boolean | true | Whether permission sets can be assigned to this group |
_is_active |
boolean | true | Whether the group is active |
_is_external |
boolean | false | Whether membership is managed externally |
_is_default |
boolean | false | Whether new users are automatically added to this group |
_tenant_id |
integer | 1 | Tenant context |
_source |
text | null | Source identifier for tracking where the group was created from |
Returns: table(__user_group_id integer)
Permission required: groups.create_group
Source: 021_functions_auth_group.sql:58
auth.update_user_group¶
Updates an existing user group's properties.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to update |
_title |
text | -- | New display title |
_is_assignable |
boolean | -- | Whether permission sets can be assigned |
_is_active |
boolean | -- | Whether the group is active |
_is_external |
boolean | -- | Whether membership is managed externally |
_is_default |
boolean | -- | Whether new users are automatically added |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer)
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:77
auth.enable_user_group¶
Activates a user group by setting is_active to true.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to enable |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer, __is_active boolean, __is_assignable boolean, __updated_at timestamptz, __updated_by text)
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:109
auth.disable_user_group¶
Deactivates a user group by setting is_active to false.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to disable |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer, __is_active boolean, __is_assignable boolean, __updated_at timestamptz, __updated_by text)
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:140
auth.lock_user_group¶
Locks a user group by setting is_assignable to false, preventing new permission set assignments.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to lock |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer, __is_active boolean, __is_assignable boolean, __updated_at timestamptz, __updated_by text)
Permission required: groups.lock_group
Source: 021_functions_auth_group.sql:171
auth.unlock_user_group¶
Unlocks a user group by setting is_assignable to true.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to unlock |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer, __is_active boolean, __is_assignable boolean, __updated_at timestamptz, __updated_by text)
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:202
auth.delete_user_group¶
Deletes a user group. Refuses to delete system groups (raises error 52271) or non-existent groups (raises error 52171).
| Parameter | Type | Default | Description |
|---|---|---|---|
_deleted_by |
text | -- | Identifier of the deleting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to delete |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer)
Permission required: groups.delete_group
Source: 021_functions_auth_group.sql:233
auth.get_user_group_by_id¶
Retrieves a single user group by its ID. Delegates to unsecure.get_user_group_by_id.
| Parameter | Type | Default | Description |
|---|---|---|---|
_requested_by |
text | -- | Identifier of the requesting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to retrieve |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer, __tenant_id integer, __title text, __code text, __is_system boolean, __is_external boolean, __is_assignable boolean, __is_active boolean, __is_default boolean)
Permission required: groups.get_group
Source: 021_functions_auth_group.sql:609
Group Type Conversion¶
These functions convert a group between internal, external, and hybrid types. Each conversion has side effects on membership and mappings.
auth.set_user_group_as_hybrid¶
Converts a group to hybrid type by setting is_external to false on a group that has mappings. Hybrid groups accept both manual members and external mappings.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to convert |
_tenant_id |
integer | 1 | Tenant context |
Returns: void
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:587
auth.set_user_group_as_external¶
Converts a group to external type. Removes all manual members and sets is_external to true. Membership is then determined solely by external provider mappings.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to convert |
_tenant_id |
integer | 1 | Tenant context |
Returns: void
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:658
Warning
Converting to external removes all manual group members. This action cannot be undone.
auth.set_user_group_as_internal¶
Converts a group to internal type. Removes all external/synced members and all group mappings. Sets is_external, is_synced, and create_missing_users_on_sync to false. Only manual members are retained.
| Parameter | Type | Default | Description |
|---|---|---|---|
_updated_by |
text | -- | Identifier of the updating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | ID of the group to convert |
_tenant_id |
integer | 1 | Tenant context |
Returns: void
Permission required: groups.update_group
Source: 021_functions_auth_group.sql:690
Warning
Converting to internal removes all external/synced members and deletes all group mappings. This action cannot be undone.
auth.create_external_user_group¶
Creates a new external user group with an associated mapping in a single call. Combines create_user_group and create_user_group_mapping.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_title |
text | -- | Display title for the group |
_provider |
text | -- | Provider code for the mapping |
_is_assignable |
boolean | true | Whether permission sets can be assigned |
_is_active |
boolean | true | Whether the group is active |
_mapped_object_id |
text | null | External group/object identifier from the provider |
_mapped_object_name |
text | null | Human-readable name of the external group |
_mapped_role |
text | null | External role name from the provider |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_id integer)
Permission required: groups.create_group and groups.create_mapping (via create_user_group_mapping)
Source: 021_functions_auth_group.sql:558
Group Members¶
auth.create_user_group_member¶
Adds a user as a manual member of a group. Authorization is checked via auth.can_manage_user_group.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to add the member to |
_target_user_id |
bigint | -- | User to add as a member |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_member_id bigint)
Permission required: groups.create_member (via can_manage_user_group)
Source: 021_functions_auth_group.sql:624
auth.delete_user_group_member¶
Removes a manual member from a group. Refuses to remove externally managed members (type external or synced) -- those must be removed by deleting the mapping or sync source instead.
| Parameter | Type | Default | Description |
|---|---|---|---|
_deleted_by |
text | -- | Identifier of the deleting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to remove the member from |
_target_user_id |
bigint | -- | User to remove |
_tenant_id |
integer | 1 | Tenant context |
Returns: void
Permission required: groups.delete_member (via can_manage_user_group)
Source: 021_functions_auth_group.sql:276
Note
Only members with member_type_code = 'manual' can be removed through this function. Attempting to remove an external or synced member raises an exception.
auth.get_user_group_members¶
Retrieves all members of a group, including their membership type and mapping information.
| Parameter | Type | Default | Description |
|---|---|---|---|
_requested_by |
text | -- | Identifier of the requesting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to list members for |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__created timestamptz, __created_by text, __member_id bigint, __member_type_code text, __user_id bigint, __user_display_name text, __user_is_system boolean, __user_is_active boolean, __user_is_locked boolean, __mapping_id integer, __mapping_mapped_object_name text, __mapping_provider_code text)
Permission required: groups.get_members
Source: 021_functions_auth_group.sql:641
auth.get_user_assigned_groups¶
Returns all groups a specific user belongs to. If the requesting user is checking their own groups, no permission check is performed. Checking another user's groups requires the users.read_user_group_memberships permission.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_target_user_id |
bigint | -- | User whose group memberships to retrieve |
_tenant_id |
integer | 1 | Tenant context |
_target_tenant_id |
integer | null | Cross-tenant: query a specific tenant's data (admin only) |
Returns: table(__user_group_member_id bigint, __user_group_id integer, __user_group_code text, __user_group_title text, __user_group_member_type_code text, __user_group_mapping_id integer, __tenant_id integer, __tenant_code text, __tenant_title text)
Permission required: users.read_user_group_memberships / users.read_all_user_group_memberships (cross-tenant). Self-query (_user_id = _target_user_id) requires no permission.
Source: 021_functions_auth_group.sql:730
Group Mappings¶
Group mappings link internal groups to external identity provider groups or roles. See Group Types for details on how mappings drive external and hybrid group membership.
auth.get_user_group_mappings¶
Retrieves all mappings for a specific group.
| Parameter | Type | Default | Description |
|---|---|---|---|
_requested_by |
text | -- | Identifier of the requesting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to list mappings for |
_tenant_id |
integer | 1 | Tenant context |
_target_tenant_id |
integer | null | Cross-tenant: query a specific tenant's data (admin only) |
Returns: setof auth.user_group_mapping
Permission required: groups.get_mapping / groups.get_all_mappings (cross-tenant)
Source: 021_functions_auth_group.sql:321
auth.search_user_group_mappings¶
Searches group mappings across groups with filtering by provider, mapped object ID, role, and free-text search. Supports pagination and cross-tenant queries.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | null | Correlation ID for tracing |
_search_criteria |
jsonb | null | Search filters (see criteria keys below) |
_page |
integer | 1 | Page number |
_page_size |
integer | 30 | Page size (max 100) |
_tenant_id |
integer | 1 | Tenant context |
_target_tenant_id |
integer | null | Cross-tenant: query a specific tenant's data (admin only) |
Criteria keys:
| Key | Type | Description |
|---|---|---|
search_text |
text | Free-text search across mapped_object_id, mapped_object_name, mapped_role, and group title |
provider_code |
text | Filter by identity provider code |
mapped_object_id |
text | Filter by exact external object identifier |
mapped_role |
text | Filter by exact external role name |
Returns: table(__user_group_mapping_id integer, __user_group_id integer, __user_group_title text, __user_group_code text, __provider_code text, __mapped_object_id text, __mapped_object_name text, __mapped_role text, __total_items bigint)
Permission required: groups.get_mapping / groups.get_all_mappings (cross-tenant)
Source: 021_functions_auth_group.sql:342
auth.create_user_group_mapping¶
Creates a new mapping between a group and an external identity provider group or role. At least one of _mapped_object_id or _mapped_role must be provided. Validates that the provider allows group mapping. Invalidates the permission cache for any users whose identity matches the new mapping.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to create the mapping for |
_provider_code |
text | -- | Identity provider code |
_mapped_object_id |
text | null | External group/object identifier from the provider |
_mapped_object_name |
text | null | Human-readable name of the external group |
_mapped_role |
text | null | External role name from the provider |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_mapping_id integer, __user_group_id integer)
Permission required: groups.create_mapping
Source: 021_functions_auth_group.sql:453
Note
Both _mapped_object_id and _mapped_role are stored in lowercase. At least one must be non-null or error 52174 is raised.
auth.ensure_user_group_mapping¶
Idempotent version of create_user_group_mapping. Returns the existing mapping if one matches the group, provider, object ID, and role combination; otherwise creates a new one.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | -- | Group to ensure the mapping for |
_provider_code |
text | -- | Identity provider code |
_mapped_object_id |
text | null | External group/object identifier from the provider |
_mapped_object_name |
text | null | Human-readable name of the external group |
_mapped_role |
text | null | External role name from the provider |
_tenant_id |
integer | 1 | Tenant context |
Returns: table(__user_group_mapping_id integer, __user_group_id integer, __is_new boolean)
Permission required: groups.create_mapping (only when creating a new mapping)
Source: 021_functions_auth_group.sql:419
auth.delete_user_group_mapping¶
Deletes a group mapping by its ID. Invalidates the permission cache for all users who were members through this mapping.
| Parameter | Type | Default | Description |
|---|---|---|---|
_deleted_by |
text | -- | Identifier of the deleting user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_mapping_id |
integer | -- | ID of the mapping to delete |
_tenant_id |
integer | 1 | Tenant context |
Returns: void
Permission required: groups.delete_mapping
Source: 021_functions_auth_group.sql:515
External Member Synchronization¶
These functions handle bulk synchronization of group members from external identity providers using data staged in the stage.external_group_member table.
auth.get_user_groups_to_sync¶
Returns all groups that are flagged for synchronization (is_synced = true) along with their mapping details. Only includes mappings where the provider allows group sync.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
Returns: table(__user_group_id integer, __user_group_mapping_id integer, __title text, __code text, __provider_code text, __mapped_object_id text, __mapped_object_name text)
Permission required: groups.get_groups
Source: 021_functions_auth_group.sql:761
auth.process_external_group_member_sync_by_mapping¶
Synchronizes group members for a single mapping from staged data in stage.external_group_member. Compares staged members against current members and creates/updates entries as needed. If the group has create_missing_users_on_sync enabled, new users are automatically created via auth.ensure_user_info.
| Parameter | Type | Default | Description |
|---|---|---|---|
_run_by |
text | -- | Identifier of the user/system running the sync |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_mapping_id |
integer | -- | Mapping ID to sync members for |
Returns: table(__user_group_id integer, __user_group_mapping_id integer, __state_code text, __user_id bigint, __upn text)
The __state_code column indicates the operation performed: 'created' or 'updated'.
Permission required: None (permission check is commented out in the source)
Source: 021_functions_auth_group.sql:786
auth.process_external_group_member_sync¶
Orchestrates a full external group member synchronization across one or all synced groups. For each group, it processes all mappings via process_external_group_member_sync_by_mapping, then removes members who are no longer present in the staged data.
| Parameter | Type | Default | Description |
|---|---|---|---|
_run_by |
text | -- | Identifier of the user/system running the sync |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_group_id |
integer | null | Specific group to sync, or null for all synced groups |
Returns: table(__user_group_id integer, __user_group_mapping_id integer, __state_code text, __user_id bigint, __upn text)
The __state_code column indicates the operation performed: 'created', 'updated', or 'deleted'.
Permission required: None (permission check is commented out in the source)
Source: 021_functions_auth_group.sql:904
Search¶
auth.search_user_groups¶
Searches user groups with filtering, text search, and pagination. Text search uses normalized text matching against the group's nrm_search_data field.
| Parameter | Type | Default | Description |
|---|---|---|---|
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | null | Correlation ID for tracing |
_search_criteria |
jsonb | null | Search filters (see criteria keys below) |
_page |
integer | 1 | Page number (1-based) |
_page_size |
integer | 30 | Results per page (max 100) |
_tenant_id |
integer | 1 | Tenant context |
_target_tenant_id |
integer | null | Cross-tenant: query a specific tenant's data (admin only) |
Criteria keys:
| Key | Type | Description |
|---|---|---|
search_text |
text | Free-text search string (normalized before matching) |
is_active |
boolean | Filter by active status (null for all) |
is_external |
boolean | Filter by external status (null for all) |
is_system |
boolean | Filter by system status (null for all) |
Returns: table(__user_group_id integer, __title text, __code text, __is_system boolean, __is_external boolean, __is_assignable boolean, __is_active boolean, __is_default boolean, __member_count bigint, __total_items bigint, __tenant_id integer, __tenant_code text, __tenant_title text)
Results are ordered by title. The __total_items column contains the total matching count across all pages for pagination.
Permission required: groups.get_group / groups.get_all_groups (cross-tenant)
Source: 021_functions_auth_group.sql:1030
Batch Ensure Functions¶
These functions provide idempotent batch operations for provisioning groups and mappings, typically used during application setup or external configuration synchronization.
auth.ensure_user_groups¶
Ensures a set of groups exist for a tenant based on a JSON array input. Groups that already exist (matched by generated code) are skipped. Optionally operates in final-state mode, where groups from the same _source that are not in the input set are deleted.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_user_groups |
jsonb | -- | JSON array of group objects (see format below) |
_tenant_id |
integer | 1 | Tenant context |
_source |
text | null | Source identifier; required when _is_final_state is true |
_is_final_state |
boolean | false | When true, deletes groups with matching source that are not in the input |
Input JSON format:
Each element in the _user_groups array should be an object with:
| Field | Type | Default | Description |
|---|---|---|---|
title |
text | -- | Group title (used to generate the code) |
is_assignable |
boolean | true | Whether the group is assignable |
is_active |
boolean | true | Whether the group is active |
is_external |
boolean | false | Whether the group is external |
is_default |
boolean | false | Whether the group is a default group |
Returns: setof auth.user_group -- all groups matching the input titles for the tenant
Permission required: groups.create_group (and groups.delete_group when _is_final_state is true)
Source: 021_functions_auth_group.sql:1121
Warning
When _is_final_state is true, groups from the same source that are not in the input will be deleted, along with their mappings and permission assignments. System groups are never deleted.
auth.ensure_user_group_mappings¶
Ensures a set of group mappings exist based on a JSON array input. Uses ensure_user_group_mapping for each item. Optionally operates in final-state mode, where mappings for the same (group, provider) pair that are not in the input set are deleted.
| Parameter | Type | Default | Description |
|---|---|---|---|
_created_by |
text | -- | Identifier of the creating user/system |
_user_id |
bigint | -- | User performing the action |
_correlation_id |
text | -- | Correlation ID for tracing |
_mappings |
jsonb | -- | JSON array of mapping objects (see format below) |
_tenant_id |
integer | 1 | Tenant context |
_is_final_state |
boolean | false | When true, deletes mappings for the same (group, provider) pair not in the input |
Input JSON format:
Each element in the _mappings array should be an object with:
| Field | Type | Required | Description |
|---|---|---|---|
user_group_id |
integer | One of user_group_id or user_group_title |
Group ID |
user_group_title |
text | One of user_group_id or user_group_title |
Group title (resolved to ID via code lookup) |
provider_code |
text | Yes | Identity provider code |
mapped_object_id |
text | No | External group/object identifier |
mapped_object_name |
text | No | Human-readable name of the external group |
mapped_role |
text | No | External role name |
Returns: setof auth.user_group_mapping -- all processed mappings
Permission required: groups.create_mapping (and groups.delete_mapping when _is_final_state is true)
Source: 021_functions_auth_group.sql:1234
Warning
When _is_final_state is true, existing mappings for the same (group, provider) pair that are not in the input will be deleted. Permission caches for affected users are automatically invalidated.