feat(v1.1.8): per-app roles + roles in user record (migration 0031)
migration 0031: app_user_roles table — composite PK (app_id, user_id,
role) so add is idempotent (ON CONFLICT DO NOTHING). v1.1.8 stores
strings only; permission matrices / hierarchies / role registry are
explicitly v1.2 work per the brief.
UsersServiceImpl wires roles: Arc<dyn AppUserRoleRepo>:
* fetch_roles() now actually queries the repo (replacing the empty
Vec stub from commit 4). Every AppUser returned from get /
find_by_email / list / update / verify / login now carries its
role list.
* users::add_role gated on AppUsersAdmin; first checks the user
exists in this app so a FK violation can't leak "no such user".
* users::remove_role gated on AppUsersAdmin; idempotent.
* users::has_role gated on AppUsersRead.
* accept_invite now applies pre-staged roles atomically with the
user creation; malformed role strings are skipped with a warn
rather than aborting the whole accept (the invitation was an
admin's promise — we honor as much of it as we can).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
21
crates/manager-core/migrations/0031_app_user_roles.sql
Normal file
21
crates/manager-core/migrations/0031_app_user_roles.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
-- v1.1.8 User Management — per-app string-tagged roles.
|
||||
--
|
||||
-- v1.1.8 ships ROLE STORAGE ONLY. There is no role registry, no
|
||||
-- hierarchy, no permission matrix — what "admin" / "editor" /
|
||||
-- "viewer" mean is up to the script app. The `users::*` SDK
|
||||
-- surfaces a Vec<String> on every AppUser record; the surrounding
|
||||
-- script reads it and gates behavior accordingly.
|
||||
--
|
||||
-- Per-role permission matrices are a v1.2 design item (see brief);
|
||||
-- pre-baking them would either cement a wrong model or force a
|
||||
-- breaking change at v1.2.
|
||||
|
||||
CREATE TABLE app_user_roles (
|
||||
app_id UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES app_users(id) ON DELETE CASCADE,
|
||||
role TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (app_id, user_id, role)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_app_user_roles_user ON app_user_roles (app_id, user_id);
|
||||
Reference in New Issue
Block a user