Launcher and auth gate share one visibility predicate, so runtime grants show in /_apps - #56
Merged
Merged
Conversation
…nts show in /_apps The /_apps launcher and enlace_auth's request-time gate each computed "may this user reach this app" separately, and drifted: the gate unioned static allowed_users with live runtime grants (case-insensitively), the launcher did neither. A granted user could open an app they could never find. New enlace.access module holds the single copy: is_user_allowed (the protected:user allowlist predicate), can_see_app (launcher visibility by access level), and granted_users (per-request grants resolution, fail-closed on resolver errors). The launcher and its icon route read the grants resolver from the root app's state (GRANTS_STATE_ATTR = dynamic_allowed_users), which the auth plugin installs; absent it, behaviour is unchanged. Refs #35 (the gate side lands in enlace_auth, calling the same predicate).
… log The built-in HTML index (used when there is no landing app) listed every app to every caller; it now shows only what the caller may open, via the same _can_access as /_apps. granted_users logs one line per failure (traceback at DEBUG) with the text the gate used before, since the launcher resolves every protected app per call.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #35.
What was wrong
Two code paths answered "may this user reach this app": enlace_auth's request-time gate (static
allowed_users∪ live runtime grants, case-insensitive) and the/_appslauncher here (static list only, case-sensitive). A user with a runtime grant could open an app but never see it in the launcher.The fix: one predicate, called by both sides
enlace/access.py:is_user_allowed(theprotected:userallowlist predicate),can_see_app(launcher visibility by access level),granted_users(per-request grants resolution, fail-closed on resolver errors), andGRANTS_STATE_ATTR(dynamic_allowed_users), the root-app state slot where the auth plugin puts the same grants resolver its gate uses./_apps,/_apps/{name}/icon(and the other icon/manifest routes behind_visible_app) and the built-in HTML index all go through_can_access, which delegates toenlace.access. The built-in index used to list every app to everyone; it now lists only what the caller may open.allowed_usersmatching is now case-insensitive, like the gate.The gate side lands in i2mint/enlace_auth (a PR follows once this release is on PyPI): its middleware calls
enlace.access.is_user_allowed+granted_users, and its plugin hands the same resolver to both sides. That repo also carries the end-to-end test that runs the real gate and the real launcher side by side and asserts opens ⟺ listed for static-only, grant-only, both, neither, mixed case and open apps, plus grant and expiry without a restart.Review
An independent adversarial review compared old and new gate behaviour line by line. It found no blockers. What it raised, and what was done:
enlace_auth.middlewaretoenlace.access.user_id. The gate now refuses a session whoseuser_idis missing. Before, it could let one into an open app. Every session creator setsuser_id, so only a malformed record is affected. This is the stricter direction./_appscall with 30 apps on the file-backed grant store. That was judged acceptable. Caching the grants would bring back the expiry drift that A runtime grant lets a user open a protected app but not see it in the launcher #35 warns about.Release ordering
Merging releases enlace 0.1.41. enlace_auth will require
enlace>=0.1.41. New enlace with the old enlace_auth is safe: no resolver is injected, so the launcher uses the static list only.https://claude.ai/code/session_01JQJh4hGdYjSKiFkyj9zrAh