mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 16:02:59 +00:00
* feat: list agents on the home page and create them from the new menu Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep runnables out of the agents view and anchor a new agent once saved Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: autosave a new agent's first edit and list agents past one page Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add agent detail and editor pages Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: explain when an agent cannot be run and drop the broken agent move Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep query params and a unique path when creating an agent Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: lead the home list with agents and keep rows while the agent view loads Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: treat a loading agent as undeployed when leaving the editor page Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent detail page config panel, run page on form runs, chat badge Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent configuration modal and draft paths like other new items Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep a new agent draft-free until the first input, land agents with runnables Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: place AI agent after apps in the new menu and describe chat and flow use Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: new agents start with managed memory, editor form says how to turn it off Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: clearer managed memory hint in the agent editor form Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: share the agent editor's pane notice as a PaneNotice component Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: name a new agent after a path no resource holds Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: tell repeated tool names apart and hide permissions on draft-only agents Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent configuration beside the model in the chat composer and above the form Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: center the agent run form, configuration beside its Run button Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: icon-only agent configuration button beside Run Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agents run on behalf of their deployer through a run-by-path endpoint Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agents keep their run-as identity in their value, preserved by the CLI The identity an agent runs as lives in `value.on_behalf_of` instead of a column. Every write of an agent resolves it server-side as a flow's is: the writer's own, unless an admin or wm_deployers member asks to keep it, and a folder default on create. Retyping a resource into an agent resolves its value the same way. Export leaves it out; the run endpoint reads it and drops it from the step's inputs. The CLI follows the app model: a pushed agent never takes its identity from the tracked file, an unchanged agent compares equal to the deployed one, and an admin or deployer push claims the deployed identity back. The owner-change pre-check lists agents. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: deploying an agent draft from review keeps its deployed identity As for an app: an agent draft carries no identity, so the review page claims the deployed one back, which the backend honours for an admin or wm_deployers member. The draft diff leaves the deployed identity out, since a draft never holds one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: deploying an agent to another workspace offers the run-as choice An agent deployed to prod/staging, merged from a fork or promoted with `wmill workspace merge` gets the same identity choice as a flow or an app: the target's current one, the deployer, or a picked user, sent as a principal the way a trigger's is. The source workspace's principal is never copied, and a difference in identity alone is not a change in the workspace compare or its diff. The frontend consumes the published windmill-utils-internal, so its deploy provider carries the same rewrite until that version ships. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: agent identity on fork, agent-scoped job reads, and the run license gate A fork re-points an agent's identity at its creator when they may not preserve someone else's, and at the creator when it names nobody in the fork, as it does an app's. A token scoped to `jobs:run:agents:<path>` reads back the runs it starts, chat turns included. The agent run endpoint checks the enterprise license like every other run entrypoint. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: a CLI push decides agent identity handling by the tracked file's type An agent retyped into another resource by a push kept neither its value's `on_behalf_of` as the file stated it nor clear of the old agent's identity. The file's type now decides, and only a deployed agent's identity is claimed back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: agents run as the caller, with a note on what a shared one needs Drops the agent run-as identity: its storage in the agent value, the backfill, the resolution on every write, and its handling in export, the CLI, draft and cross-workspace deploys, forks and the workspace compare. The run endpoint runs as the caller, so an operator or reader runs an agent with their own access. The editor tells the author of a folder agent that anyone running it needs access to its AI resource and to what its tools use. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: agent editor comments describe runs as the caller Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: agent editor pane notes use Alert Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: agent editor pane notes render as Alert Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: agent editor notes as regular Alerts, not banners Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent path as its own editor level, and evals on the agent page The path leaves the agent form: the editor dialog opens it as a level, as it does evals, and the editor page in a drawer. The agent page gains Evals, in a dialog. The page supplies the run form, keeping it out of the editor the flow editor reaches. The draft edit gate no longer throws when a focused, changed field is removed: the `change` that removal fires lands mid-teardown, so the gate opens just after instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent settings as the resource editor's header fields, behind a cog The agent's own settings (path, labels, workspace specific, description) open from a cog as the flow and script editors' do, laid out as the top of the resource editor. The folder note is gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: agent settings fields and cog, completing the rename Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: evals open as a dialog over the agent editor page The editor page opens an agent's evals over itself, as the agent page does, with the unsaved edits offered to a run; the editor dialog keeps evals as a level of its own. The two pages share the dialog. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the unreachable model provider note reads like the unreachable agent one Same warning level and wording as the note above it, with the path inline rather than in parentheses that lost their spaces. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the unreachable model provider note offers editing the agent too Editing the agent to use a provider the reader can access is often the simpler way out; offered when the reader can write the agent, with Unlink and asking for access. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: provider note lists asking for access as its own option Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: shorter provider note, without the header's buttons repeated Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: evals only for those who can edit the agent Evaluating builds datasets and runs against the agent, which is authoring: the agent page and the editor offer it only with write access to the agent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: agent page actions ordered as the script and flow pages The menu leads and Edit comes last, as DetailPageHeader lays them out. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the agent editor dialog's levels slide in built on the first visit Warmed, as the evals pane's levels are, so settings and evals are mounted before the first navigation rather than inside its transition. Evals are only in the strip where they can be opened. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the agent's settings report path errors, and hold nothing a reader can edit The path field reads its error back, so it shows it and keeps deploy blocked on it. Labels are shown rather than editable without write access. Evals wait for the load to know they can be opened, and the page layout builds no levels it never shows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: one builder for an agent's run flow, and the agent page on the shared header The run endpoint and evals build the agent's one-step flow through the same function. The agent page uses DetailPageHeader, whose error handler, tag and trigger context are now optional, and whose menu items keep their disabled state. The home row and the page share the agent's menu and delete. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the agent page's menu is built from its current path Deploy settings are the workspace's, so they load once and the menu is derived from them rather than fetched per path, where a superseded fetch could land after a navigation. The shared run-flow builder lives with agent runs rather than evals. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: the scoped-read comment names the run-flow builder as it is Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
355 lines
19 KiB
SQL
355 lines
19 KiB
SQL
-- Fixture for the single-job read authorization regression test
|
|
-- (see tests/jobs_read_auth.rs).
|
|
--
|
|
-- Users available from `base`:
|
|
-- test-user (admin, token SECRET_TOKEN)
|
|
-- test-user-2 (User, token SECRET_TOKEN_2) -- owner of the secret script
|
|
-- test-user-3 (User, token SECRET_TOKEN_3) -- the unprivileged "viewer"
|
|
--
|
|
-- test-user-3 is NOT a member of any folder/group granting access to
|
|
-- `u/test-user-2/...`, so under the same RLS as `jobs/list` they cannot see any
|
|
-- of these jobs unless they created them.
|
|
|
|
-- A tag-scoped token for test-user-2 (who can read both VICTIM (tag 'deno') and
|
|
-- the flow (tag 'flow')). The `if_jobs:filter_tags:deno` modifier restricts it to
|
|
-- the 'deno' tag, so it must NOT be able to mint a share token for the 'flow' job.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('SCOPED_DENO_TOKEN'::bytea), 'hex'), 'SCOPED_DEN', 'SCOPED_DENO_TOKEN',
|
|
'test2@windmill.dev', 'scoped deno token', false,
|
|
ARRAY['jobs:read', 'if_jobs:filter_tags:deno']
|
|
);
|
|
|
|
-- A path-scoped run token for test-user-2, as the trigger UI mints per runnable for a
|
|
-- webhook caller. test-user-2 created every job this token is asserted against, so the
|
|
-- `created_by` grant would otherwise hand it all of them; it must reach only jobs of
|
|
-- `f/shared/flow1`.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('RUN_SCOPED_TOKEN'::bytea), 'hex'), 'RUN_SCOPE', 'RUN_SCOPED_TOKEN',
|
|
'test2@windmill.dev', 'flow webhook token', false,
|
|
ARRAY['jobs:run:flows:f/shared/flow1']
|
|
);
|
|
|
|
-- Same, scoped to a script. The two jobs below both run through a `singlestepflow`
|
|
-- wrapper (native retry / scheduled runs produce these) — one wrapping a script, one
|
|
-- wrapping a flow — so the confinement has to project each onto the runnable it wraps
|
|
-- rather than onto the wrapper's own `kind`.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('RUN_SCOPED_SCRIPT_TOKEN'::bytea), 'hex'), 'RUN_SCRIP', 'RUN_SCOPED_SCRIPT_TOKEN',
|
|
'test2@windmill.dev', 'script webhook token', false,
|
|
ARRAY['jobs:run:scripts:u/test-user-2/wrapped_script']
|
|
);
|
|
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, raw_flow
|
|
) VALUES (
|
|
'14141414-1414-1414-1414-141414141414', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'singlestepflow', 'deno', 'u/test-user-2/wrapped_script', 'deno', true,
|
|
'{"modules": [{"id": "a", "value": {"type": "script", "path": "u/test-user-2/wrapped_script"}}]}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('14141414-1414-1414-1414-141414141414', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"wrapped": "WRAPPED_RESULT"}');
|
|
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, raw_flow
|
|
) VALUES (
|
|
'15151515-1515-1515-1515-151515151515', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'singlestepflow', 'deno', 'f/shared/flow1', 'flow', true,
|
|
'{"modules": [{"id": "a", "value": {"type": "flow", "path": "f/shared/flow1"}}]}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('15151515-1515-1515-1515-151515151515', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"wrapped": "WRAPPED_FLOW_RESULT"}');
|
|
|
|
-- Scoped to an agent. An agent run is a preview of the one-step flow the run endpoint builds
|
|
-- (step `__wm_agent_root`), filed under the agent's path, or `<path>.chat` for a chat turn.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('RUN_SCOPED_AGENT_TOKEN'::bytea), 'hex'), 'RUN_AGENT', 'RUN_SCOPED_AGENT_TOKEN',
|
|
'test2@windmill.dev', 'agent token', false,
|
|
ARRAY['jobs:run:agents:f/shared/agent1']
|
|
);
|
|
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, raw_flow
|
|
) VALUES
|
|
('17171717-1717-1717-1717-171717171717', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flowpreview', 'deno', 'f/shared/agent1', 'flow', true,
|
|
'{"modules": [{"id": "__wm_agent_root", "value": {"type": "aiagent", "tools": []}}]}'),
|
|
('18181818-1818-1818-1818-181818181818', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flowpreview', 'deno', 'f/shared/agent1.chat', 'flow', true,
|
|
'{"modules": [{"id": "__wm_agent_root", "value": {"type": "aiagent", "tools": []}}]}');
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('17171717-1717-1717-1717-171717171717', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"agent": "AGENT_RESULT"}'),
|
|
('18181818-1818-1818-1818-181818181818', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"agent": "AGENT_CHAT_RESULT"}');
|
|
|
|
-- A token pairing an app scope with a run scope, as someone driving an app's components
|
|
-- programmatically would build. `APP_INLINE_JOB` is an inline-script component run: no
|
|
-- `jobs:run` scope can name its kind, so only the `apps:run` half puts it in reach.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('APP_RUNNER_TOKEN'::bytea), 'hex'), 'APP_RUNNE', 'APP_RUNNER_TOKEN',
|
|
'test2@windmill.dev', 'app runner token', false,
|
|
ARRAY['apps:run:u/test-user-2/dash', 'jobs:run:scripts:u/test-user-2/wrapped_script']
|
|
);
|
|
|
|
-- An inline-script component run of app `u/test-user-2/dash`, stamped with the
|
|
-- app provenance `execute_component` sets (`trigger_kind = 'app'`).
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, trigger_kind, trigger, args
|
|
) VALUES (
|
|
'16161616-1616-1616-1616-161616161616', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'appscript', 'deno', NULL, 'deno', false, 'app', 'u/test-user-2/dash',
|
|
'{"component": "arg"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('16161616-1616-1616-1616-161616161616', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"inline": "APP_INLINE_RESULT"}');
|
|
|
|
-- App embed token for the admin viewer (test-user). Mirrors a minted sandboxed
|
|
-- low-code app token: carries the `app_embed` sentinel plus the embed scope set.
|
|
-- Used to assert the token is confined to jobs the viewer LAUNCHED, not every job
|
|
-- the (admin) viewer could otherwise read.
|
|
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin, scopes) VALUES (
|
|
encode(sha256('EMBED_APP_TOKEN'::bytea), 'hex'), 'EMBED_APP_', 'EMBED_APP_TOKEN',
|
|
'test@windmill.dev', 'app embed token', false,
|
|
ARRAY['apps:run', 'jobs:read', 'app_embed', 'resources:run', 'users:read', 'folders:read']
|
|
);
|
|
|
|
-- A completed app-component job LAUNCHED BY the admin viewer (created_by =
|
|
-- test-user), running as the app owner. The embed token must keep reading its own
|
|
-- launched job (the `created_by == viewer` fast path).
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, args
|
|
) VALUES (
|
|
'12121212-1212-1212-1212-121212121212', 'test-workspace', 'test-user',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/app_component', 'deno', false,
|
|
'{"own": "arg"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('12121212-1212-1212-1212-121212121212', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"own": "EMBED_OWN_RESULT"}');
|
|
|
|
-- A QUEUED job launched by the admin embed viewer (created_by = test-user). The
|
|
-- embed token may cancel its own launched job; it must NOT cancel another user's.
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner
|
|
) VALUES (
|
|
'13131313-1313-1313-1313-131313131313', 'test-workspace', 'test-user',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/app_component', 'deno', false
|
|
);
|
|
INSERT INTO public.v2_job_queue (id, workspace_id, scheduled_for, running, tag) VALUES
|
|
('13131313-1313-1313-1313-131313131313', 'test-workspace', '2023-01-01 00:00:00', false, 'deno');
|
|
|
|
-- RUNNING job: queued (no completed row) and owned by test-user-2. Used to check
|
|
-- that `completed/get_result_maybe?get_started=true` authorizes before disclosing
|
|
-- running-state to a non-reader.
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner
|
|
) VALUES (
|
|
'77777777-7777-7777-7777-777777777777', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/running_secret', 'deno', true
|
|
);
|
|
INSERT INTO public.v2_job_queue (id, workspace_id, scheduled_for, running, tag) VALUES
|
|
('77777777-7777-7777-7777-777777777777', 'test-workspace', '2023-01-01 00:00:00', true, 'deno');
|
|
|
|
-- 1. VICTIM job: a completed run of test-user-2's private script, e.g. produced
|
|
-- by a public HTTP trigger. `created_by` is the route identity (test-user-2),
|
|
-- NOT the viewer; `permissioned_as`/`runnable_path` sit in test-user-2's
|
|
-- namespace; `visible_to_owner` is true. Its args + result carry secrets.
|
|
-- Pre-fix, test-user-3 could read all of these by UUID.
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, args
|
|
) VALUES (
|
|
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/secret_script', 'deno', true,
|
|
'{"secret": "LEAK_TEST_ARGS"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (
|
|
id, workspace_id, duration_ms, status, result
|
|
) VALUES (
|
|
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'test-workspace', 1000,
|
|
'success'::job_status, '{"secret": "RESULT_SECRET"}'
|
|
);
|
|
INSERT INTO public.job_logs (job_id, workspace_id, logs) VALUES
|
|
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'test-workspace', 'secret logs LEAK_TEST_LOGS');
|
|
|
|
-- 2. APP-style job: run by the viewer (test-user-3) on behalf of an app whose
|
|
-- policy executes as test-user-2. `created_by` is the launching viewer, but
|
|
-- `permissioned_as`/`runnable_path` are the app owner's and
|
|
-- `visible_to_owner` is false (apps hide their component runs from the runs
|
|
-- list). This is the case that must KEEP working after the fix: the viewer
|
|
-- polls their own component result by UUID.
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, args
|
|
) VALUES (
|
|
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'test-workspace', 'test-user-3',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/app_component', 'deno', false,
|
|
'{"app_arg": "ok"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (
|
|
id, workspace_id, duration_ms, status, result
|
|
) VALUES (
|
|
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'test-workspace', 1000,
|
|
'success'::job_status, '{"app_result": "visible_to_launcher"}'
|
|
);
|
|
|
|
-- 3. ANONYMOUS job: a public-trigger run whose creator is `anonymous`. Reading
|
|
-- it without authentication must keep working (unchanged behavior).
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner, args
|
|
) VALUES (
|
|
'cccccccc-cccc-cccc-cccc-cccccccccccc', 'test-workspace', 'anonymous',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/public_trigger', 'deno', true,
|
|
'{"public": "arg"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (
|
|
id, workspace_id, duration_ms, status, result
|
|
) VALUES (
|
|
'cccccccc-cccc-cccc-cccc-cccccccccccc', 'test-workspace', 1000,
|
|
'success'::job_status, '{"public": "result"}'
|
|
);
|
|
|
|
-- 4. FLOW + STEP: test-user-3 has *read* access to folder `shared` (extra_perms),
|
|
-- so they can see flow `f/shared/flow1` (run by test-user-2) even though they
|
|
-- did not launch it. The flow's STEP job runs the inner script
|
|
-- `u/test-user-2/inner_secret` (test-user-3 has NO direct ACL on it) and is
|
|
-- not in their list. Visibility must be INHERITED from the flow root: being
|
|
-- able to see the flow means being able to inspect its steps (the flow-run UI
|
|
-- fetches each step by id). This guards against the fix over-blocking.
|
|
INSERT INTO public.folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
|
VALUES ('test-workspace', 'shared', 'Shared Folder', '{"u/test-user-2"}',
|
|
'{"u/test-user-3": false}', 'test-user-2');
|
|
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner
|
|
) VALUES (
|
|
'dddddddd-dddd-dddd-dddd-dddddddddddd', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flow', 'deno', 'f/shared/flow1', 'flow', true
|
|
);
|
|
INSERT INTO public.v2_job_completed (
|
|
id, workspace_id, duration_ms, status, result
|
|
) VALUES (
|
|
'dddddddd-dddd-dddd-dddd-dddddddddddd', 'test-workspace', 1000,
|
|
'success'::job_status, '{"flow": "done"}'
|
|
);
|
|
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner,
|
|
parent_job, root_job, flow_innermost_root_job, args
|
|
) VALUES (
|
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/inner_secret', 'deno', true,
|
|
'dddddddd-dddd-dddd-dddd-dddddddddddd', 'dddddddd-dddd-dddd-dddd-dddddddddddd',
|
|
'dddddddd-dddd-dddd-dddd-dddddddddddd', '{"step_arg": "x"}'
|
|
);
|
|
INSERT INTO public.v2_job_completed (
|
|
id, workspace_id, duration_ms, status, result
|
|
) VALUES (
|
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'test-workspace', 1000,
|
|
'success'::job_status, '{"step": "STEP_RESULT_INHERITED"}'
|
|
);
|
|
|
|
-- 5. DEEP NESTING / MIDDLE-LAYER VISIBILITY: top flow `f/secret/top` is NOT
|
|
-- visible to test-user-3; it has a sub-flow step `f/shared/mid` that IS visible
|
|
-- (folder `shared`); and that sub-flow has its own leaf step running
|
|
-- `u/test-user-2/deep_secret` (not visible). The leaf's `root_job` points at the
|
|
-- *outermost* top (not visible), so visibility must come from the *intermediate*
|
|
-- sub-flow the user can see — which requires walking the full parent chain, not
|
|
-- just [self, root].
|
|
INSERT INTO public.folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
|
VALUES ('test-workspace', 'secret', 'Secret Folder', '{"u/test-user-2"}', '{}', 'test-user-2');
|
|
|
|
-- top flow (not visible to test-user-3)
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner
|
|
) VALUES (
|
|
'ffffffff-ffff-ffff-ffff-ffffffffffff', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flow', 'deno', 'f/secret/top', 'flow', true
|
|
);
|
|
-- intermediate sub-flow (visible via folder `shared`), child of top
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner,
|
|
parent_job, root_job, flow_innermost_root_job
|
|
) VALUES (
|
|
'99999999-9999-9999-9999-999999999999', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flow', 'deno', 'f/shared/mid', 'flow', true,
|
|
'ffffffff-ffff-ffff-ffff-ffffffffffff', 'ffffffff-ffff-ffff-ffff-ffffffffffff',
|
|
'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
|
);
|
|
-- leaf step of the sub-flow; runnable not visible, root_job = outermost top (not visible)
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner,
|
|
parent_job, root_job, flow_innermost_root_job
|
|
) VALUES (
|
|
'88888888-8888-8888-8888-888888888888', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'script', 'deno', 'u/test-user-2/deep_secret', 'deno', true,
|
|
'99999999-9999-9999-9999-999999999999', 'ffffffff-ffff-ffff-ffff-ffffffffffff',
|
|
'99999999-9999-9999-9999-999999999999'
|
|
);
|
|
INSERT INTO public.v2_job_completed (id, workspace_id, duration_ms, status, result) VALUES
|
|
('ffffffff-ffff-ffff-ffff-ffffffffffff', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"top": "TOP_SECRET_RESULT"}'),
|
|
('99999999-9999-9999-9999-999999999999', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"mid": "MID_RESULT"}'),
|
|
('88888888-8888-8888-8888-888888888888', 'test-workspace', 1000, 'success'::job_status,
|
|
'{"deep": "DEEP_STEP_INHERITED"}');
|
|
|
|
-- 6. QUEUED nesting for force-cancel: a hidden top flow (`f/secret/qtop`) whose
|
|
-- step is a sub-flow in the visible `shared` folder (`f/shared/qmid`). Force
|
|
-- cancel walks up to the highest queued ancestor, so force-cancelling the
|
|
-- sub-flow test-user-3 CAN see would kill the top flow they cannot.
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner
|
|
) VALUES (
|
|
'66666666-6666-6666-6666-666666666666', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flow', 'deno', 'f/secret/qtop', 'flow', true
|
|
);
|
|
INSERT INTO public.v2_job (
|
|
id, workspace_id, created_by, created_at, permissioned_as, permissioned_as_email,
|
|
kind, script_lang, runnable_path, tag, visible_to_owner,
|
|
parent_job, root_job, flow_innermost_root_job
|
|
) VALUES (
|
|
'55555555-5555-5555-5555-555555555555', 'test-workspace', 'test-user-2',
|
|
'2023-01-01 00:00:00', 'u/test-user-2', 'test2@windmill.dev',
|
|
'flow', 'deno', 'f/shared/qmid', 'flow', true,
|
|
'66666666-6666-6666-6666-666666666666', '66666666-6666-6666-6666-666666666666',
|
|
'66666666-6666-6666-6666-666666666666'
|
|
);
|
|
INSERT INTO public.v2_job_queue (id, workspace_id, scheduled_for, running, tag) VALUES
|
|
('66666666-6666-6666-6666-666666666666', 'test-workspace', '2023-01-01 00:00:00', true, 'flow'),
|
|
('55555555-5555-5555-5555-555555555555', 'test-workspace', '2023-01-01 00:00:00', true, 'flow');
|