mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 08:02:38 +00:00
Merge remote-tracking branch 'origin/main' into hugo/agent-memory-refactor
# Conflicts: # backend/ee-repo-ref.txt
This commit is contained in:
@@ -10,4 +10,4 @@ export const WM_FORK_PREFIX = "wm-fork";
|
||||
// (e.g. utils.ts) can read it without importing main.ts and creating a circular
|
||||
// dependency (main → workspace → utils → main) that triggers a TDZ.
|
||||
// Re-exported from main.ts for backwards compatibility.
|
||||
export const VERSION = "1.809.0";
|
||||
export const VERSION = "1.811.1";
|
||||
|
||||
Generated
+15
@@ -8525,6 +8525,21 @@ properties:
|
||||
type: boolean
|
||||
description: If true, passes the request body as a raw string instead of parsing
|
||||
as JSON
|
||||
allowed_origins:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: 'Origins allowed to call this route cross-origin, matched against
|
||||
the request''s Origin header (ignoring case) and echoed back on a match. When
|
||||
set, the list governs both the preflight and the response, overriding any Access-Control-Allow-Origin
|
||||
the runnable returns via wm_headers. Use [''*''] to opt out of any restriction,
|
||||
including the http_route_default_allowed_origins instance setting. An empty
|
||||
list is not a configuration and resolves exactly as null does. When null, the
|
||||
instance setting applies, or Access-Control-Allow-Origin: * if it is unset.
|
||||
Ignored on a static website, which has no authentication of its own and so hands
|
||||
out public files: restricting which browsers may read them protects nothing
|
||||
while breaking cross-origin webfonts and fetches. A single-file static asset
|
||||
is not exempt, since it can carry an authentication_method.'
|
||||
error_handler_path:
|
||||
type: string
|
||||
description: Path to a script to run when the triggered job fails. A bare path,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { deployItem } from "../windmill-utils-internal/src/deploy.ts";
|
||||
|
||||
// `deployItem` spreads the source item into the request body, and a script's/flow's
|
||||
// on_behalf_of names a username that only exists in the source
|
||||
// workspace. Sending it to the target pairs one workspace's principal with the other's
|
||||
// email, which the backend rejects. Deleting the spread is an easy regression, so pin
|
||||
// that the key never reaches the wire.
|
||||
// `deployItem` spreads the source item into the request body, and the principal it carries
|
||||
// (`on_behalf_of`, at the top level for a script or flow and inside the policy for an app)
|
||||
// names a username that only exists in the source workspace. Sending it to the target pairs
|
||||
// one workspace's principal with the other's email, which the backend rejects. Deleting the
|
||||
// spread is an easy regression, so pin that the principal never reaches the wire while the
|
||||
// caller's chosen address does.
|
||||
function recordingProvider(captured: [string, any][], flowExists: boolean) {
|
||||
const source = {
|
||||
on_behalf_of_email: "alice@corp",
|
||||
@@ -32,6 +33,19 @@ function recordingProvider(captured: [string, any][], flowExists: boolean) {
|
||||
}),
|
||||
createScript: async (p: any) =>
|
||||
void captured.push(["createScript", p.requestBody]),
|
||||
existsApp: async () => false,
|
||||
getAppByPath: async () => ({
|
||||
path: "f/x/a",
|
||||
summary: "",
|
||||
value: {},
|
||||
raw_app: false,
|
||||
policy: {
|
||||
execution_mode: "publisher",
|
||||
on_behalf_of: "u/alice",
|
||||
on_behalf_of_email: "alice@corp",
|
||||
},
|
||||
}),
|
||||
createApp: async (p: any) => void captured.push(["createApp", p.requestBody]),
|
||||
} as any;
|
||||
}
|
||||
|
||||
@@ -65,19 +79,29 @@ test("deployItem: never sends the source workspace's on_behalf_of", async () =>
|
||||
"dst",
|
||||
"alice@corp",
|
||||
);
|
||||
await deployItem(
|
||||
recordingProvider(captured, false),
|
||||
"app" as any,
|
||||
"f/x/a",
|
||||
"src",
|
||||
"dst",
|
||||
"alice@corp",
|
||||
);
|
||||
|
||||
expect(captured.map(([fn]) => fn)).toEqual([
|
||||
"createFlow",
|
||||
"updateFlow",
|
||||
"createScript",
|
||||
"createApp",
|
||||
]);
|
||||
for (const [, body] of captured) {
|
||||
// The email is still overridden with the caller's choice...
|
||||
expect(body.on_behalf_of_email).toBe("alice@corp");
|
||||
for (const [name, body] of captured) {
|
||||
expect(body.preserve_on_behalf_of).toBe(true);
|
||||
// Both surfaces spell it `on_behalf_of`; only its nesting differs — an app carries the
|
||||
// identity inside its policy, the others at the top level.
|
||||
const identity = name === "createApp" ? body.policy : body;
|
||||
// The email is still overridden with the caller's choice...
|
||||
expect(identity.on_behalf_of_email).toBe("alice@corp");
|
||||
// ...while the principal is dropped, so the backend derives the target's own.
|
||||
expect(
|
||||
"on_behalf_of" in JSON.parse(JSON.stringify(body)),
|
||||
).toBe(false);
|
||||
expect("on_behalf_of" in JSON.parse(JSON.stringify(identity))).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -506,10 +506,23 @@ export async function deployItem(
|
||||
},
|
||||
});
|
||||
} else if (kind === "app" || kind === "raw_app") {
|
||||
const app = await provider.getAppByPath({
|
||||
const rawApp = await provider.getAppByPath({
|
||||
workspace: workspaceFrom,
|
||||
path,
|
||||
});
|
||||
// See the flow branch: a source-workspace principal is never valid here, and the
|
||||
// policy carries the app's in `on_behalf_of`. Clearing it lets the backend derive
|
||||
// the target's own from the address. A group travels as its synthetic
|
||||
// `group-*@windmill.dev` address, which an admin-created account holding it would
|
||||
// win: known and accepted, see `users::permissioned_as_from_email` in the backend.
|
||||
const app = {
|
||||
...rawApp,
|
||||
policy: {
|
||||
...rawApp.policy,
|
||||
on_behalf_of: undefined,
|
||||
on_behalf_of_email: onBehalfOf,
|
||||
},
|
||||
};
|
||||
if (alreadyExists) {
|
||||
if (app.raw_app) {
|
||||
const secret = await provider.getPublicSecretOfLatestVersionOfApp({
|
||||
|
||||
Reference in New Issue
Block a user