mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-23 16:03:33 +00:00
* feat: operators get AI sessions `prefersSessionHandoff` stops taking the role, so every AI entry point an operator can reach — Ask AI, the home composer, Open in session, the flow editor's hand-off, the raw-app template picker — opens a session for them like anyone else. The sessions page drops its refusal, and the layout stops keeping them on the docked chat. In the sidebar they now get the Workspace/AI Sessions switch and session mode, and lose the Ask AI entry that stood in for them, since the pane behind it is no longer the one they are sent to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: operators get run-oriented examples in the home composer Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: note builder_flows in the operator composer examples comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: gate session tools and editors on operator rights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: keep operator AI sessions behind a browser opt-in Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep flows refused in the prompt for operators without builder rights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor: decide every frontend edit check through one editRights module Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: let admins who are also operators open session editors, as drafts.rs does Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: let operators with manage rights save schedule and trigger drafts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: keep rebase_draft from operators who can only draft schedules and triggers Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: let operators save resource and variable drafts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: name deploy and delete tools only where a kind is left to act on Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: list an operator's draft-only resources and variables while they may draft them Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
128 lines
4.3 KiB
TypeScript
128 lines
4.3 KiB
TypeScript
import type { OperatorSettings } from '$lib/gen'
|
|
import type { UserExt } from '$lib/stores'
|
|
import { canWrite } from '$lib/utils'
|
|
|
|
/**
|
|
* The frontend's one copy of "may this user edit this item". The server decides — drafts.rs
|
|
* `require_can_write_path`, the item handlers' operator refusals, `gate_operator_writes`, and
|
|
* row-level security — and this mirrors it so a control the server would refuse is never
|
|
* offered. Check edits through `canEditItem` (or `useEditRights` in a component), never by
|
|
* pairing `canWrite` with an operator test of your own.
|
|
*/
|
|
|
|
/** Kinds whose role half differs. Any other kind is code an operator never authors. */
|
|
export type EditKind =
|
|
| 'script'
|
|
| 'flow'
|
|
| 'app'
|
|
| 'raw_app'
|
|
| 'agent'
|
|
| 'pipeline'
|
|
| 'schedule'
|
|
| 'trigger'
|
|
| 'resource'
|
|
| 'variable'
|
|
| 'group'
|
|
| (string & {})
|
|
|
|
type OperatorRights =
|
|
| Pick<
|
|
NonNullable<OperatorSettings>,
|
|
'builder_flows' | 'builder_apps' | 'manage_schedules' | 'manage_triggers'
|
|
>
|
|
| null
|
|
| undefined
|
|
|
|
/**
|
|
* Whether a user's role lets them author items of `kind` — create, edit, deploy or delete one —
|
|
* in a workspace with these `operator_settings`: the role half of every check against the item
|
|
* handlers. Saving a draft is `roleCanDraft`'s rule, and which items they may write is
|
|
* `canEditItem`'s other half. For an operator:
|
|
* - code (scripts, low-code apps, agents, pipelines): never;
|
|
* - flows and full-code apps: where the workspace grants `builder_flows` / `builder_apps`
|
|
* (`check_operator_can_build`);
|
|
* - schedules and triggers: unless the workspace set `manage_*` to false — a withdrawable
|
|
* right, so an unset key grants it (docs/operator-write-rights.md);
|
|
* - resources, variables and groups: always, their permissions alone decide.
|
|
*/
|
|
export function roleCanAuthor(
|
|
kind: EditKind,
|
|
user: Pick<UserExt, 'operator'> | undefined,
|
|
operatorSettings: OperatorRights
|
|
): boolean {
|
|
if (!user?.operator) return true
|
|
switch (kind) {
|
|
case 'flow':
|
|
case 'raw_app':
|
|
return builderGrants(kind, operatorSettings)
|
|
case 'schedule':
|
|
return operatorSettings?.manage_schedules !== false
|
|
case 'trigger':
|
|
return operatorSettings?.manage_triggers !== false
|
|
case 'resource':
|
|
case 'variable':
|
|
case 'group':
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the user may save drafts of `kind` — what every session editor and the chat's draft
|
|
* tools write. Mirrors drafts.rs `require_can_write_path`, which differs from the item handlers
|
|
* both ways: it admits an admin before its operator branch, and an operator drafts only the kinds
|
|
* they could deploy — resources and variables always, flows and full-code apps under their builder
|
|
* right, schedules and triggers under the manage rights.
|
|
*/
|
|
export function roleCanDraft(
|
|
kind: EditKind,
|
|
user: Pick<UserExt, 'operator' | 'is_admin' | 'is_super_admin'> | undefined,
|
|
operatorSettings: OperatorRights
|
|
): boolean {
|
|
if (user?.is_admin || user?.is_super_admin || !user?.operator) return true
|
|
switch (kind) {
|
|
case 'flow':
|
|
case 'raw_app':
|
|
return builderGrants(kind, operatorSettings)
|
|
case 'schedule':
|
|
case 'trigger':
|
|
return roleCanAuthor(kind, user, operatorSettings)
|
|
case 'resource':
|
|
case 'variable':
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
function builderGrants(kind: EditKind, operatorSettings: OperatorRights): boolean {
|
|
return (
|
|
(kind === 'flow' && operatorSettings?.builder_flows === true) ||
|
|
(kind === 'raw_app' && operatorSettings?.builder_apps === true)
|
|
)
|
|
}
|
|
|
|
/** `roleCanDraft` for the item at `path`, with its own permissions as in `canEditItem`. */
|
|
export function canDraftItem(
|
|
kind: EditKind,
|
|
path: string,
|
|
extraPerms: Record<string, boolean> | undefined,
|
|
user: UserExt | undefined,
|
|
operatorSettings: OperatorRights
|
|
): boolean {
|
|
return roleCanDraft(kind, user, operatorSettings) && canWrite(path, extraPerms ?? {}, user)
|
|
}
|
|
|
|
/** Whether `user` may edit the item at `path`: their role authors `kind`, and the item's own
|
|
* permissions (`canWrite`: ownership, folder write, `extra_perms`) let them write it. */
|
|
export function canEditItem(
|
|
kind: EditKind,
|
|
path: string,
|
|
extraPerms: Record<string, boolean> | undefined,
|
|
user: UserExt | undefined,
|
|
operatorSettings: OperatorRights
|
|
): boolean {
|
|
return roleCanAuthor(kind, user, operatorSettings) && canWrite(path, extraPerms ?? {}, user)
|
|
}
|