mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-21 10:54:08 +02:00
Holding an unsaved body against its response id let a plugin stash the id and read it in a later call, which would throw only sometimes and only for ad-hoc sends. Documenting that was never going to be enough. send now returns the response and its body together, so there is nothing to stash: an unsaved body is a value you were handed. ctx.httpResponse .body() goes back to meaning one thing, a saved response read by id, and refuses ids it has no row for. Reading is identical either way, so no caller has to know which kind of send it made.
95 lines
2.7 KiB
TypeScript
95 lines
2.7 KiB
TypeScript
import type { PluginDefinition } from "@yaakapp/api";
|
|
|
|
import { ntlm } from "httpntlm";
|
|
|
|
function extractNtlmChallenge(headers: Array<{ name: string; value: string }>): string | null {
|
|
const authValues = headers
|
|
.filter((h) => h.name.toLowerCase() === "www-authenticate")
|
|
.flatMap((h) => h.value.split(","))
|
|
.map((v) => v.trim())
|
|
.filter(Boolean);
|
|
|
|
return authValues.find((v) => /^NTLM\s+\S+/i.test(v)) ?? null;
|
|
}
|
|
|
|
export const plugin: PluginDefinition = {
|
|
authentication: {
|
|
name: "windows",
|
|
label: "NTLM Auth",
|
|
shortLabel: "NTLM",
|
|
args: [
|
|
{
|
|
type: "banner",
|
|
color: "info",
|
|
inputs: [
|
|
{
|
|
type: "markdown",
|
|
content:
|
|
"NTLM is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "text",
|
|
name: "username",
|
|
label: "Username",
|
|
optional: true,
|
|
},
|
|
{
|
|
type: "text",
|
|
name: "password",
|
|
label: "Password",
|
|
optional: true,
|
|
password: true,
|
|
},
|
|
{
|
|
type: "accordion",
|
|
label: "Advanced",
|
|
inputs: [
|
|
{ name: "domain", label: "Domain", type: "text", optional: true },
|
|
{ name: "workstation", label: "Workstation", type: "text", optional: true },
|
|
],
|
|
},
|
|
],
|
|
async onApply(ctx, { values, method, url }) {
|
|
const username = values.username ? String(values.username) : undefined;
|
|
const password = values.password ? String(values.password) : undefined;
|
|
const domain = values.domain ? String(values.domain) : undefined;
|
|
const workstation = values.workstation ? String(values.workstation) : undefined;
|
|
|
|
const options = {
|
|
url,
|
|
username,
|
|
password,
|
|
workstation,
|
|
domain,
|
|
};
|
|
|
|
const type1 = ntlm.createType1Message(options);
|
|
|
|
const { httpResponse: negotiateResponse } = await ctx.httpRequest.send({
|
|
httpRequest: {
|
|
method,
|
|
url,
|
|
headers: [
|
|
{ name: "Authorization", value: type1 },
|
|
{ name: "Connection", value: "keep-alive" },
|
|
],
|
|
},
|
|
});
|
|
|
|
const ntlmChallenge = extractNtlmChallenge(negotiateResponse.headers);
|
|
if (ntlmChallenge == null) {
|
|
throw new Error("Unable to find NTLM challenge in WWW-Authenticate response headers");
|
|
}
|
|
|
|
const type2 = ntlm.parseType2Message(ntlmChallenge, (err: Error | null) => {
|
|
if (err != null) throw err;
|
|
});
|
|
const type3 = ntlm.createType3Message(type2, options);
|
|
|
|
return { setHeaders: [{ name: "Authorization", value: type3 }] };
|
|
},
|
|
},
|
|
};
|