mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-30 08:16:49 +00:00
* feat: fork capability per role and fork role mapping on rulesets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep unset fork for unrestricted roles and apply fork role mapping to fork-owner adds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: fork_roles feature usage telemetry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: derive an unset fork in the role editor as the server does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: update ee-repo-ref to d535972f0b35c6a7f257720816bdaec18465adef This commit updates the EE repository reference after PR #929 was merged in windmill-ee-private. Previous ee-repo-ref: 4a8d59cd1563382fcec85b1367f7e15e09bc13c2 New ee-repo-ref: d535972f0b35c6a7f257720816bdaec18465adef Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
82 lines
3.0 KiB
TypeScript
82 lines
3.0 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import type { EffectiveRole } from '$lib/gen'
|
|
import {
|
|
applyCapabilitiesPatch,
|
|
baseCapabilitiesFor,
|
|
FULL_CAPABILITIES,
|
|
resolveCapabilities,
|
|
validRoleOr,
|
|
workspaceRolesErrors
|
|
} from './workspaceRoles'
|
|
|
|
// These mirror `windmill_common::roles`; the server refuses what they let through.
|
|
describe('role capability layering', () => {
|
|
it('falls back to full for developer and to the operator rights for any other name', () => {
|
|
expect(baseCapabilitiesFor('developer')).toEqual(FULL_CAPABILITIES)
|
|
const other = baseCapabilitiesFor('reviewer')
|
|
expect([other.code, other.flows, other.manage_schedules]).toEqual([false, 'none', true])
|
|
})
|
|
|
|
it('applies only the keys a patch sets', () => {
|
|
const caps = applyCapabilitiesPatch(baseCapabilitiesFor('x'), {
|
|
flows: 'compose',
|
|
visibility: { workers: false }
|
|
})
|
|
expect(caps.flows).toBe('compose')
|
|
expect(caps.apps).toBe('none')
|
|
expect(caps.visibility.workers).toBe(false)
|
|
expect(caps.visibility.runs).toBe(true)
|
|
})
|
|
|
|
it('treats a stored null as unset, as serde reads it', () => {
|
|
const caps = applyCapabilitiesPatch(baseCapabilitiesFor('developer'), {
|
|
code: null as any,
|
|
visibility: { runs: null as any }
|
|
})
|
|
expect(caps).toEqual(FULL_CAPABILITIES)
|
|
expect(
|
|
workspaceRolesErrors([{ name: 'developer', capabilities: { code: null as any } }])
|
|
).toEqual({})
|
|
})
|
|
|
|
it('refuses what parse_instance_roles refuses', () => {
|
|
expect(workspaceRolesErrors(undefined)).toEqual({})
|
|
expect(workspaceRolesErrors([])).toHaveProperty('list')
|
|
expect(workspaceRolesErrors([{ name: 'admin' }])).toHaveProperty('0.name')
|
|
expect(workspaceRolesErrors([{ name: 'a' }, { name: 'a' }])).toHaveProperty('1.name')
|
|
expect(workspaceRolesErrors([{ name: 'x', capabilities: { flows: 'full' } }])).toHaveProperty(
|
|
'0.capabilities'
|
|
)
|
|
expect(
|
|
workspaceRolesErrors([{ name: 'developer', capabilities: { apps: 'compose' } }])
|
|
).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('validRoleOr', () => {
|
|
const role = (name: string) => ({ name }) as EffectiveRole
|
|
|
|
it('falls back to developer, or the first role when the instance dropped it', () => {
|
|
expect(validRoleOr([role('operator'), role('developer')], undefined)).toBe('developer')
|
|
expect(validRoleOr([role('builder'), role('viewer')], undefined)).toBe('builder')
|
|
expect(validRoleOr([role('builder'), role('viewer')], 'developer')).toBe('builder')
|
|
expect(validRoleOr([role('builder')], 'admin')).toBe('admin')
|
|
})
|
|
})
|
|
|
|
describe('resolveCapabilities', () => {
|
|
it('derives an unset fork from the restriction, as the server does', () => {
|
|
const maintainer = resolveCapabilities(
|
|
baseCapabilitiesFor('maintainer'),
|
|
{ code: true, flows: 'full', apps: 'full' },
|
|
false
|
|
)
|
|
expect(maintainer.fork).toBe(true)
|
|
const developer = resolveCapabilities(baseCapabilitiesFor('developer'), { code: false }, false)
|
|
expect(developer.fork).toBe(false)
|
|
expect(resolveCapabilities(baseCapabilitiesFor('operator'), { fork: true }, false).fork).toBe(
|
|
true
|
|
)
|
|
})
|
|
})
|