Skip to content

Auth refactor: SITE_ACCESS/AUTH_PROVIDERS, auth provider plugins, scoped role bindings, groups, token v2 - #1139

Open
devin-ai-integration[bot] wants to merge 49 commits into
developmentfrom
devin/1792534660-authz-redesign
Open

devin-ai-integration[bot] wants to merge 49 commits into
developmentfrom
devin/1792534660-authz-redesign

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

  • Replace the ambiguous AUTH=off|none|local|csso setting with explicit SITE_ACCESS (what a visitor experiences) and AUTH_PROVIDERS (how people log in).
  • Replace the 111/110/001 permission string with named capabilities and roles granted to users, groups or audiences at global, mission or layer-subtree scope, checked in one place (can() / authorize()).
  • Make login methods pluggable: a new auth plugin type for login providers, with local and csso as the first core providers.
  • Existing usernames and passwords keep working unchanged; their permissions are imported into the new model.

Proposed Changes

  • [CHANGE] SITE_ACCESS=public|optional|required (no default) and AUTH_PROVIDERS. The server refuses to start while AUTH is set and prints the exact replacement (API/permissions/siteAccess.js). .env, sample.env, ENVs.md updated.
  • [ADD] Permission core in API/permissions/: policy.js (capabilities; roles superadmin, data_admin, mission_admin, layer_editor, viewer; allow-only), principal.js (session / provider identity / bearer token / guest), authorize.js (authorize(cap, resolver), authorize.public(reason), authorize.inHandler), bindings.js (grant/revoke, no granting above your own access, always at least one SuperAdmin), missions.js (stable mission UUIDs: kept through rename, new on clone; layer UUID ancestry), models.js.
  • [ADD] Auth provider plugins (plugins/core/auth/<Name>/, "type": "auth"), registry in API/permissions/providers.js. Providers must be core plugins with "tier": "core", "overridable": false; providers in other containers are refused at startup. A provider can export identify(req), routes(router, ctx) (mounted at /api/auth/<id>, route permissions enforced like core routes), validate(env) and warnings(env). ctx.completeLogin() links provider + subject to an account in the new auth_identities table (never by matching an existing username) and syncs membership of groups sourced from that provider. AUTH_PROVIDERS only accepts discovered providers.
    • core/auth/Local: username/password accounts (routes still served by the Users backend at /api/users).
    • core/auth/CSSO: the existing X-Sub / X-Groups / X-Session header login, moved out of scripts/server.js, with the optional CSSO_PROXY_SECRET check.
    • ldap, earthdata, keycloak, cognito are reserved names for future providers; setting one fails startup with "not supported yet".
  • [ADD] Tables missions, auth_groups, auth_group_members, role_bindings, auth_identities; token v2 columns; owner_mission_uuid on geodatasets/datasets (unused for now).
  • [CHANGE] Every core /api route declares its capability; startup fails on an undeclared route. URL-suffix allowlists and 111/110 checks removed; legacy values are only read by the one-time import. Adjacent-server proxies map GET → *.read, other methods → *.write.
  • [ADD] External backend plugins that mount routes without a declared permission (e.g. via s.app.use) still load; startup logs a warning listing those routes. Existing setup helpers (ensureUser, stopGuests, etc.) are unchanged.
  • [CHANGE] Mission admins edit their missions' configuration and layers only. Mission create/clone/rename/delete, accounts, options, webhooks are SuperAdmin; geodatasets, datasets and STAC are SuperAdmin or data_admin. Layer editors save through the per-layer routes; /api/configure/upsert needs mission.config.edit.
  • [CHANGE] Strict mission isolation, with Missions/shared readable by signed-in users.
  • [CHANGE] Long-term tokens: hashed (plaintext shown once; old tokens converted on first use), named, read-only or full scopes, optional mission limit, expiry, revocation, last-used. A token is always capped by its creator's current access. Any signed-in user can create read-only tokens. Tokens can't manage tokens.
  • [ADD] npm run auth:migrate -- --dry-run / npm run auth:migrate: imports legacy permissions into bindings (also runs once on startup); never writes username or password; idempotent. Legacy columns kept so rolling back still works.
  • [ADD] Configure → Access page (/api/access/*, plugins/core/backend/Access): groups and members, user/group/audience bindings with scope pickers, effective access with where it comes from. Users page edits roles directly. Navigation is gated by capabilities. Token page gets scope and mission pickers.
  • [FIX] Deleting a user now also removes their bindings and tokens; /api/utils/healthcheck answers when login is required; API routes answer guests on a login-required site with a JSON 403.
  • [ADD] Docs: Migration → Site Access & Roles (docs/pages/Migration/auth-access/auth-access.md), Auth Providers section in plugins/README.md, adjacent servers must only be reachable through MMGIS' proxy, spec 001 updated.

Issues

  • None linked.

Testing

  • Unit (tests/unit/permissions-*.spec.js): every role/capability/scope, groups and audiences, layer inheritance, token intersections, last-SuperAdmin, property checks (token never exceeds creator, removing a binding never adds access); provider discovery (core-only, non-overridable, duplicate ids, broken plugins), reserved provider names, CSSO header parsing and proxy secret, account linking never taking over an existing username, provider group sync; plugin route warnings. Full unit suite: 1528 passed.
  • tests/e2e/auth/permission-matrix.spec.js (real API, SITE_ACCESS=required): two missions with folder/child/sibling layers; SuperAdmins, mission admin, layer editor, viewer, plain user, data admin, group member; mission list filtering and config isolation, layer-subtree writes, /upsert limits, site-wide operations denied, group grant/revoke, last-SuperAdmin, read-only and mission-limited tokens, creator demotion, token expiry, guests denied.
  • tests/migration/run-legacy-compat.sh: seeds 11 accounts (111, 110 with one/several missions, viewers, mixed case, email-like, Unicode, long password, demoted admin) on origin/development, runs the dry run, starts this branch on the same DB: 11 password hashes byte-identical, 44 login probes (right/wrong password, changed-case and trailing-space usernames) identical to before. Runs in CI for PRs.
  • Existing E2E suites tests/e2e/{api,security,startup,auth} on a fresh mmgis-test DB, 0 failures: required 173 passed, optional 149 passed, public 145 passed (rest skipped by mode). CI (test (local), test (off), including the upgrade check) passes.
  • Manual: AUTH_PROVIDERS=["local","csso"] with CSSO_PROXY_SECRET set: /api/access/me is anonymous without headers or with a wrong secret, and an external/csso principal with the reported groups with the right secret. External Agent and Workflows plugins from MMGIS-Plugins load, and startup lists their undeclared routes as warnings.
  • Configure build passes.

Upgrade note: deployments must replace AUTH before starting this version (table in ENVs.md and the migration page) and should run npm run auth:migrate -- --dry-run first.

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/8608a14efd934ae6b7f1f1695ce0b347
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/8608a14efd934ae6b7f1f1695ce0b347?variant=devin
Requested by: @tariqksoliman

devin-ai-integration Bot and others added 24 commits October 20, 2026 22:21
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…red access

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…gated navigation, token scope picker

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…e defaults, account deletion with tokens, healthcheck under required access

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…routes; existing tests follow the new access rules

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

✅ Version Already Updated

This PR includes a manual version update to 5.5.0-20261021

No automatic version bump needed.

github-advanced-security[bot]

This comment was marked as resolved.

github-advanced-security[bot]

This comment was marked as resolved.

…undeclared backend plugin routes

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
devin-ai-integration Bot and others added 2 commits October 21, 2026 01:17
…setTimes

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…stalls

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@devin-ai-integration devin-ai-integration Bot changed the title Auth refactor: SITE_ACCESS/AUTH_PROVIDERS, scoped role bindings, groups, token v2 Auth refactor: SITE_ACCESS/AUTH_PROVIDERS, auth provider plugins, scoped role bindings, groups, token v2 Oct 21, 2026
devin-ai-integration Bot and others added 19 commits October 21, 2026 01:41
…er IPv4; allowlist test fixtures

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…accepts a login-required refusal

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… client addresses; token test matches by id

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…tenance DB

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…sites show the unauthorized page instead of the local login form

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…havior

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…n /configure; restore CRLF in websocket.js; migration guide per previous AUTH setup

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…s, missions, site-wide; grant dialog with layer picker)

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…s dropdown in the side panel; style UA_v1 tables like Users

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…k header in side panels

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… clarify Site-wide audience descriptions

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… All/None, colored site-role badges; drop card wrappers to match Users page

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…el view radio from grants

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…on bars

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…actions

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@tariqksoliman
tariqksoliman marked this pull request as ready for review October 22, 2026 00:32
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 7 potential issues.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment thread API/permissions/bindings.js Outdated
Comment on lines +26 to +31
const rows = await RoleBinding.findAll({
where: { subject_type: "user", role: "superadmin", scope_type: "global" },
attributes: ["subject_id"],
raw: true,
});
return rows.map((r) => parseInt(r.subject_id));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Duplicate bindings undermine last-admin safeguard

When a user has duplicate SuperAdmin bindings, superAdminUserIds counts rows instead of distinct users. Deleting that account can leave the site without an administrator.

Learn more

PostgreSQL treats NULL values as distinct in the composite role-binding index, so repeated global grants can create multiple rows for one user. The account deletion guard checks the length of this list before deleting every binding for the account; two rows for the sole administrator pass the check. remove account then deletes all of that user's bindings.

Example: Grant the sole administrator the global SuperAdmin role twice using the Access page; both rows can exist. Delete that administrator; superAdminUserIds() returns two entries with the same ID and allows deletion.

Recommended fix: Count distinct administrator user IDs in the last-admin guard, and enforce uniqueness for bindings with nullable scope fields (for example, separate partial unique indexes per scope). Test duplicate grants and deletion of the only administrator.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: superAdminUserIds() counts each user once, and removing a SuperAdmin row counts that user's own rows, so two rows for one user no longer count as two SuperAdmins.

Comment thread API/permissions/bindings.js Outdated
Comment on lines +120 to +125
const resource = mission_uuid
? { mission: mission_uuid, layers: layer_uuid ? [layer_uuid] : [] }
: null;
for (const cap of policy.ROLES[role].capabilities)
if (!policy.can(actor, cap, policy.MISSION_SCOPED.has(cap) ? resource : null))
throw new BindingError("You cannot grant a role with more access than your own.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nested layer grants reject eligible editors

When a layer editor grants access to a child, addBinding checks only the child's UUID. The editor's parent-layer binding cannot match, so eligible delegation fails.

Learn more

A layer binding applies to the entire subtree. Authorization for edits uses layer resolver, which includes all ancestor UUIDs in the resource chain. addBinding verifies the layer exists but constructs a chain with only the child UUID, rejecting a parent-bound editor's legitimate grant.

Example: An editor bound to folder F can edit layer L inside F. When they try to grant layer_editor on L, policy.can sees only L and denies the grant because their binding names F.

Recommended fix: Use the already-validated missions.layerChain(config.layers, layer_uuid) as the resource's layers array, and cover nested delegation in tests.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: addBinding now checks the grant against the layer's whole ancestor chain (missions.layerChain), so someone who can edit a folder can grant on its child layers, and still nothing outside it.

Comment on lines +223 to +235
const role = body.role;
if (role === "superadmin") {
await bindings.addBinding(actor, userBinding("superadmin", "global"));
} else if (role === "mission_admin" || role === "user") {
await remove((b) => b.role === "superadmin" && b.scope_type === "global");
await bindings.setUserMissionRole(
actor,
id,
"mission_admin",
role === "mission_admin" && Array.isArray(body.missions_managing)
? body.missions_managing.filter((m) => typeof m === "string")
: []
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Failed account update leaves partial roles

When a requested mission is invalid, updateUser removes old bindings before setUserMissionRole rejects it. The response reports failure, but the user's earlier grants remain deleted.

Learn more

Account updates rewrite persisted role bindings. setUserMissionRole removes old mission grants before attempting each new grant, and an unknown mission causes addBinding to throw. Because no transaction wraps the update, the failed request preserves any prior deletions or successful additions.

Example: An admin changes a user's missions from A to B and Typo; the A binding is removed and B may be granted, then Typo fails. The UI reports failure even though the user's permissions changed.

Recommended fix: Validate all requested missions and grants before modifying the account, then apply email and binding changes in one transaction. Roll back all mutations on failure.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: updateUser now checks the role, every mission name and the delegation limits before changing anything, and the email is written only after the role changes succeed. Covered by the e2e test "a rejected account update changes nothing" in tests/e2e/auth/permission-matrix.spec.js.

Comment thread API/permissions/policy.js Outdated
Comment on lines +140 to +146
if (
Array.isArray(token.missions) &&
resource &&
resource.mission &&
!token.missions.includes(resource.mission)
)
return false;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Mission-limited tokens retain unrestricted global powers

A token limited to mission A still passes policy.can for global capabilities because the mission limit applies only when a resource carries a mission. A holder can use that token to modify site-wide datasets or create missions despite its mission restriction.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: a token limited to missions now only reaches mission capabilities inside those missions, plus site-wide reads; mission.create, account, token and other site-wide capabilities are refused. Covered by unit tests in tests/unit/permissions-policy.spec.js and the e2e test "mission-limited tokens cannot use site-wide capabilities".

Comment thread scripts/server.js
Comment on lines +505 to 508
})
.catch(next);
};
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Guest writes reach adjacent servers

On public or optional sites, ensureUserForAdjacentServers allows guests for every HTTP method. Guests can send POST or DELETE requests through enabled adjacent proxies, changing upstream data.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: on public/optional sites, requests other than GET/HEAD through the adjacent-server proxies now go through ensureAdmin; reads still follow the site gate. Covered by tests/e2e/auth/guest-access.spec.js (POST/PUT/DELETE on /stac, /tipg, /titiler, /titilerpgstac).

* User login
*/
router.post("/login", authLimiter, function (req, res) {
router.post("/login", authLimiter, authorize.public("login and account self-service"), function (req, res) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Disabled local provider still accepts passwords

With AUTH_PROVIDERS excluding local, /api/users/login still authenticates existing accounts. Users can establish local sessions despite a deployment configured for external login only.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: /api/users/login now refuses password login unless local is in AUTH_PROVIDERS. The page-reload token path is unchanged.

Comment thread API/permissions/policy.js
Comment on lines +160 to +167
// Editing any layer in a mission implies viewing that mission
if (cap === "mission.view" && resource && resource.mission)
return bindings.some(
(b) =>
ROLE_CAPS[b.role] &&
ROLE_CAPS[b.role].has(cap) &&
b.scope.startsWith(`layer:${resource.mission}:`)
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Restricted mission configs exposed through layer editor role

A layer-scoped editor receives mission.view for their mission. The config load returns the full mission configuration rather than a layer-filtered view, exposing other layers in that mission.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this as is, by design: editing a layer implies viewing that mission, the same as a Viewer there. This model has no per-layer view restriction (layer scopes only limit editing), and the Configure editor needs the whole layer tree to show where the editable layers sit. Mission isolation still holds: a layer editor can't read other missions (see "mission configs are readable only inside granted missions").

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 7 potential issues.

Devin Review

Comment thread API/permissions/bindings.js
Comment thread API/permissions/bindings.js
Comment thread API/permissions/missions.js
* User login
*/
router.post("/login", authLimiter, function (req, res) {
router.post("/login", authLimiter, authorize.public("login and account self-service"), function (req, res) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Disabled password login still grants sessions

When AUTH_PROVIDERS excludes local, /login still authenticates stored passwords and starts sessions. Existing local credentials bypass the configured CSSO-only login policy.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: /api/users/login now refuses password login unless local is in AUTH_PROVIDERS. The page-reload token path is unchanged.

Comment thread API/permissions/principal.js Outdated
Comment thread API/permissions/policy.js Outdated
Comment on lines +141 to +148
Array.isArray(token.missions) &&
resource &&
resource.mission &&
!token.missions.includes(resource.mission)
)
return false;
}
if (MISSION_SCOPED.has(cap) && !(resource && resource.mission)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Mission-limited tokens create unrestricted missions

A full-access token limited to Alpha can still call mission.create for Beta. can checks token missions only when the resource contains a mission, but creation has no resource.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c7cb257: a token limited to missions now only reaches mission capabilities inside those missions, plus site-wide reads; mission.create, account, token and other site-wide capabilities are refused. Covered by unit tests in tests/unit/permissions-policy.spec.js and the e2e test "mission-limited tokens cannot use site-wide capabilities".

Comment thread plugins/core/backend/Config/routes/configs.js Outdated
- Bearer tokens keep their limits when sent with a session; a token from another user is refused
- Mission-limited tokens only reach their missions plus site reads
- Account updates validate everything before changing anything
- SuperAdmin removals lock the SuperAdmin rows so one always remains
- Layer grants check the whole ancestor chain; duplicate SuperAdmin rows count once
- Renaming a mission refreshes derived account mission lists
- Password login requires the local provider
- Shared mission files need a signed-in user; proxy writes need an admin on open sites

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants