canonical / canonical/postgresql-single-kernel-library
feat(database): accept GROUP entity requests from data-integrator to provision mapped authorization groups
- Dominant language
- Python
- Stars
- 4
- Forks
- 3
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 26
Description
### Description
Per the DA148 LDAP integration spec, PostgreSQL LDAP (identity) users get their authorization through group membership: the `ldap_map` config option maps LDAP groups to pre-created PostgreSQL group roles, and the `ldap-sync` sidecar grants synced users membership in the mapped role. The charm itself grants no database privileges to identity users — `identity_access` is a pure authentication marker (hba `ldap` line), by design (least privilege).
Today, the operator must therefore perform a manual `GRANT` after setting up the mapping:
```sql
CREATE ROLE NOLOGIN;
GRANT CONNECT ON DATABASE TO ;
```
This works, but the operator must know the mapping up front and run DDL by hand, and there is a foot-gun: `validate_group_map` accepts *any* existing role as a mapping target, including charm-managed LOGIN users (`relation_id_N`, system users).
### Suggested improvement
Support the GROUP entity protocol that `data_platform_libs` (vendored `data_interfaces.py`, `LIBAPI 0`) and `data-integrator` already implement on the requirer side:
- data-integrator exposes `entity-type=GROUP`, `extra-group-roles`, and `entity-permissions` (`[{"resource_name": ..., "resource_type": ..., "privileges": [...]}]`), and sends them in the relation application data.
- The PostgreSQL provider currently ignores them: `DatabaseRequestedEvent` exposes only `extra_user_roles` / `requested_entity_secret_content`, and `managers/database.py` unconditionally creates a LOGIN relation user.
### Proposed change (provider side)
1. `DatabaseRequestedEvent` (vendored `data_interfaces.py`): expose `entity_type`, `extra_group_roles`, and `entity_permissions` properties (read from the relation application data, mirroring the requirer-side fields).
2. `managers/database.py`: carry the fields in `DatabaseRequest`; on `entity_type == GROUP` create a NOLOGIN group role (no credentials/secret, no relation-access mixing) named e.g. `group_` and apply the requested `entity_permissions` on the requested database(s); skip the credential flow; write the resulting group name back into the relation data.
3. Hardening: reject mapping targets in `validate_group_map` that are charm-managed LOGIN users (`relation_id_%`, system users), so `ldap-map` cannot silently pour LDAP users into a client charm's LOGIN role (breaks per-user identity/auditing, and the mapping dangles if the relation is re-created).
### Resulting UX
```
juju config data-integrator entity-type=GROUP entity-permissions='[{"resource_name": "mydb", "resource_type": "database", "privileges": ["CONNECT"]}]'
juju config postgresql ldap-map=superheros=group_0 # group exists once the request is processed
```
No operator DDL: synced LDAP users inherit the group's privileges.
### Scope note
The LDAP integration test PRs (#1723 K8s / #1936 VM) currently keep the explicit operator grant and document this limitation; once this lands they can switch to the group-request flow and drop the grant.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the GROUP entity fields in the vendored data_interfaces.py and the existing DatabaseRequestedEvent flow, then trace DatabaseRequest and role handling in managers/database.py. Review validate_group_map for charm-managed LOGIN targets. Done means GROUP requests create a NOLOGIN role, apply entity_permissions, avoid credentials, and publish the group name back through the relation data.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- postgresql, python
- Domain
- authorization, backend-api-design, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100