llng-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +77 -0
- package/.prettierrc +7 -0
- package/LICENSE +661 -0
- package/README.md +502 -0
- package/dist/__tests__/api-transport.test.d.ts +1 -0
- package/dist/__tests__/api-transport.test.js +577 -0
- package/dist/__tests__/api-transport.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +472 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/integration/api-mode.test.d.ts +1 -0
- package/dist/__tests__/integration/api-mode.test.js +199 -0
- package/dist/__tests__/integration/api-mode.test.js.map +1 -0
- package/dist/__tests__/integration/oidc-rp.test.d.ts +1 -0
- package/dist/__tests__/integration/oidc-rp.test.js +120 -0
- package/dist/__tests__/integration/oidc-rp.test.js.map +1 -0
- package/dist/__tests__/integration/ssh-mode.test.d.ts +1 -0
- package/dist/__tests__/integration/ssh-mode.test.js +101 -0
- package/dist/__tests__/integration/ssh-mode.test.js.map +1 -0
- package/dist/__tests__/k8s-transport.test.d.ts +1 -0
- package/dist/__tests__/k8s-transport.test.js +254 -0
- package/dist/__tests__/k8s-transport.test.js.map +1 -0
- package/dist/__tests__/oidc-tools.test.d.ts +1 -0
- package/dist/__tests__/oidc-tools.test.js +457 -0
- package/dist/__tests__/oidc-tools.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +1 -0
- package/dist/__tests__/registry.test.js +96 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/ssh-transport.test.d.ts +1 -0
- package/dist/__tests__/ssh-transport.test.js +618 -0
- package/dist/__tests__/ssh-transport.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +1 -0
- package/dist/__tests__/tools.test.js +525 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.js +506 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/documentation.d.ts +5 -0
- package/dist/resources/documentation.js +56 -0
- package/dist/resources/documentation.js.map +1 -0
- package/dist/tools/cli-utilities.d.ts +3 -0
- package/dist/tools/cli-utilities.js +187 -0
- package/dist/tools/cli-utilities.js.map +1 -0
- package/dist/tools/config.d.ts +6 -0
- package/dist/tools/config.js +326 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/consents.d.ts +3 -0
- package/dist/tools/consents.js +39 -0
- package/dist/tools/consents.js.map +1 -0
- package/dist/tools/instances.d.ts +3 -0
- package/dist/tools/instances.js +14 -0
- package/dist/tools/instances.js.map +1 -0
- package/dist/tools/oidc-rp.d.ts +6 -0
- package/dist/tools/oidc-rp.js +246 -0
- package/dist/tools/oidc-rp.js.map +1 -0
- package/dist/tools/oidc.d.ts +3 -0
- package/dist/tools/oidc.js +343 -0
- package/dist/tools/oidc.js.map +1 -0
- package/dist/tools/secondfactors.d.ts +3 -0
- package/dist/tools/secondfactors.js +62 -0
- package/dist/tools/secondfactors.js.map +1 -0
- package/dist/tools/sessions.d.ts +6 -0
- package/dist/tools/sessions.js +300 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/transport/api.d.ts +35 -0
- package/dist/transport/api.js +327 -0
- package/dist/transport/api.js.map +1 -0
- package/dist/transport/interface.d.ts +50 -0
- package/dist/transport/interface.js +2 -0
- package/dist/transport/interface.js.map +1 -0
- package/dist/transport/k8s.d.ts +41 -0
- package/dist/transport/k8s.js +303 -0
- package/dist/transport/k8s.js.map +1 -0
- package/dist/transport/registry.d.ts +20 -0
- package/dist/transport/registry.js +91 -0
- package/dist/transport/registry.js.map +1 -0
- package/dist/transport/ssh.d.ts +37 -0
- package/dist/transport/ssh.js +353 -0
- package/dist/transport/ssh.js.map +1 -0
- package/docker-compose.test.yml +16 -0
- package/eslint.config.js +21 -0
- package/package.json +38 -0
- package/src/__tests__/api-transport.test.ts +746 -0
- package/src/__tests__/config.test.ts +587 -0
- package/src/__tests__/integration/api-mode.test.ts +229 -0
- package/src/__tests__/integration/oidc-rp.test.ts +138 -0
- package/src/__tests__/integration/ssh-mode.test.ts +113 -0
- package/src/__tests__/k8s-transport.test.ts +342 -0
- package/src/__tests__/oidc-tools.test.ts +554 -0
- package/src/__tests__/registry.test.ts +110 -0
- package/src/__tests__/ssh-transport.test.ts +805 -0
- package/src/__tests__/tools.test.ts +735 -0
- package/src/config.ts +605 -0
- package/src/index.ts +48 -0
- package/src/resources/documentation.ts +65 -0
- package/src/tools/cli-utilities.ts +207 -0
- package/src/tools/config.ts +382 -0
- package/src/tools/consents.ts +50 -0
- package/src/tools/instances.ts +21 -0
- package/src/tools/oidc-rp.ts +299 -0
- package/src/tools/oidc.ts +434 -0
- package/src/tools/secondfactors.ts +78 -0
- package/src/tools/sessions.ts +342 -0
- package/src/transport/api.ts +429 -0
- package/src/transport/interface.ts +58 -0
- package/src/transport/k8s.ts +367 -0
- package/src/transport/registry.ts +105 -0
- package/src/transport/ssh.ts +430 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +8 -0
- package/vitest.integration.config.ts +9 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { SshConfig, resolvePaths } from "../config.js";
|
|
3
|
+
import {
|
|
4
|
+
ILlngTransport,
|
|
5
|
+
SessionFilter,
|
|
6
|
+
ConfigInfo,
|
|
7
|
+
SessionGetOptions,
|
|
8
|
+
SessionDeleteOptions,
|
|
9
|
+
} from "./interface.js";
|
|
10
|
+
|
|
11
|
+
export class SshTransport implements ILlngTransport {
|
|
12
|
+
private paths: { cliPath: string; sessionsPath: string; configEditorPath: string };
|
|
13
|
+
|
|
14
|
+
constructor(private config: SshConfig) {
|
|
15
|
+
this.paths = resolvePaths(
|
|
16
|
+
config.binPrefix,
|
|
17
|
+
config.cliPath,
|
|
18
|
+
config.sessionsPath,
|
|
19
|
+
config.configEditorPath,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private async exec(args: string[], env?: Record<string, string>): Promise<string> {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let cmd: string;
|
|
26
|
+
let cmdArgs: string[];
|
|
27
|
+
|
|
28
|
+
if (this.config.host) {
|
|
29
|
+
// SSH mode
|
|
30
|
+
cmd = "ssh";
|
|
31
|
+
cmdArgs = [];
|
|
32
|
+
|
|
33
|
+
if (this.config.port) {
|
|
34
|
+
cmdArgs.push("-p", this.config.port.toString());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hostSpec = this.config.user
|
|
38
|
+
? `${this.config.user}@${this.config.host}`
|
|
39
|
+
: this.config.host;
|
|
40
|
+
cmdArgs.push(hostSpec);
|
|
41
|
+
|
|
42
|
+
// Build the remote command
|
|
43
|
+
let remoteCmd = args.map((arg) => this.shellQuote(arg)).join(" ");
|
|
44
|
+
|
|
45
|
+
// If env vars are provided, prefix with env command
|
|
46
|
+
if (env) {
|
|
47
|
+
const envPrefix = Object.entries(env)
|
|
48
|
+
.map(([k, v]) => `${k}=${this.shellQuote(v)}`)
|
|
49
|
+
.join(" ");
|
|
50
|
+
remoteCmd = `env ${envPrefix} ${remoteCmd}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Insert remoteCommand between sudo and the LLNG command
|
|
54
|
+
if (this.config.remoteCommand) {
|
|
55
|
+
remoteCmd = `${this.config.remoteCommand} ${remoteCmd}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (this.config.sudo) {
|
|
59
|
+
remoteCmd = `sudo -u ${this.shellQuote(this.config.sudo)} ${remoteCmd}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
cmdArgs.push(remoteCmd);
|
|
63
|
+
} else {
|
|
64
|
+
// Local mode
|
|
65
|
+
if (this.config.sudo) {
|
|
66
|
+
cmd = "sudo";
|
|
67
|
+
cmdArgs = ["-u", this.config.sudo];
|
|
68
|
+
if (this.config.remoteCommand) {
|
|
69
|
+
cmdArgs.push(...this.config.remoteCommand.split(" "), ...args);
|
|
70
|
+
} else {
|
|
71
|
+
cmdArgs.push(...args);
|
|
72
|
+
}
|
|
73
|
+
} else if (this.config.remoteCommand) {
|
|
74
|
+
const parts = this.config.remoteCommand.split(" ");
|
|
75
|
+
cmd = parts[0];
|
|
76
|
+
cmdArgs = [...parts.slice(1), ...args];
|
|
77
|
+
} else {
|
|
78
|
+
cmd = args[0];
|
|
79
|
+
cmdArgs = args.slice(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const spawnOpts = env ? { env: { ...process.env, ...env } } : undefined;
|
|
84
|
+
const proc = spawn(cmd, cmdArgs, spawnOpts);
|
|
85
|
+
let stdout = "";
|
|
86
|
+
|
|
87
|
+
proc.stdout.on("data", (data) => {
|
|
88
|
+
stdout += data.toString();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
proc.stderr.on("data", () => {
|
|
92
|
+
// stderr is consumed but not exposed to clients for security
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
proc.on("close", (code) => {
|
|
96
|
+
if (code !== 0) {
|
|
97
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
98
|
+
} else {
|
|
99
|
+
resolve(stdout);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
proc.on("error", () => {
|
|
104
|
+
reject(new Error("Command execution failed"));
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async execWithStdin(args: string[], input: string): Promise<string> {
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
let cmd: string;
|
|
112
|
+
let cmdArgs: string[];
|
|
113
|
+
|
|
114
|
+
if (this.config.host) {
|
|
115
|
+
// SSH mode
|
|
116
|
+
cmd = "ssh";
|
|
117
|
+
cmdArgs = [];
|
|
118
|
+
|
|
119
|
+
if (this.config.port) {
|
|
120
|
+
cmdArgs.push("-p", this.config.port.toString());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const hostSpec = this.config.user
|
|
124
|
+
? `${this.config.user}@${this.config.host}`
|
|
125
|
+
: this.config.host;
|
|
126
|
+
cmdArgs.push(hostSpec);
|
|
127
|
+
|
|
128
|
+
// Build the remote command
|
|
129
|
+
let remoteCmd = args.map((arg) => this.shellQuote(arg)).join(" ");
|
|
130
|
+
|
|
131
|
+
if (this.config.remoteCommand) {
|
|
132
|
+
remoteCmd = `${this.config.remoteCommand} ${remoteCmd}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.config.sudo) {
|
|
136
|
+
remoteCmd = `sudo -u ${this.shellQuote(this.config.sudo)} ${remoteCmd}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
cmdArgs.push(remoteCmd);
|
|
140
|
+
} else {
|
|
141
|
+
// Local mode
|
|
142
|
+
if (this.config.sudo) {
|
|
143
|
+
cmd = "sudo";
|
|
144
|
+
cmdArgs = ["-u", this.config.sudo];
|
|
145
|
+
if (this.config.remoteCommand) {
|
|
146
|
+
cmdArgs.push(...this.config.remoteCommand.split(" "), ...args);
|
|
147
|
+
} else {
|
|
148
|
+
cmdArgs.push(...args);
|
|
149
|
+
}
|
|
150
|
+
} else if (this.config.remoteCommand) {
|
|
151
|
+
const parts = this.config.remoteCommand.split(" ");
|
|
152
|
+
cmd = parts[0];
|
|
153
|
+
cmdArgs = [...parts.slice(1), ...args];
|
|
154
|
+
} else {
|
|
155
|
+
cmd = args[0];
|
|
156
|
+
cmdArgs = args.slice(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const proc = spawn(cmd, cmdArgs);
|
|
161
|
+
let stdout = "";
|
|
162
|
+
|
|
163
|
+
proc.stdout.on("data", (data) => {
|
|
164
|
+
stdout += data.toString();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
proc.stderr.on("data", () => {
|
|
168
|
+
// stderr is consumed but not exposed to clients for security
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
proc.on("close", (code) => {
|
|
172
|
+
if (code !== 0) {
|
|
173
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
174
|
+
} else {
|
|
175
|
+
resolve(stdout);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
proc.on("error", () => {
|
|
180
|
+
reject(new Error("Command execution failed"));
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Write input to stdin
|
|
184
|
+
proc.stdin.write(input);
|
|
185
|
+
proc.stdin.end();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private shellQuote(arg: string): string {
|
|
190
|
+
// Simple shell quoting - escape single quotes and wrap in single quotes
|
|
191
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private async execCli(subArgs: string[]): Promise<string> {
|
|
195
|
+
return this.exec([this.paths.cliPath, ...subArgs]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async execSessions(subArgs: string[]): Promise<string> {
|
|
199
|
+
return this.exec([this.paths.sessionsPath, ...subArgs]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private async execConfigEditor(subArgs: string[]): Promise<string> {
|
|
203
|
+
return this.exec([this.paths.configEditorPath, ...subArgs], { EDITOR: "cat" });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private pushSessionGetOptions(args: string[], options?: SessionGetOptions): void {
|
|
207
|
+
if (!options) return;
|
|
208
|
+
if (options.persistent) {
|
|
209
|
+
args.push("--persistent");
|
|
210
|
+
}
|
|
211
|
+
if (options.hash) {
|
|
212
|
+
args.push("--hash");
|
|
213
|
+
}
|
|
214
|
+
if (options.refreshTokens) {
|
|
215
|
+
args.push("--refresh-tokens");
|
|
216
|
+
}
|
|
217
|
+
if (options.backend) {
|
|
218
|
+
args.push("--backend", options.backend);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Config methods
|
|
223
|
+
async configInfo(): Promise<ConfigInfo> {
|
|
224
|
+
const output = await this.execCli(["info"]);
|
|
225
|
+
// Parse text output like "Num : 1\nAuthor : The LemonLDAP::NG team\n..."
|
|
226
|
+
const lines = output.trim().split("\n");
|
|
227
|
+
const data: Record<string, string> = {};
|
|
228
|
+
for (const line of lines) {
|
|
229
|
+
const match = line.match(/^(\S+)\s*:\s*(.*)$/);
|
|
230
|
+
if (match) {
|
|
231
|
+
data[match[1]] = match[2];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
cfgNum: parseInt(data["Num"] || "0", 10),
|
|
236
|
+
cfgAuthor: data["Author"] || "",
|
|
237
|
+
cfgDate: data["Date"] || "",
|
|
238
|
+
cfgLog: data["Log"],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async configGet(keys: string[]): Promise<Record<string, any>> {
|
|
243
|
+
const output = await this.execCli(["-json", "get", ...keys]);
|
|
244
|
+
return JSON.parse(output);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async configSet(pairs: Record<string, any>, log?: string): Promise<void> {
|
|
248
|
+
const args: string[] = ["set", "-yes", "1"];
|
|
249
|
+
|
|
250
|
+
for (const [key, value] of Object.entries(pairs)) {
|
|
251
|
+
args.push(key, String(value));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (log) {
|
|
255
|
+
args.push("-log", log);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await this.execCli(args);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async configAddKey(key: string, subkey: string, value: string): Promise<void> {
|
|
262
|
+
await this.execCli(["addKey", key, subkey, value]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async configDelKey(key: string, subkey: string): Promise<void> {
|
|
266
|
+
await this.execCli(["delKey", key, subkey]);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async configSave(): Promise<string> {
|
|
270
|
+
return await this.execCli(["save"]);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async configRestore(json: string): Promise<void> {
|
|
274
|
+
await this.execWithStdin([this.paths.cliPath, "restore", "-yes", "1", "-"], json);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async configMerge(json: string): Promise<void> {
|
|
278
|
+
await this.execWithStdin([this.paths.cliPath, "merge", "-yes", "1", "-"], json);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async configRollback(): Promise<void> {
|
|
282
|
+
await this.execCli(["rollback", "-yes", "1"]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async configUpdateCache(): Promise<void> {
|
|
286
|
+
await this.execCli(["update-cache"]);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async configTestEmail(destination: string): Promise<void> {
|
|
290
|
+
await this.execCli(["test-email", destination]);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Session methods
|
|
294
|
+
async sessionGet(id: string, options?: SessionGetOptions): Promise<Record<string, any>> {
|
|
295
|
+
const args = ["get", id];
|
|
296
|
+
this.pushSessionGetOptions(args, options);
|
|
297
|
+
|
|
298
|
+
const output = await this.execSessions(args);
|
|
299
|
+
return JSON.parse(output);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async sessionSearch(filters: SessionFilter): Promise<any[]> {
|
|
303
|
+
const args: string[] = ["search"];
|
|
304
|
+
|
|
305
|
+
if (filters.where) {
|
|
306
|
+
for (const [field, value] of Object.entries(filters.where)) {
|
|
307
|
+
args.push("--where", `${field}=${value}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (filters.select && filters.select.length > 0) {
|
|
312
|
+
args.push("--select", filters.select.join(","));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (filters.backend) {
|
|
316
|
+
args.push("--backend", filters.backend);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (filters.count) {
|
|
320
|
+
args.push("--count");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (filters.refreshTokens) {
|
|
324
|
+
args.push("--refresh-tokens");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (filters.persistent) {
|
|
328
|
+
args.push("--persistent");
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (filters.hash) {
|
|
332
|
+
args.push("--hash");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (filters.idOnly) {
|
|
336
|
+
args.push("--id-only");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const output = await this.execSessions(args);
|
|
340
|
+
return JSON.parse(output);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async sessionDelete(ids: string[], options?: SessionDeleteOptions): Promise<void> {
|
|
344
|
+
if (options?.where) {
|
|
345
|
+
// Where-based deletion uses lemonldap-ng-sessions delete
|
|
346
|
+
const args: string[] = ["delete"];
|
|
347
|
+
for (const [field, value] of Object.entries(options.where)) {
|
|
348
|
+
args.push("--where", `${field}=${value}`);
|
|
349
|
+
}
|
|
350
|
+
this.pushSessionGetOptions(args, options);
|
|
351
|
+
await this.execSessions(args);
|
|
352
|
+
} else {
|
|
353
|
+
// ID-based deletion uses llngDeleteSession
|
|
354
|
+
const deleteScriptPath =
|
|
355
|
+
this.config.deleteSessionPath ||
|
|
356
|
+
this.paths.cliPath.replace("lemonldap-ng-cli", "llngDeleteSession");
|
|
357
|
+
for (const id of ids) {
|
|
358
|
+
const args = [deleteScriptPath, id];
|
|
359
|
+
this.pushSessionGetOptions(args, options);
|
|
360
|
+
await this.exec(args);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async sessionSetKey(
|
|
366
|
+
id: string,
|
|
367
|
+
pairs: Record<string, any>,
|
|
368
|
+
options?: SessionGetOptions,
|
|
369
|
+
): Promise<void> {
|
|
370
|
+
const args: string[] = ["setKey", id];
|
|
371
|
+
for (const [key, value] of Object.entries(pairs)) {
|
|
372
|
+
args.push(key, String(value));
|
|
373
|
+
}
|
|
374
|
+
this.pushSessionGetOptions(args, options);
|
|
375
|
+
await this.execSessions(args);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async sessionDelKey(id: string, keys: string[], options?: SessionGetOptions): Promise<void> {
|
|
379
|
+
const args: string[] = ["delKey", id, ...keys];
|
|
380
|
+
this.pushSessionGetOptions(args, options);
|
|
381
|
+
await this.execSessions(args);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async sessionBackup(
|
|
385
|
+
backend?: string,
|
|
386
|
+
refreshTokens?: boolean,
|
|
387
|
+
persistent?: boolean,
|
|
388
|
+
): Promise<string> {
|
|
389
|
+
// Return all sessions as JSON via search with no filters
|
|
390
|
+
const args = ["search"];
|
|
391
|
+
if (backend) {
|
|
392
|
+
args.push("--backend", backend);
|
|
393
|
+
}
|
|
394
|
+
if (refreshTokens) {
|
|
395
|
+
args.push("--refresh-tokens");
|
|
396
|
+
}
|
|
397
|
+
if (persistent) {
|
|
398
|
+
args.push("--persistent");
|
|
399
|
+
}
|
|
400
|
+
const output = await this.execSessions(args);
|
|
401
|
+
return output;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 2FA methods
|
|
405
|
+
async secondFactorsGet(_user: string): Promise<any[]> {
|
|
406
|
+
throw new Error("secondFactorsGet is not supported via CLI. Use API mode.");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async secondFactorsDelete(_user: string, _ids: string[]): Promise<void> {
|
|
410
|
+
throw new Error("secondFactorsDelete is not supported via CLI. Use API mode.");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async secondFactorsDelType(_user: string, _type: string): Promise<void> {
|
|
414
|
+
throw new Error("secondFactorsDelType is not supported via CLI. Use API mode.");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Consents methods
|
|
418
|
+
async consentsGet(_user: string): Promise<any[]> {
|
|
419
|
+
throw new Error("consentsGet is not supported via CLI. Use API mode.");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async consentsDelete(_user: string, _ids: string[]): Promise<void> {
|
|
423
|
+
throw new Error("consentsDelete is not supported via CLI. Use API mode.");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async execScript(scriptName: string, args: string[]): Promise<string> {
|
|
427
|
+
const prefix = this.config.binPrefix || "/usr/share/lemonldap-ng/bin";
|
|
428
|
+
return this.exec([`${prefix}/${scriptName}`, ...args]);
|
|
429
|
+
}
|
|
430
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"sourceMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"]
|
|
16
|
+
}
|
package/vitest.config.ts
ADDED