Skip to content

feat: gate access behind a Flow User role - #92

Open
KushX wants to merge 2 commits into
frappe:developfrom
Cecypo-Tech:flow-user-access-control-pr
Open

feat: gate access behind a Flow User role#92
KushX wants to merge 2 commits into
frappe:developfrom
Cecypo-Tech:flow-user-access-control-pr

Conversation

@KushX

@KushX KushX commented Aug 6, 2026

Copy link
Copy Markdown

Problem

Flow has no access control. Any logged-in user can drive the assistant.

Flow Agent and Flow Model grant read to All. Flow Session and Flow Run do the same. None of the seven whitelisted endpoints check a role.

This includes website users. They cannot open the desk, but they can still call /api/method/flow.api.start_run.

Change

Add a Flow User role. Enforce it on every whitelisted endpoint. Admins grant it per user from User > Roles.

Four layers:

  1. Role. Flow User, desk_access = 1. Created in after_install and after_migrate. Idempotent.
  2. Enforcement. flow/permissions.py exposes assert_flow_access(). It runs first in all seven endpoints.
  3. UI. boot_session exports flow_enabled. The panel skips mounting when false, so there is no Ctrl+I shortcut. This is presentation only. app_include_js is static per site, so the bundle still ships to everyone.
  4. Permissions. The All rows on the four doctypes become Flow User.

Notes

Role permissions are OR'd. Adding a role alone changes nothing, so the All rows had to go.

The gate is the role alone. A System Manager without Flow User is refused. Administrator bypasses frappe.only_for upstream.

Triggers are not gated. They run as run_as or owner, who need not hold the role. flow/lib/session.py is untouched for that reason. The whitelisted API is the untrusted boundary. A test pins this.

Migration

flow/patches/grant_flow_role.py grants the role to existing enabled System Managers. Without it, every current user loses access on upgrade.

Each grant is isolated in a savepoint. Granting a role saves the User doc, which re-runs every installed app's user validation. Those can reject for reasons unrelated to Flow. A rejection rolls back that one user and the sweep continues. Skipped users are printed at the end.

Fresh installs skip the patch. install_app marks patches complete without running them.

Tests

flow/tests/test_permissions.py adds 11 cases:

  • user without the role is blocked
  • System Manager without the role is blocked
  • user with the role runs
  • Administrator always runs
  • every whitelisted endpoint rejects
  • boot exports the flag per role
  • triggers still fire when run_as lacks the role

Two existing suites needed a change. test_ai_api and test_flow_agent create users that must first clear the doctype read check. Those tests cover session ownership and User Permission, not the role gate. Without the role they would pass for the wrong reason.

Full suite run locally: 499 tests, no new failures against the base branch.

Flow had no access control. Any logged-in user could drive the
assistant through the whitelisted API. Website users included.

Add a Flow User role and enforce it on all seven endpoints. Grant
it per user from User > Roles.

- flow/permissions.py holds the role and the gate
- boot exports flow_enabled so the panel skips mounting for others
- doctype permissions move from All to Flow User
- a patch grants the role to existing System Managers so current
  users keep access

Each grant in the patch is isolated in a savepoint. Another app's
user validation cannot abort the migration.

Triggers stay ungated. They run as run_as or owner, who need not
hold the role.
@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Reviews (2): Last reviewed commit: "fix: sweep every endpoint and make role ..." | Re-trigger Greptile

Comment thread flow/permissions.py
Comment thread flow/tests/test_permissions.py Outdated
The gate sweep listed six of the seven endpoints. attach_file was
missing, so a regression there would not have failed the suite.

The call map is now asserted against flow.api.__all__ first. A newly
exported endpoint fails the test until it is covered.

Role creation checked existence, then inserted. Two lifecycle hooks
racing could both pass that check. The insert now ignores duplicates,
so the insert itself is the safe operation.
@KushX KushX mentioned this pull request Aug 12, 2026
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.

1 participant