Repository navigation
fix(authz)!: site users' roles grant editing, checked on app:site; roles apply without a restart (#130) - #132
Merged
Merged
Conversation
…dmin settings Core's history engine, job state, search analytics and public sessions use the runtime directory, and the history engine uses max_revisions, so they cannot live under admin: when the admin is optional. They are system.runtime_dir and system.max_revisions now, resolved by resolveRuntimeDir() and resolveMaxRevisions(). The old admin.* keys, in both spellings, are refused with the new location (step 1 of the admin config plan). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ix slot) (#123, step 2) Core no longer reads admin.path. The admin plugin provides the admin.prefix capability slot; core reads it lazily for page-cache and transform exclusion, onRequest short-circuit protection, plugin route reservation, hybrid static output and the dev/serve banners. With no provider there is no admin and no path is treated as one. resolveCapabilities() resolves slots idempotently for callers that run before createDuneApp(). MCP stops reporting admin.path. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…st (#123, step 4) bootstrap() stops importing @dune/plugin-admin itself. The admin runs only when site.yaml lists it under plugins:; dune new writes the entry and dune migrate:config adds it and removes admin.enabled. admin.enabled and admin.authz_store are removed, the authProvider bootstrap option and the admin version guard are gone, the authz system is created when a plugin provides auth.session or declares authzActions, plugin factories receive (config, { root, dev }), multisite builds its island snapshot from the sites' plugins, and dune doctor keys its admin check on admin.prefix. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…#123, step 3) services.engines.mt/audit, PluginServicesOptions.root, SiteConfig.machine_translation and the @dune/core/mt and @dune/core/audit entry points are removed; @dune/plugin-admin owns them now. The response-transforms test stops importing types from the published admin. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t core's (#123, step 5) DuneConfig.admin and its types, defaults, schema entry and validation are removed; a system.yaml admin: block is refused. The settings live under config: of the admin's entry in plugins: and dune migrate:config moves them (lifting runtime_dir, max_revisions and data_dir to the top level). MCP stops reporting admin settings and drops the audit-log resource. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ig too site.machine_translation is the admin's setting: migrate:config moves it under config: of the admin's entry as machine_translation, renaming apiKey/baseUrl to api_key/base_url; a site.yaml that still has it is refused. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The authz system now exists whenever the site has a login (site.auth), a plugin provides auth.session, or a plugin declares authzActions, and mountDuneAuth reuses that one instance. admin, editor and author are granted once at group level (group:<role> holds <role> on app:site), so the role membership site auth already syncs is enough: no per-user tuple, no admin plugin. The resource is renamed from app:admin to app:site; tuples are derived data and re-seeded at every start. Closes #130 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t once (#130) Group membership, which page gating and the admin-tier role grants read, was only seeded at startup, so any change to roles[] waited for a restart. User stores that core creates are now wrapped with withRoleSync: create, a roles update and delete make member-of-group tuples match the record. The CLI role commands sync the same way, and the payments webhook stops writing the membership twice on a synced store. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The base was squash-merged into main (and this stack rebased), so history no longer matched. The tree is this PR's own change replayed onto the new base. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The base was squash-merged into main (and this stack rebased), so history no longer matched. The tree is this PR's own change replayed onto the new base. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The base was squash-merged into main (and this stack rebased), so history no longer matched. The tree is this PR's own change replayed onto the new base. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…mounts plugins; drop unused admin import-map entries; warn on per-environment admin.enabled headless-smoke set admin.enabled: false, which core now refuses. The headless main.ts imported the admin's mount subpath unversioned; it now calls mountPlugins and takes island specifiers from the loaded plugins. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ookup, document exact membership sync, note orphaned app:admin tuples Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts: # src/cli/migrate-config.ts # tests/cli/migrate_config_test.ts
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fresh's BuildOptions has no islandSpecifiers, so the option the headless main.ts passed to the Builder constructor was silently dropped and no plugin island (the admin's included) was registered. Register each specifier after construction, as dev.ts and serve.ts do. Fixes #140 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o feat/mt-audit-to-admin
…ver write a lifted key twice Two bugs in the admin-settings move: - A 0.35 site with `admin.enabled: false` had its whole admin: block deleted, including runtime_dir, max_revisions and data_dir, which are core settings now lifted to the top level for every other site. They are lifted here too. Keys that belong to forms or webhooks (honeypot, notifications, incoming_webhooks) make it a manual move, as for an enabled admin, instead of being deleted. - A lifted key already present at the top level of system.yaml was appended a second time, which @std/yaml refuses to parse. An equal value is skipped; a different one is reported as a manual move and the files are left untouched. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t/admin-settings-in-plugin
…nto fix/site-user-editing
# Conflicts: # src/auth/authz-schema.ts
zumbrunn
marked this pull request as ready for review
November 10, 2026 13:29
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 #130. Stacked on #131 (base
feat/admin-settings-in-plugin); retarget tomainonce the stack below it merges.What
A signed-in site user with an editing role can now edit inline on a site with no admin plugin, and a role granted or revoked while the app runs applies at once.
site.authis configured, a plugin providesauth.session, or a plugin declaresauthzActions.mountDuneAuthreuses that one instance. The inline-edit WebSocket checks the public-auth instance (it differs from bootstrap's whensite.auth.authz_storeisdb).bootstrapRoleGrantswritesgroup:admin|editor|author→admin|editor|authoronapp:siteonce. Site auth already keepsmember of group:<role>in sync, so having the role is enough: no per-user tuple, no admin plugin. A content-only role (member,subscriber, ...) grants nothing. Group subjects neededgroupadded to the schema'ssubjectTypes; spiked first, grants propagate to members.app:admin→app:site(SITE_RESOURCE, exported). The resource names what is protected, not the panel. Tuples are derived data and re-seeded at every start; an HMAC-protected store needsdune authz:signonce.roles[]. It was only seeded at startup, so any runtime role change waited for a restart (page gating has the same gap).syncUserRoles/withRoleSyncnow keep it equal on every create, roles update and delete, for user stores core creates (site auth,services.users.createLocalStore()). The CLI role commands sync too. The payments webhook checks membership first so a synced store doesn't get the tuple twice.Behaviour changes to review
admin,editororauthorin the IdP's token now carriespages.update. Before, only users the admin plugin registered had it. Easy to guard todunemode if you'd rather not.SITE_RESOURCE.@dune/plugin-adminand@dune/testingalready do (companion changes below).syncUserRolesitself.Tests
tests/runtime/site_user_editing_test.ts: login-only site, the same with anauth.sessionprovider, every role, and a role granted at runtime throughservices.users(403 → 204 → 403 without a restart). The first three fail without the fix.tests/auth/sync_roles_test.tsand a payments case.deno task test2270 pass,deno task checkclean, nodeno.lockchange. plugin-admin (283) and testing (44) suites pass against this branch.Companion changes (already pushed to their open drafts)
7225112, permissions onapp:site72ce003, stub admin onapp:site1141d11,AuthzServicedocsb15caeb,572cf04, authorization pageNot covered: whether a running server picks up a tuple the CLI writes to the file store from another process without a restart (true of the direct tuples before this too).
🤖 Generated with Claude Code