Files
my-pi/pi-permission-system/src/permissions-service.ts
T

101 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { AccessIntent } from "./access-intent/access-intent";
import { buildAccessIntentForSurface } from "./access-intent/input-normalizer";
import type { Authorizer } from "./authority/authorizer";
import type { AuthorizerRegistrar } from "./authority/authorizer-registry";
import { resolveBashAdvisoryCheck } from "./bash-advisory-check";
import type { PathNormalizer } from "./path-normalizer";
import type { PermissionsService } from "./service";
import type {
ToolAccessExtractor,
ToolAccessExtractorRegistrar,
} from "./tool-access-extractor-registry";
import type {
ToolInputFormatter,
ToolInputFormatterRegistrar,
} from "./tool-input-formatter-registry";
import type { PermissionCheckResult, PermissionState } from "./types";
/**
* Resolution surface the service needs: answer a gate-style {@link AccessIntent}
* (composing the session ruleset internally) and report a tool-level state.
* `PermissionResolver` satisfies it.
*/
interface ResolverForService {
resolve(intent: AccessIntent): PermissionCheckResult;
getToolPermission(toolName: string, agentName?: string): PermissionState;
}
/** Narrow session view: hands out the cwd-bound path normalizer. */
interface PathNormalizerProvider {
getPathNormalizer(): PathNormalizer;
}
/**
* In-process implementation of the cross-extension {@link PermissionsService}.
*
* Constructed once in the composition root and backed by the single shared
* `PermissionResolver` and `PermissionSession` that the gates also use — so
* service queries and gate-path decisions see the same state. Path-shaped
* surface queries route through the resolver as an `access-path` intent, so
* they match the lexical aliases ∪ canonical (symlink-resolved) set the gates
* do (#503); non-path surfaces stay on the `tool` intent.
*/
export class LocalPermissionsService implements PermissionsService {
constructor(
private readonly resolver: ResolverForService,
private readonly session: PathNormalizerProvider,
private readonly formatterRegistry: ToolInputFormatterRegistrar,
private readonly accessExtractorRegistry: ToolAccessExtractorRegistrar,
private readonly authorizerRegistry: AuthorizerRegistrar,
) {}
checkPermission(
surface: string,
value?: string,
agentName?: string,
): ReturnType<PermissionsService["checkPermission"]> {
// Bash decomposes at gate parity: a chained/nested command is split into
// its command-pattern units and resolved most-restrictive, matching what
// the enforcement gate enforces (#309). A cold parser falls back to the
// whole-string match inside resolveBashAdvisoryCheck.
if (surface === "bash") {
return resolveBashAdvisoryCheck(value ?? "", agentName, this.resolver);
}
const intent = buildAccessIntentForSurface(
surface,
value,
this.session.getPathNormalizer(),
agentName,
);
return this.resolver.resolve(intent);
}
getToolPermission(
toolName: string,
agentName?: string,
): ReturnType<PermissionsService["getToolPermission"]> {
return this.resolver.getToolPermission(toolName, agentName);
}
registerToolInputFormatter(
toolName: string,
formatter: ToolInputFormatter,
): ReturnType<PermissionsService["registerToolInputFormatter"]> {
return this.formatterRegistry.register(toolName, formatter);
}
registerToolAccessExtractor(
toolName: string,
extractor: ToolAccessExtractor,
): ReturnType<PermissionsService["registerToolAccessExtractor"]> {
return this.accessExtractorRegistry.register(toolName, extractor);
}
registerAuthorizer(
name: string,
authorize: Authorizer["authorize"],
): ReturnType<PermissionsService["registerAuthorizer"]> {
return this.authorizerRegistry.register(name, authorize);
}
}