Skip to content

A capability model: privileges, roles, and a store slice that gates the UI #1672

Description

@giswqs

Summary

Introduce an explicit capability model — a set of named privileges, a role that bundles them, and a store slice the UI consults — so "what may this person do in the app" becomes one answerable question instead of an assumption spread across every menu.

Why

The app assumes every user may do everything. That assumption is baked in implicitly: the Processing menu is always live, Add Data will fetch any URL, plugins can be installed, the AI assistant is available, PostGIS connections can be added, exports always work. There is no single place that could answer "may this user publish publicly?" or "may this user install a plugin?", so there is nowhere to put the answer.

That blocks several concrete requests at once:

  • a deployment where students may explore and export but not install plugins or add arbitrary remote URLs;
  • a project opened from a view-only share link that should genuinely be view-only rather than cosmetically limited (Share links: access roles, expiry, password, and revoke #1527 makes exactly this point about maponly, which hides chrome without deciding anything);
  • an organization where only publishers may share publicly (the organizations issue);
  • a shared-update group member who may edit content but not change its sharing.

Right now each of those would be implemented as one-off UI hiding in whichever component happens to be involved, and would drift.

Proposal

1. A privilege vocabulary

Name the privileges once, in @geolibre/core, close to the store. A first cut, drawn from what the app actually gates on:

layers:edit · layers:add-remote · layers:add-local · processing:run · processing:sidecar · project:save · project:share · project:share-public · plugins:install · assistant:use · connections:manage · export:data · export:image · settings:manage

2. Roles as bundles, plus custom roles

Ship four defaults — Viewer, Editor, Publisher, Administrator — as named bundles, and allow a custom role that picks privileges individually. Roles come from whichever authority is in play: the deployment config, the organization, the group, or the share link the project was opened from. When several apply, take the intersection.

3. A store slice and one hook

An ephemeral capabilities slice (same shape as the collaboration slice: never serialized into a project, never in undo history), plus a useCapability('processing:run') hook. Components disable with a reason rather than hide — a tooltip saying "your organization does not allow this" is a support ticket avoided, a missing button is one created.

4. Independent server enforcement

The slice is a mirror, not a gate. The sidecar must reject a processing call from a client that lacks processing:sidecar on its own authority, and the share host must reject a public publish from a client lacking project:share-public on its own authority. State this in the implementation as a rule: no privilege is considered enforced until a server refuses it.

The default for a plain local desktop user, with no org and no share-link role, is every privilege granted — this must not add friction for the single-user case that is the overwhelming majority.

Scope notes

  • Precedent to follow: the collaboration relay already computes effective edit permission identically on client and server (participantCanEdit()), and the client UI mirrors the relay. Generalize that pattern rather than inventing a parallel one.
  • Enables both the locked-down deployment mode and the enforced share-link roles in Share links: access roles, expiry, password, and revoke #1527; those two are the first consumers.
  • Keep the vocabulary small at first. A privilege nobody enforces is worse than an absent one, because it implies protection that does not exist.

Effort

Wishlist — the model is modest, but it touches most of the UI and needs matching server-side checks to mean anything.


Part of #1665 (access control umbrella).

Metadata

Metadata

Assignees

No one assigned

    Labels

    wishlistFeature requests that require significant efforts

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions