import * as plugins from './plugins.js'; /** * The two Unix endpoints of the per-user authswitch account authority. * * The production pair is never spelled out here: `resolveAuthSwitchAuthorityPaths` in authswitch is * the only definition of where the daemon listens, and a second copy in AGL would drift the moment * the daemon moved. A test supplies its own pair instead, which is also what keeps the suite off the * real authority. */ export interface IAuthoritySocketPaths { managementSocketPath: string; runtimeSocketPath: string; } /** * Why the authority endpoints could not be derived on this host. * * It is deliberately not the projection's unavailable reason: that one names what a view shows, * this one names who has to act. A host that never installed the authority is an ordinary state; an * environment that cannot be read is the owner's to fix and is therefore reported. */ export type TAuthorityPathsProblem = /** No private runtime directory at all -- the authority was never installed for this user. */ | 'no_runtime_directory' /** A runtime directory exists but the environment around it is unusable, e.g. a relative `XDG_DATA_HOME`. */ | 'unusable_environment'; export class AuthorityPathsUnavailableError extends Error { constructor( public readonly problem: TAuthorityPathsProblem, messageArg: string, causeArg: unknown, ) { // The resolver's own error is the only description of what was wrong with the environment, so it // travels with this one instead of being replaced by a summary. super(messageArg, { cause: causeArg }); this.name = 'AuthorityPathsUnavailableError'; } } /** * Which of the two failures a rejected path resolution is. * * authswitch derives both socket paths from `XDG_RUNTIME_DIR` and the data home, and refuses when * either is missing or relative. Without a runtime directory there is simply no authority on this * host; with one, the refusal means the environment is misconfigured. */ const authorityPathsProblem = (): TAuthorityPathsProblem => ( process.env.XDG_RUNTIME_DIR ? 'unusable_environment' : 'no_runtime_directory' ); /** * Where the daemon listens for this user. * * A host without a private runtime directory has no authority to talk to, which is an ordinary state * on a machine where the account authority was never installed -- so it is reported as one instead * of failing startup. */ export const resolveProductionAuthoritySocketPaths = (): IAuthoritySocketPaths => { let paths: plugins.authswitch.IAuthSwitchAuthorityPaths; try { paths = plugins.authswitch.resolveAuthSwitchAuthorityPaths(); } catch (errorArg) { const problem = authorityPathsProblem(); throw new AuthorityPathsUnavailableError( problem, problem === 'no_runtime_directory' ? 'The account authority has no private runtime directory on this host.' : 'The account authority endpoints cannot be derived from this host\'s environment.', errorArg, ); } return { managementSocketPath: paths.authoritySocketPath, runtimeSocketPath: paths.runtimeSocketPath, }; }; /** * The one client AGL uses to reach the authority. * * Both endpoints are handed over together because the client owns both: the credential-free * management socket and the backend-only runtime socket. `AuthSwitchClient` opens one bounded * connection per request and holds no socket between calls, so this object owns no resource and * needs no shutdown of its own. */ export const createAuthorityClient = ( pathsArg: IAuthoritySocketPaths, ): plugins.authswitch.AuthSwitchClient => new plugins.authswitch.AuthSwitchClient( pathsArg.managementSocketPath, pathsArg.runtimeSocketPath, ); /** * The production pair, or `undefined` on a host that has none. * * The one call site is the controller entry point. Everything else -- every test, and the * controller itself -- receives endpoints rather than resolving them, so no code path can reach the * user's real authority without having been handed it. * * A misconfigured environment never disappears into that `undefined`: accounts stay unavailable * either way, but only one of the two cases is something the owner can fix, so it is reported the * way every other degraded startup condition is. */ export const resolveOptionalAuthoritySocketPaths = ( reportArg: (messageArg: string, errorArg: unknown) => void = console.error, ): IAuthoritySocketPaths | undefined => { try { return resolveProductionAuthoritySocketPaths(); } catch (errorArg) { if (!(errorArg instanceof AuthorityPathsUnavailableError)) throw errorArg; if (errorArg.problem === 'unusable_environment') { reportArg('The account authority endpoints could not be resolved; accounts stay unavailable.', errorArg); } return undefined; } }; /** The snapshot shape this AGL reads. A daemon answering anything else is `schema_unsupported`. */ export const supportedAuthoritySchemaVersion = 2 as const;