@undefineds.co/xpod 0.3.15 → 0.3.16
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/config/local.json +5 -5
- package/config/xpod.json +24 -10
- package/dist/cli/commands/auth.d.ts +1 -0
- package/dist/cli/commands/auth.js +117 -37
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/login.js +16 -23
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +2 -0
- package/dist/cli/commands/logs.js +20 -5
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/obj.d.ts +44 -0
- package/dist/cli/commands/obj.js +1059 -0
- package/dist/cli/commands/obj.js.map +1 -0
- package/dist/cli/commands/rdf.d.ts +14 -0
- package/dist/cli/commands/rdf.js +235 -0
- package/dist/cli/commands/rdf.js.map +1 -0
- package/dist/cli/commands/resource.d.ts +31 -0
- package/dist/cli/commands/resource.js +191 -0
- package/dist/cli/commands/resource.js.map +1 -0
- package/dist/cli/commands/secret.d.ts +36 -0
- package/dist/cli/commands/secret.js +285 -0
- package/dist/cli/commands/secret.js.map +1 -0
- package/dist/cli/commands/server.d.ts +11 -0
- package/dist/cli/commands/server.js +168 -0
- package/dist/cli/commands/server.js.map +1 -0
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.js +5 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +21 -6
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.js +40 -6
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/index.js +23 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/auth-context.d.ts +24 -0
- package/dist/cli/lib/auth-context.js +70 -0
- package/dist/cli/lib/auth-context.js.map +1 -0
- package/dist/cli/lib/output.d.ts +23 -0
- package/dist/cli/lib/output.js +63 -0
- package/dist/cli/lib/output.js.map +1 -0
- package/dist/cli/lib/resource.d.ts +29 -0
- package/dist/cli/lib/resource.js +114 -0
- package/dist/cli/lib/resource.js.map +1 -0
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.d.ts +11 -10
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +13 -24
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld +4 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.d.ts +8 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.js +10 -6
- package/dist/identity/oidc/AutoDetectOidcHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectOidcHandler.jsonld +3 -3
- package/dist/storage/accessors/MixDataAccessor.js +3 -0
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.d.ts +26 -1
- package/dist/storage/quint/SqliteQuintStore.js +551 -318
- package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.jsonld +102 -2
- package/dist/storage/quint/schema.d.ts +76 -0
- package/dist/storage/quint/schema.js +13 -7
- package/dist/storage/quint/schema.js.map +1 -1
- package/dist/storage/sparql/ComunicaQuintEngine.js +16 -3
- package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/config.d.ts +0 -42
- package/dist/cli/commands/config.js +0 -289
- package/dist/cli/commands/config.js.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeBaseUrl = normalizeBaseUrl;
|
|
4
|
+
exports.resolvePodRootFromWebId = resolvePodRootFromWebId;
|
|
5
|
+
exports.getStoredAuthStatus = getStoredAuthStatus;
|
|
6
|
+
exports.requireAuthContext = requireAuthContext;
|
|
7
|
+
exports.authFetch = authFetch;
|
|
8
|
+
const solid_auth_1 = require("./solid-auth");
|
|
9
|
+
const credentials_store_1 = require("./credentials-store");
|
|
10
|
+
const output_1 = require("./output");
|
|
11
|
+
function normalizeBaseUrl(url) {
|
|
12
|
+
return url.endsWith('/') ? url : `${url}/`;
|
|
13
|
+
}
|
|
14
|
+
function resolvePodRootFromWebId(webId) {
|
|
15
|
+
const webIdUrl = new URL(webId);
|
|
16
|
+
const path = webIdUrl.pathname;
|
|
17
|
+
const profileSuffix = '/profile/card';
|
|
18
|
+
if (path.endsWith(profileSuffix)) {
|
|
19
|
+
const podPath = path.slice(0, -profileSuffix.length);
|
|
20
|
+
return `${webIdUrl.origin}${podPath.endsWith('/') ? podPath : `${podPath}/`}`;
|
|
21
|
+
}
|
|
22
|
+
const pathParts = path.split('/').filter(Boolean);
|
|
23
|
+
if (pathParts.length > 0) {
|
|
24
|
+
return `${webIdUrl.origin}/${pathParts[0]}/`;
|
|
25
|
+
}
|
|
26
|
+
return `${webIdUrl.origin}/`;
|
|
27
|
+
}
|
|
28
|
+
function getStoredAuthStatus(urlOverride) {
|
|
29
|
+
const credentials = (0, credentials_store_1.loadCredentials)();
|
|
30
|
+
if (!credentials) {
|
|
31
|
+
return { authenticated: false };
|
|
32
|
+
}
|
|
33
|
+
const baseUrl = normalizeBaseUrl(urlOverride ?? credentials.url);
|
|
34
|
+
const podRoot = resolvePodRootFromWebId(credentials.webId);
|
|
35
|
+
return {
|
|
36
|
+
authenticated: true,
|
|
37
|
+
authType: credentials.authType,
|
|
38
|
+
baseUrl,
|
|
39
|
+
webId: credentials.webId,
|
|
40
|
+
podRoot,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function requireAuthContext(options = {}) {
|
|
44
|
+
const credentials = (0, credentials_store_1.loadCredentials)();
|
|
45
|
+
if (!credentials) {
|
|
46
|
+
throw new output_1.CliCommandError('auth_required', 'No credentials found. Run `xpod auth login` first.', 2);
|
|
47
|
+
}
|
|
48
|
+
const clientCredentials = (0, credentials_store_1.getClientCredentials)(credentials);
|
|
49
|
+
if (!clientCredentials) {
|
|
50
|
+
throw new output_1.CliCommandError('auth_unsupported', 'Stored OAuth credentials are not supported for CLI resource operations yet. Run `xpod auth login` to create client credentials.', 2);
|
|
51
|
+
}
|
|
52
|
+
const baseUrl = normalizeBaseUrl(options.url ?? credentials.url);
|
|
53
|
+
const tokenResult = await (0, solid_auth_1.getAccessToken)(clientCredentials.clientId, clientCredentials.clientSecret, baseUrl);
|
|
54
|
+
if (!tokenResult) {
|
|
55
|
+
throw new output_1.CliCommandError('auth_failed', 'Failed to obtain an access token. Run `xpod auth login` again.', 2);
|
|
56
|
+
}
|
|
57
|
+
const podRoot = resolvePodRootFromWebId(credentials.webId);
|
|
58
|
+
return {
|
|
59
|
+
baseUrl,
|
|
60
|
+
webId: credentials.webId,
|
|
61
|
+
podRoot,
|
|
62
|
+
baseIri: podRoot,
|
|
63
|
+
accessToken: tokenResult.accessToken,
|
|
64
|
+
credentials,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function authFetch(context, url, init) {
|
|
68
|
+
return (0, solid_auth_1.authenticatedFetch)(url, context.accessToken, init);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=auth-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../../src/cli/lib/auth-context.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAccessToken, authenticatedFetch } from './solid-auth';\nimport {\n getClientCredentials,\n loadCredentials,\n type StoredCredentials,\n} from './credentials-store';\nimport { CliCommandError } from './output';\n\nexport interface CliAuthContext {\n baseUrl: string;\n webId: string;\n podRoot: string;\n baseIri: string;\n accessToken: string;\n credentials: StoredCredentials;\n}\n\nexport interface AuthStatus {\n authenticated: boolean;\n authType?: string;\n baseUrl?: string;\n webId?: string;\n podRoot?: string;\n}\n\nexport function normalizeBaseUrl(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nexport function resolvePodRootFromWebId(webId: string): string {\n const webIdUrl = new URL(webId);\n const path = webIdUrl.pathname;\n const profileSuffix = '/profile/card';\n if (path.endsWith(profileSuffix)) {\n const podPath = path.slice(0, -profileSuffix.length);\n return `${webIdUrl.origin}${podPath.endsWith('/') ? podPath : `${podPath}/`}`;\n }\n\n const pathParts = path.split('/').filter(Boolean);\n if (pathParts.length > 0) {\n return `${webIdUrl.origin}/${pathParts[0]}/`;\n }\n return `${webIdUrl.origin}/`;\n}\n\nexport function getStoredAuthStatus(urlOverride?: string): AuthStatus {\n const credentials = loadCredentials();\n if (!credentials) {\n return { authenticated: false };\n }\n\n const baseUrl = normalizeBaseUrl(urlOverride ?? credentials.url);\n const podRoot = resolvePodRootFromWebId(credentials.webId);\n return {\n authenticated: true,\n authType: credentials.authType,\n baseUrl,\n webId: credentials.webId,\n podRoot,\n };\n}\n\nexport async function requireAuthContext(options: {\n url?: string;\n json?: boolean;\n} = {}): Promise<CliAuthContext> {\n const credentials = loadCredentials();\n if (!credentials) {\n throw new CliCommandError(\n 'auth_required',\n 'No credentials found. Run `xpod auth login` first.',\n 2,\n );\n }\n\n const clientCredentials = getClientCredentials(credentials);\n if (!clientCredentials) {\n throw new CliCommandError(\n 'auth_unsupported',\n 'Stored OAuth credentials are not supported for CLI resource operations yet. Run `xpod auth login` to create client credentials.',\n 2,\n );\n }\n\n const baseUrl = normalizeBaseUrl(options.url ?? credentials.url);\n const tokenResult = await getAccessToken(\n clientCredentials.clientId,\n clientCredentials.clientSecret,\n baseUrl,\n );\n if (!tokenResult) {\n throw new CliCommandError(\n 'auth_failed',\n 'Failed to obtain an access token. Run `xpod auth login` again.',\n 2,\n );\n }\n\n const podRoot = resolvePodRootFromWebId(credentials.webId);\n return {\n baseUrl,\n webId: credentials.webId,\n podRoot,\n baseIri: podRoot,\n accessToken: tokenResult.accessToken,\n credentials,\n };\n}\n\nexport async function authFetch(\n context: CliAuthContext,\n url: string,\n init?: RequestInit,\n): Promise<Response> {\n return authenticatedFetch(url, context.accessToken, init);\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface CliEnvelope<T = unknown> {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
code: string;
|
|
4
|
+
data?: T;
|
|
5
|
+
message?: string;
|
|
6
|
+
warnings: string[];
|
|
7
|
+
items?: unknown[];
|
|
8
|
+
}
|
|
9
|
+
export declare class CliCommandError extends Error {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly exitCode: number;
|
|
12
|
+
readonly data?: unknown;
|
|
13
|
+
constructor(code: string, message: string, exitCode?: number, data?: unknown);
|
|
14
|
+
}
|
|
15
|
+
export declare function ok<T>(data: T, code?: string, warnings?: string[]): CliEnvelope<T>;
|
|
16
|
+
export declare function fail(code: string, message: string, warnings?: string[], data?: unknown): CliEnvelope;
|
|
17
|
+
export declare function writeJson(result: CliEnvelope): void;
|
|
18
|
+
export declare function writeJsonItems(items: unknown[], code: string, warnings?: string[]): void;
|
|
19
|
+
export declare function writeJsonResult<T>(data: T, code?: string, warnings?: string[]): void;
|
|
20
|
+
export declare function isJsonMode(argv: {
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}): boolean;
|
|
23
|
+
export declare function handleCliError(error: unknown, json: boolean, fallbackCode?: string): never;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CliCommandError = void 0;
|
|
4
|
+
exports.ok = ok;
|
|
5
|
+
exports.fail = fail;
|
|
6
|
+
exports.writeJson = writeJson;
|
|
7
|
+
exports.writeJsonItems = writeJsonItems;
|
|
8
|
+
exports.writeJsonResult = writeJsonResult;
|
|
9
|
+
exports.isJsonMode = isJsonMode;
|
|
10
|
+
exports.handleCliError = handleCliError;
|
|
11
|
+
class CliCommandError extends Error {
|
|
12
|
+
constructor(code, message, exitCode = 1, data) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'CliCommandError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.exitCode = exitCode;
|
|
17
|
+
this.data = data;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CliCommandError = CliCommandError;
|
|
21
|
+
function ok(data, code = 'ok', warnings = []) {
|
|
22
|
+
return { ok: true, code, data, warnings };
|
|
23
|
+
}
|
|
24
|
+
function fail(code, message, warnings = [], data) {
|
|
25
|
+
return { ok: false, code, message, warnings, ...(data === undefined ? {} : { data }) };
|
|
26
|
+
}
|
|
27
|
+
function writeJson(result) {
|
|
28
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29
|
+
}
|
|
30
|
+
function writeJsonItems(items, code, warnings = []) {
|
|
31
|
+
const ok = items.every((item) => {
|
|
32
|
+
if (!item || typeof item !== 'object' || !('ok' in item)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return item.ok === true;
|
|
36
|
+
});
|
|
37
|
+
writeJson({ ok, code, items, warnings });
|
|
38
|
+
}
|
|
39
|
+
function writeJsonResult(data, code = 'ok', warnings = []) {
|
|
40
|
+
writeJson(ok(data, code, warnings));
|
|
41
|
+
}
|
|
42
|
+
function isJsonMode(argv) {
|
|
43
|
+
return argv.json === true;
|
|
44
|
+
}
|
|
45
|
+
function shouldForceStructuredError(error) {
|
|
46
|
+
return error instanceof CliCommandError &&
|
|
47
|
+
error.code === 'auth_required' &&
|
|
48
|
+
(process.env.CI === 'true' || !process.stdout.isTTY);
|
|
49
|
+
}
|
|
50
|
+
function handleCliError(error, json, fallbackCode = 'error') {
|
|
51
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
52
|
+
if (json || shouldForceStructuredError(err)) {
|
|
53
|
+
if (err instanceof CliCommandError) {
|
|
54
|
+
writeJson(fail(err.code, err.message, [], err.data));
|
|
55
|
+
process.exit(err.exitCode);
|
|
56
|
+
}
|
|
57
|
+
writeJson(fail(fallbackCode, err.message));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
console.error(err.message);
|
|
61
|
+
process.exit(err instanceof CliCommandError ? err.exitCode : 1);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/cli/lib/output.ts"],"names":[],"mappings":"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","sourcesContent":["export interface CliEnvelope<T = unknown> {\n ok: boolean;\n code: string;\n data?: T;\n message?: string;\n warnings: string[];\n items?: unknown[];\n}\n\nexport class CliCommandError extends Error {\n public readonly code: string;\n public readonly exitCode: number;\n public readonly data?: unknown;\n\n public constructor(code: string, message: string, exitCode = 1, data?: unknown) {\n super(message);\n this.name = 'CliCommandError';\n this.code = code;\n this.exitCode = exitCode;\n this.data = data;\n }\n}\n\nexport function ok<T>(data: T, code = 'ok', warnings: string[] = []): CliEnvelope<T> {\n return { ok: true, code, data, warnings };\n}\n\nexport function fail(code: string, message: string, warnings: string[] = [], data?: unknown): CliEnvelope {\n return { ok: false, code, message, warnings, ...(data === undefined ? {} : { data }) };\n}\n\nexport function writeJson(result: CliEnvelope): void {\n console.log(JSON.stringify(result, null, 2));\n}\n\nexport function writeJsonItems(items: unknown[], code: string, warnings: string[] = []): void {\n const ok = items.every((item) => {\n if (!item || typeof item !== 'object' || !('ok' in item)) {\n return false;\n }\n return (item as { ok?: unknown }).ok === true;\n });\n writeJson({ ok, code, items, warnings });\n}\n\nexport function writeJsonResult<T>(data: T, code = 'ok', warnings: string[] = []): void {\n writeJson(ok(data, code, warnings));\n}\n\nexport function isJsonMode(argv: { json?: boolean }): boolean {\n return argv.json === true;\n}\n\nfunction shouldForceStructuredError(error: Error): boolean {\n return error instanceof CliCommandError &&\n error.code === 'auth_required' &&\n (process.env.CI === 'true' || !process.stdout.isTTY);\n}\n\nexport function handleCliError(error: unknown, json: boolean, fallbackCode = 'error'): never {\n const err = error instanceof Error ? error : new Error(String(error));\n if (json || shouldForceStructuredError(err)) {\n if (err instanceof CliCommandError) {\n writeJson(fail(err.code, err.message, [], err.data));\n process.exit(err.exitCode);\n }\n writeJson(fail(fallbackCode, err.message));\n process.exit(1);\n }\n\n console.error(err.message);\n process.exit(err instanceof CliCommandError ? err.exitCode : 1);\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CliAuthContext } from './auth-context';
|
|
2
|
+
export interface ResourceTarget {
|
|
3
|
+
input: string;
|
|
4
|
+
resourceUrl: string;
|
|
5
|
+
webId: string;
|
|
6
|
+
podRoot: string;
|
|
7
|
+
baseIri: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResourceResponseData {
|
|
10
|
+
webId: string;
|
|
11
|
+
podRoot: string;
|
|
12
|
+
baseIri: string;
|
|
13
|
+
resourceUrl: string;
|
|
14
|
+
status: number;
|
|
15
|
+
statusText: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveResourceTarget(context: CliAuthContext, input: string): ResourceTarget;
|
|
19
|
+
export declare function responseHeaders(response: Response): Record<string, string>;
|
|
20
|
+
export declare function responseData(target: ResourceTarget, response: Response): ResourceResponseData;
|
|
21
|
+
export declare function ensureOk(response: Response, code: string, message: string): void;
|
|
22
|
+
export declare function contentTypeForPath(filePath: string): string;
|
|
23
|
+
export declare function readBodyFile(filePath: string): {
|
|
24
|
+
body: Buffer;
|
|
25
|
+
contentType: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function fetchResource(context: CliAuthContext, target: ResourceTarget, init: RequestInit): Promise<Response>;
|
|
28
|
+
export declare function parseContainedResources(turtle: string, containerUrl: string): string[];
|
|
29
|
+
export declare function relativeToPodRoot(resourceUrl: string, podRoot: string): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveResourceTarget = resolveResourceTarget;
|
|
4
|
+
exports.responseHeaders = responseHeaders;
|
|
5
|
+
exports.responseData = responseData;
|
|
6
|
+
exports.ensureOk = ensureOk;
|
|
7
|
+
exports.contentTypeForPath = contentTypeForPath;
|
|
8
|
+
exports.readBodyFile = readBodyFile;
|
|
9
|
+
exports.fetchResource = fetchResource;
|
|
10
|
+
exports.parseContainedResources = parseContainedResources;
|
|
11
|
+
exports.relativeToPodRoot = relativeToPodRoot;
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const auth_context_1 = require("./auth-context");
|
|
15
|
+
const output_1 = require("./output");
|
|
16
|
+
function resolveResourceTarget(context, input) {
|
|
17
|
+
const trimmed = input.trim();
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
throw new output_1.CliCommandError('invalid_path', 'Resource path is required.');
|
|
20
|
+
}
|
|
21
|
+
const resourceUrl = /^https?:\/\//i.test(trimmed)
|
|
22
|
+
? new URL(trimmed).toString()
|
|
23
|
+
: new URL(trimmed.replace(/^\/+/, ''), context.podRoot).toString();
|
|
24
|
+
return {
|
|
25
|
+
input,
|
|
26
|
+
resourceUrl,
|
|
27
|
+
webId: context.webId,
|
|
28
|
+
podRoot: context.podRoot,
|
|
29
|
+
baseIri: context.baseIri,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function responseHeaders(response) {
|
|
33
|
+
const headers = {};
|
|
34
|
+
response.headers.forEach((value, key) => {
|
|
35
|
+
const lower = key.toLowerCase();
|
|
36
|
+
if (lower === 'authorization' || lower === 'cookie' || lower === 'set-cookie') {
|
|
37
|
+
headers[key] = '[redacted]';
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
headers[key] = value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return headers;
|
|
44
|
+
}
|
|
45
|
+
function responseData(target, response) {
|
|
46
|
+
return {
|
|
47
|
+
webId: target.webId,
|
|
48
|
+
podRoot: target.podRoot,
|
|
49
|
+
baseIri: target.baseIri,
|
|
50
|
+
resourceUrl: target.resourceUrl,
|
|
51
|
+
status: response.status,
|
|
52
|
+
statusText: response.statusText,
|
|
53
|
+
headers: responseHeaders(response),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function ensureOk(response, code, message) {
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new output_1.CliCommandError(code, `${message}: HTTP ${response.status} ${response.statusText}`, 1, {
|
|
59
|
+
status: response.status,
|
|
60
|
+
statusText: response.statusText,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function contentTypeForPath(filePath) {
|
|
65
|
+
switch ((0, path_1.extname)(filePath).toLowerCase()) {
|
|
66
|
+
case '.ttl':
|
|
67
|
+
return 'text/turtle';
|
|
68
|
+
case '.json':
|
|
69
|
+
case '.jsonld':
|
|
70
|
+
return 'application/ld+json';
|
|
71
|
+
case '.txt':
|
|
72
|
+
case '.md':
|
|
73
|
+
return 'text/plain';
|
|
74
|
+
case '.html':
|
|
75
|
+
return 'text/html';
|
|
76
|
+
case '.png':
|
|
77
|
+
return 'image/png';
|
|
78
|
+
case '.jpg':
|
|
79
|
+
case '.jpeg':
|
|
80
|
+
return 'image/jpeg';
|
|
81
|
+
default:
|
|
82
|
+
return 'application/octet-stream';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function readBodyFile(filePath) {
|
|
86
|
+
return {
|
|
87
|
+
body: (0, fs_1.readFileSync)(filePath),
|
|
88
|
+
contentType: contentTypeForPath(filePath),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function fetchResource(context, target, init) {
|
|
92
|
+
return (0, auth_context_1.authFetch)(context, target.resourceUrl, init);
|
|
93
|
+
}
|
|
94
|
+
function parseContainedResources(turtle, containerUrl) {
|
|
95
|
+
const resources = new Set();
|
|
96
|
+
const containsPattern = /(?:ldp:contains|<http:\/\/www\.w3\.org\/ns\/ldp#contains>)\s+((?:<[^>]+>\s*,?\s*)+)/g;
|
|
97
|
+
let match;
|
|
98
|
+
while ((match = containsPattern.exec(turtle)) !== null) {
|
|
99
|
+
const block = match[1] ?? '';
|
|
100
|
+
const iriPattern = /<([^>]+)>/g;
|
|
101
|
+
let iriMatch;
|
|
102
|
+
while ((iriMatch = iriPattern.exec(block)) !== null) {
|
|
103
|
+
const iri = iriMatch[1];
|
|
104
|
+
if (iri && iri !== containerUrl) {
|
|
105
|
+
resources.add(new URL(iri, containerUrl).toString());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Array.from(resources).sort();
|
|
110
|
+
}
|
|
111
|
+
function relativeToPodRoot(resourceUrl, podRoot) {
|
|
112
|
+
return resourceUrl.startsWith(podRoot) ? resourceUrl.slice(podRoot.length) : resourceUrl;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../../src/cli/lib/resource.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from 'fs';\nimport { extname } from 'path';\nimport type { CliAuthContext } from './auth-context';\nimport { authFetch } from './auth-context';\nimport { CliCommandError } from './output';\n\nexport interface ResourceTarget {\n input: string;\n resourceUrl: string;\n webId: string;\n podRoot: string;\n baseIri: string;\n}\n\nexport interface ResourceResponseData {\n webId: string;\n podRoot: string;\n baseIri: string;\n resourceUrl: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport function resolveResourceTarget(context: CliAuthContext, input: string): ResourceTarget {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new CliCommandError('invalid_path', 'Resource path is required.');\n }\n\n const resourceUrl = /^https?:\\/\\//i.test(trimmed)\n ? new URL(trimmed).toString()\n : new URL(trimmed.replace(/^\\/+/, ''), context.podRoot).toString();\n\n return {\n input,\n resourceUrl,\n webId: context.webId,\n podRoot: context.podRoot,\n baseIri: context.baseIri,\n };\n}\n\nexport function responseHeaders(response: Response): Record<string, string> {\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower === 'authorization' || lower === 'cookie' || lower === 'set-cookie') {\n headers[key] = '[redacted]';\n } else {\n headers[key] = value;\n }\n });\n return headers;\n}\n\nexport function responseData(target: ResourceTarget, response: Response): ResourceResponseData {\n return {\n webId: target.webId,\n podRoot: target.podRoot,\n baseIri: target.baseIri,\n resourceUrl: target.resourceUrl,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders(response),\n };\n}\n\nexport function ensureOk(response: Response, code: string, message: string): void {\n if (!response.ok) {\n throw new CliCommandError(code, `${message}: HTTP ${response.status} ${response.statusText}`, 1, {\n status: response.status,\n statusText: response.statusText,\n });\n }\n}\n\nexport function contentTypeForPath(filePath: string): string {\n switch (extname(filePath).toLowerCase()) {\n case '.ttl':\n return 'text/turtle';\n case '.json':\n case '.jsonld':\n return 'application/ld+json';\n case '.txt':\n case '.md':\n return 'text/plain';\n case '.html':\n return 'text/html';\n case '.png':\n return 'image/png';\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg';\n default:\n return 'application/octet-stream';\n }\n}\n\nexport function readBodyFile(filePath: string): { body: Buffer; contentType: string } {\n return {\n body: readFileSync(filePath),\n contentType: contentTypeForPath(filePath),\n };\n}\n\nexport async function fetchResource(\n context: CliAuthContext,\n target: ResourceTarget,\n init: RequestInit,\n): Promise<Response> {\n return authFetch(context, target.resourceUrl, init);\n}\n\nexport function parseContainedResources(turtle: string, containerUrl: string): string[] {\n const resources = new Set<string>();\n const containsPattern = /(?:ldp:contains|<http:\\/\\/www\\.w3\\.org\\/ns\\/ldp#contains>)\\s+((?:<[^>]+>\\s*,?\\s*)+)/g;\n let match: RegExpExecArray | null;\n while ((match = containsPattern.exec(turtle)) !== null) {\n const block = match[1] ?? '';\n const iriPattern = /<([^>]+)>/g;\n let iriMatch: RegExpExecArray | null;\n while ((iriMatch = iriPattern.exec(block)) !== null) {\n const iri = iriMatch[1];\n if (iri && iri !== containerUrl) {\n resources.add(new URL(iri, containerUrl).toString());\n }\n }\n }\n return Array.from(resources).sort();\n}\n\nexport function relativeToPodRoot(resourceUrl: string, podRoot: string): string {\n return resourceUrl.startsWith(podRoot) ? resourceUrl.slice(podRoot.length) : resourceUrl;\n}\n"]}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { HttpHandler, type HttpHandlerInput } from '@solid/community-server';
|
|
2
2
|
export interface AutoDetectIdentityProviderHandlerOptions {
|
|
3
|
-
/** 外部 IdP 的基础 URL,如果提供则启用 SP
|
|
3
|
+
/** 外部 IdP 的基础 URL,如果提供则启用 Local SP mode. */
|
|
4
4
|
oidcIssuer?: string;
|
|
5
|
-
/**
|
|
5
|
+
/** Message used when no source handler is available. */
|
|
6
6
|
message?: string;
|
|
7
|
-
/** CSS
|
|
7
|
+
/** CSS IdentityProviderHandler that owns account, consent and WebID selection routes. */
|
|
8
8
|
source?: HttpHandler;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Auto-detect Identity Provider Handler
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Local SP mode still needs the local `/.account/*` surface: CSS keeps the
|
|
14
|
+
* OIDC interaction and the scoped WebID picker here, while token validation can
|
|
15
|
+
* trust the configured external issuer. Disabling this surface makes LinX fall
|
|
16
|
+
* back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.
|
|
16
17
|
*/
|
|
17
18
|
export declare class AutoDetectIdentityProviderHandler extends HttpHandler {
|
|
18
19
|
private readonly logger;
|
|
@@ -22,14 +23,14 @@ export declare class AutoDetectIdentityProviderHandler extends HttpHandler {
|
|
|
22
23
|
constructor(options?: AutoDetectIdentityProviderHandlerOptions);
|
|
23
24
|
/**
|
|
24
25
|
* 判断是否处理请求
|
|
25
|
-
* - SP
|
|
26
|
-
* -
|
|
26
|
+
* - Local SP mode: delegate local account/consent routes to source Handler
|
|
27
|
+
* - Standard mode: delegate to source Handler
|
|
27
28
|
*/
|
|
28
29
|
canHandle(input: HttpHandlerInput): Promise<void>;
|
|
29
30
|
/**
|
|
30
31
|
* 处理请求
|
|
31
|
-
* - SP
|
|
32
|
-
* -
|
|
32
|
+
* - Local SP mode: delegate to source Handler so consent remains scoped by SP
|
|
33
|
+
* - Standard mode: delegate to source Handler
|
|
33
34
|
*/
|
|
34
35
|
handle(input: HttpHandlerInput): Promise<void>;
|
|
35
36
|
/**
|
|
@@ -6,28 +6,29 @@ const community_server_1 = require("@solid/community-server");
|
|
|
6
6
|
/**
|
|
7
7
|
* Auto-detect Identity Provider Handler
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Local SP mode still needs the local `/.account/*` surface: CSS keeps the
|
|
10
|
+
* OIDC interaction and the scoped WebID picker here, while token validation can
|
|
11
|
+
* trust the configured external issuer. Disabling this surface makes LinX fall
|
|
12
|
+
* back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.
|
|
12
13
|
*/
|
|
13
14
|
class AutoDetectIdentityProviderHandler extends community_server_1.HttpHandler {
|
|
14
15
|
constructor(options = {}) {
|
|
15
16
|
super();
|
|
16
17
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
17
18
|
this.oidcIssuer = options.oidcIssuer;
|
|
18
|
-
this.message = options.message ?? '
|
|
19
|
+
this.message = options.message ?? 'No source IdentityProviderHandler configured';
|
|
19
20
|
this.source = options.source;
|
|
20
21
|
if (this.oidcIssuer) {
|
|
21
|
-
this.logger.info(`SP mode enabled:
|
|
22
|
+
this.logger.info(`Local SP mode enabled: account and consent routes stay local, external issuer: ${this.oidcIssuer}`);
|
|
22
23
|
}
|
|
23
24
|
else {
|
|
24
|
-
this.logger.info('Standard mode enabled
|
|
25
|
+
this.logger.info('Standard mode enabled: delegating identity routes to source IdentityProviderHandler');
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* 判断是否处理请求
|
|
29
|
-
* - SP
|
|
30
|
-
* -
|
|
30
|
+
* - Local SP mode: delegate local account/consent routes to source Handler
|
|
31
|
+
* - Standard mode: delegate to source Handler
|
|
31
32
|
*/
|
|
32
33
|
async canHandle(input) {
|
|
33
34
|
const url = input.request.url ?? '';
|
|
@@ -35,36 +36,24 @@ class AutoDetectIdentityProviderHandler extends community_server_1.HttpHandler {
|
|
|
35
36
|
if (!this.isIdpPath(url)) {
|
|
36
37
|
throw new community_server_1.NotImplementedHttpError('Not an IdP request');
|
|
37
38
|
}
|
|
38
|
-
// SP 模式:接受 IdP 路径请求,在 handle 中返回 404
|
|
39
|
-
if (this.oidcIssuer) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// 标准模式:委托给 source Handler
|
|
43
39
|
if (this.source) {
|
|
44
40
|
await this.source.canHandle(input);
|
|
45
41
|
}
|
|
46
42
|
else {
|
|
47
|
-
throw new community_server_1.NotImplementedHttpError(
|
|
43
|
+
throw new community_server_1.NotImplementedHttpError(this.message);
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
/**
|
|
51
47
|
* 处理请求
|
|
52
|
-
* - SP
|
|
53
|
-
* -
|
|
48
|
+
* - Local SP mode: delegate to source Handler so consent remains scoped by SP
|
|
49
|
+
* - Standard mode: delegate to source Handler
|
|
54
50
|
*/
|
|
55
51
|
async handle(input) {
|
|
56
|
-
if (this.oidcIssuer) {
|
|
57
|
-
const { response } = input;
|
|
58
|
-
response.writeHead(404, { 'Content-Type': 'application/json' });
|
|
59
|
-
response.end(JSON.stringify({ error: this.message }));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// 标准模式:委托给 source Handler
|
|
63
52
|
if (this.source) {
|
|
64
53
|
await this.source.handle(input);
|
|
65
54
|
}
|
|
66
55
|
else {
|
|
67
|
-
throw new community_server_1.NotImplementedHttpError(
|
|
56
|
+
throw new community_server_1.NotImplementedHttpError(this.message);
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoDetectIdentityProviderHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/AutoDetectIdentityProviderHandler.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,8DAIiC;AAWjC
|
|
1
|
+
{"version":3,"file":"AutoDetectIdentityProviderHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/AutoDetectIdentityProviderHandler.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,8DAIiC;AAWjC;;;;;;;GAOG;AACH,MAAa,iCAAkC,SAAQ,8BAAW;IAMhE,YAAY,UAAoD,EAAE;QAChE,KAAK,EAAE,CAAC;QANO,WAAM,GAAG,IAAA,oCAAY,EAAC,IAAI,CAAC,CAAC;QAO3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,8CAA8C,CAAC;QACjF,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE7B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kFAAkF,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACxH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;IAED;;;;OAIG;IACa,KAAK,CAAC,SAAS,CAAC,KAAuB;QACrD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;QAEpC,eAAe;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,0CAAuB,CAAC,oBAAoB,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,0CAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACa,KAAK,CAAC,MAAM,CAAC,KAAuB;QAClD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,0CAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,GAAW;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,CACL,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;YAC5B,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC;YACjC,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,QAAQ;YACrB,QAAQ,KAAK,SAAS,CACvB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,GAAW;QAC7B,IAAI,CAAC;YACH,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF;AA5ED,8EA4EC","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport {\n HttpHandler,\n type HttpHandlerInput,\n NotImplementedHttpError,\n} from '@solid/community-server';\n\nexport interface AutoDetectIdentityProviderHandlerOptions {\n /** 外部 IdP 的基础 URL,如果提供则启用 Local SP mode. */\n oidcIssuer?: string;\n /** Message used when no source handler is available. */\n message?: string;\n /** CSS IdentityProviderHandler that owns account, consent and WebID selection routes. */\n source?: HttpHandler;\n}\n\n/**\n * Auto-detect Identity Provider Handler\n *\n * Local SP mode still needs the local `/.account/*` surface: CSS keeps the\n * OIDC interaction and the scoped WebID picker here, while token validation can\n * trust the configured external issuer. Disabling this surface makes LinX fall\n * back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.\n */\nexport class AutoDetectIdentityProviderHandler extends HttpHandler {\n private readonly logger = getLoggerFor(this);\n private readonly oidcIssuer?: string;\n private readonly message: string;\n private readonly source?: HttpHandler;\n\n constructor(options: AutoDetectIdentityProviderHandlerOptions = {}) {\n super();\n this.oidcIssuer = options.oidcIssuer;\n this.message = options.message ?? 'No source IdentityProviderHandler configured';\n this.source = options.source;\n\n if (this.oidcIssuer) {\n this.logger.info(`Local SP mode enabled: account and consent routes stay local, external issuer: ${this.oidcIssuer}`);\n } else {\n this.logger.info('Standard mode enabled: delegating identity routes to source IdentityProviderHandler');\n }\n }\n\n /**\n * 判断是否处理请求\n * - Local SP mode: delegate local account/consent routes to source Handler\n * - Standard mode: delegate to source Handler\n */\n public override async canHandle(input: HttpHandlerInput): Promise<void> {\n const url = input.request.url ?? '';\n\n // 检查是否是 IdP 路径\n if (!this.isIdpPath(url)) {\n throw new NotImplementedHttpError('Not an IdP request');\n }\n\n if (this.source) {\n await this.source.canHandle(input);\n } else {\n throw new NotImplementedHttpError(this.message);\n }\n }\n\n /**\n * 处理请求\n * - Local SP mode: delegate to source Handler so consent remains scoped by SP\n * - Standard mode: delegate to source Handler\n */\n public override async handle(input: HttpHandlerInput): Promise<void> {\n if (this.source) {\n await this.source.handle(input);\n } else {\n throw new NotImplementedHttpError(this.message);\n }\n }\n\n /**\n * 检查是否是 IdP 路径\n */\n private isIdpPath(url: string): boolean {\n const pathname = this.getPathname(url);\n return (\n pathname.startsWith('/idp/') ||\n pathname.startsWith('/.account/') ||\n pathname === '/register' ||\n pathname === '/login' ||\n pathname === '/logout'\n );\n }\n\n /**\n * 从 URL 提取 pathname\n */\n private getPathname(url: string): string {\n try {\n return new URL(url, 'http://localhost').pathname;\n } catch {\n return url.split('?')[0];\n }\n }\n}\n"]}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"extends": [
|
|
13
13
|
"css:dist/server/HttpHandler.jsonld#HttpHandler"
|
|
14
14
|
],
|
|
15
|
-
"comment": "Auto-detect Identity Provider Handler
|
|
15
|
+
"comment": "Auto-detect Identity Provider Handler Local SP mode still needs the local `/.account/*` surface: CSS keeps the OIDC interaction and the scoped WebID picker here, while token validation can trust the configured external issuer. Disabling this surface makes LinX fall back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.",
|
|
16
16
|
"parameters": [
|
|
17
17
|
{
|
|
18
18
|
"@id": "undefineds:dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld#AutoDetectIdentityProviderHandler_options_oidcIssuer",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
-
"comment": "外部 IdP 的基础 URL,如果提供则启用 SP
|
|
28
|
+
"comment": "外部 IdP 的基础 URL,如果提供则启用 Local SP mode."
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"@id": "undefineds:dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld#AutoDetectIdentityProviderHandler_options_message",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
]
|
|
40
40
|
},
|
|
41
|
-
"comment": "
|
|
41
|
+
"comment": "Message used when no source handler is available."
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"@id": "undefineds:dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld#AutoDetectIdentityProviderHandler_options_source",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
|
-
"comment": "CSS
|
|
54
|
+
"comment": "CSS IdentityProviderHandler that owns account, consent and WebID selection routes."
|
|
55
55
|
}
|
|
56
56
|
],
|
|
57
57
|
"memberFields": [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { HttpHandler, type HttpHandlerInput } from '@solid/community-server';
|
|
2
2
|
export interface AutoDetectOidcHandlerOptions {
|
|
3
|
-
/**
|
|
3
|
+
/** External OIDC issuer base URL used as the trust source for Local SP mode. */
|
|
4
4
|
oidcIssuer?: string;
|
|
5
|
-
/**
|
|
5
|
+
/** Explanation used when this handler declines non-JWKS OIDC routes. */
|
|
6
6
|
message?: string;
|
|
7
7
|
/** JWKS 缓存时间 (ms) */
|
|
8
8
|
cacheMs?: number;
|
|
@@ -11,9 +11,13 @@ export interface AutoDetectOidcHandlerOptions {
|
|
|
11
11
|
* Auto-detect OIDC Handler
|
|
12
12
|
*
|
|
13
13
|
* 自动检测运行模式:
|
|
14
|
-
* - 如果配置了 oidcIssuer -> SP
|
|
14
|
+
* - 如果配置了 oidcIssuer -> Local SP 模式:只代理外部 issuer 的 JWKS
|
|
15
15
|
* - 如果没有配置 oidcIssuer -> 标准模式:所有 OIDC 请求透传(由 CSS 默认 Handler 处理)
|
|
16
16
|
*
|
|
17
|
+
* 注意:Local SP 模式不能禁用本地 account/consent。OIDC 交互页面和
|
|
18
|
+
* scoped WebID picker 必须继续由本地 CSS 提供,否则 Local 登录会退回
|
|
19
|
+
* Cloud consent 并暴露 Cloud Pod。
|
|
20
|
+
*
|
|
17
21
|
* 使用方式:在 HTTP pipeline 中替换默认的 OidcHandler
|
|
18
22
|
*/
|
|
19
23
|
export declare class AutoDetectOidcHandler extends HttpHandler {
|
|
@@ -26,7 +30,7 @@ export declare class AutoDetectOidcHandler extends HttpHandler {
|
|
|
26
30
|
constructor(options?: AutoDetectOidcHandlerOptions);
|
|
27
31
|
/**
|
|
28
32
|
* 判断是否处理请求
|
|
29
|
-
* - SP 模式:只处理 JWKS 请求,其他 OIDC
|
|
33
|
+
* - Local SP 模式:只处理 JWKS 请求,其他 OIDC 请求透传给 CSS 本地 OIDC handler
|
|
30
34
|
* - 标准模式:不处理任何请求(透传给 CSS 默认 Handler)
|
|
31
35
|
*/
|
|
32
36
|
canHandle({ request }: HttpHandlerInput): Promise<void>;
|
|
@@ -8,9 +8,13 @@ const community_server_1 = require("@solid/community-server");
|
|
|
8
8
|
* Auto-detect OIDC Handler
|
|
9
9
|
*
|
|
10
10
|
* 自动检测运行模式:
|
|
11
|
-
* - 如果配置了 oidcIssuer -> SP
|
|
11
|
+
* - 如果配置了 oidcIssuer -> Local SP 模式:只代理外部 issuer 的 JWKS
|
|
12
12
|
* - 如果没有配置 oidcIssuer -> 标准模式:所有 OIDC 请求透传(由 CSS 默认 Handler 处理)
|
|
13
13
|
*
|
|
14
|
+
* 注意:Local SP 模式不能禁用本地 account/consent。OIDC 交互页面和
|
|
15
|
+
* scoped WebID picker 必须继续由本地 CSS 提供,否则 Local 登录会退回
|
|
16
|
+
* Cloud consent 并暴露 Cloud Pod。
|
|
17
|
+
*
|
|
14
18
|
* 使用方式:在 HTTP pipeline 中替换默认的 OidcHandler
|
|
15
19
|
*/
|
|
16
20
|
class AutoDetectOidcHandler extends community_server_1.HttpHandler {
|
|
@@ -19,10 +23,10 @@ class AutoDetectOidcHandler extends community_server_1.HttpHandler {
|
|
|
19
23
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
20
24
|
this.oidcIssuer = options.oidcIssuer;
|
|
21
25
|
this.jwksUrl = this.oidcIssuer ? `${this.oidcIssuer.replace(/\/$/, '')}/.oidc/jwks` : undefined;
|
|
22
|
-
this.message = options.message ?? 'OIDC
|
|
26
|
+
this.message = options.message ?? 'OIDC route handled by local CSS OIDC handler';
|
|
23
27
|
this.cacheMs = options.cacheMs ?? 300000; // 默认 5 分钟
|
|
24
28
|
if (this.oidcIssuer) {
|
|
25
|
-
this.logger.info(`SP mode enabled, external
|
|
29
|
+
this.logger.info(`Local SP mode enabled, external issuer: ${this.oidcIssuer}, JWKS: ${this.jwksUrl}`);
|
|
26
30
|
}
|
|
27
31
|
else {
|
|
28
32
|
this.logger.info('Standard mode enabled, OIDC requests will pass through');
|
|
@@ -30,7 +34,7 @@ class AutoDetectOidcHandler extends community_server_1.HttpHandler {
|
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
36
|
* 判断是否处理请求
|
|
33
|
-
* - SP 模式:只处理 JWKS 请求,其他 OIDC
|
|
37
|
+
* - Local SP 模式:只处理 JWKS 请求,其他 OIDC 请求透传给 CSS 本地 OIDC handler
|
|
34
38
|
* - 标准模式:不处理任何请求(透传给 CSS 默认 Handler)
|
|
35
39
|
*/
|
|
36
40
|
async canHandle({ request }) {
|
|
@@ -43,9 +47,9 @@ class AutoDetectOidcHandler extends community_server_1.HttpHandler {
|
|
|
43
47
|
if (!this.jwksUrl) {
|
|
44
48
|
throw new community_server_1.NotImplementedHttpError('Pass through to default OIDC handler');
|
|
45
49
|
}
|
|
46
|
-
// SP 模式:只有 JWKS
|
|
50
|
+
// Local SP 模式:只有 JWKS 请求由这里处理,其它 OIDC 路由交给 CSS 本地 handler
|
|
47
51
|
if (!this.isJwksPath(url)) {
|
|
48
|
-
throw new community_server_1.NotImplementedHttpError(`
|
|
52
|
+
throw new community_server_1.NotImplementedHttpError(`Local SP mode: ${this.message}.`);
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
/**
|