From f18582914b96e5fbd6001cdc69bc750204df84d8 Mon Sep 17 00:00:00 2001 From: Xingwang Liao Date: Sat, 12 Oct 2024 12:50:42 +0800 Subject: [PATCH] feat: refactor plugin types and config parse --- eslint.config.mjs | 7 +++ src/server/plugin/AuthCore.ts | 6 +- src/server/plugin/Config.ts | 114 ++++++++++++++-------------------- src/server/plugin/Plugin.ts | 36 +++++++---- 4 files changed, 79 insertions(+), 84 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index c8bfb77..309d5c8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -59,6 +59,13 @@ export default tseslint.config( }, ], "@typescript-eslint/no-import-type-side-effects": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], }, settings: { "import-x/resolver": { diff --git a/src/server/plugin/AuthCore.ts b/src/server/plugin/AuthCore.ts index 6823b53..72ef121 100644 --- a/src/server/plugin/AuthCore.ts +++ b/src/server/plugin/AuthCore.ts @@ -88,9 +88,9 @@ export class AuthCore { */ private initConfiguredGroups(packages: Record = {}): string[] { for (const packageConfig of Object.values(packages)) { - const groups = ["access", "publish", "unpublish"] - .flatMap((key) => packageConfig[key] as string[]) - .filter(Boolean); + const groups = (["access", "publish", "unpublish"] as const) + .flatMap((key) => packageConfig[key]) + .filter(Boolean) as string[]; return [...new Set(groups)]; } diff --git a/src/server/plugin/Config.ts b/src/server/plugin/Config.ts index 975ff5a..6d33a27 100644 --- a/src/server/plugin/Config.ts +++ b/src/server/plugin/Config.ts @@ -1,11 +1,7 @@ import process from "node:process"; import { defaultSecurity } from "@verdaccio/config"; -import type { - Config as IncorrectVerdaccioConfig, - PackageAccess as IncorrectVerdaccioPackageAccess, - Security, -} from "@verdaccio/types"; +import type { Config, PackageAccess as IncorrectPackageAccess, PackageList, Security } from "@verdaccio/types"; import merge from "deepmerge"; import { mixed, object, Schema, string } from "yup"; @@ -13,20 +9,13 @@ import { plugin, pluginKey } from "@/constants"; import { CONFIG_ENV_NAME_REGEX } from "@/server/constants"; import logger from "@/server/logger"; -// Verdaccio incorrectly types some of these as string arrays -// although they are all strings. -export type PackageAccess = IncorrectVerdaccioPackageAccess & { - unpublish?: string[]; -}; - -export type VerdaccioConfig = IncorrectVerdaccioConfig & { - packages?: Record; - security?: Partial; -}; - type ProviderType = "gitlab"; -export interface PluginConfig { +export interface PackageAccess extends IncorrectPackageAccess { + unpublish?: string[]; +} + +export interface OpenIDConfig { "provider-host": string; "provider-type"?: ProviderType; "configuration-uri"?: string; @@ -44,13 +33,6 @@ export interface PluginConfig { "group-users"?: string | Record; } -export interface OpenIdConfig { - middlewares: Record<"openid", PluginConfig>; - auth: Record<"openid", PluginConfig>; -} - -export type Config = OpenIdConfig & VerdaccioConfig; - export interface ConfigHolder { providerHost: string; providerType?: string; @@ -107,61 +89,55 @@ function handleValidationError(error: any, key: string) { process.exit(1); } -function getOpenIdConfigValue(config: OpenIdConfig, key: keyof PluginConfig, schema: Schema): T { - const valueOrEnvironmentName = config.auth?.[pluginKey]?.[key] ?? config.middlewares?.[pluginKey]?.[key]; - - /** - * If the value is not defined in the config, use the plugin name and key as the environment variable name. - * - * eg. client-id -> `VERDACCIO_OPENID_CLIENT_ID` - */ - const environmentName: string = - typeof valueOrEnvironmentName === "string" && CONFIG_ENV_NAME_REGEX.test(valueOrEnvironmentName) - ? valueOrEnvironmentName - : `${plugin.name}-${key}`.toUpperCase().replaceAll("-", "_"); - - /** - * Allow environment variables to be used as values. - */ - const value = getEnvironmentValue(environmentName) ?? valueOrEnvironmentName; - - try { - schema.validateSync(value); - } catch (error: any) { - handleValidationError(error, key); - } - - return value as T; -} - export class ParsedPluginConfig implements ConfigHolder { - constructor(public readonly config: Config) { - for (const node of ["middlewares", "auth"] satisfies (keyof OpenIdConfig)[]) { - const object_ = config[node]?.[pluginKey]; + constructor( + private readonly config: OpenIDConfig, + private readonly verdaccioConfig: Config, + ) {} - if (!object_) { - throw new Error(`"${node}.${pluginKey}" must be defined in the verdaccio config.`); - } - } - } - - public get secret() { - return this.config.secret; + public get secret(): string { + return this.verdaccioConfig.secret; } public get security(): Security { - return merge(defaultSecurity, this.config.security || {}); + return merge(defaultSecurity, this.verdaccioConfig.security ?? {}); } - public get packages() { - return this.config.packages ?? {}; + public get packages(): PackageList { + return this.verdaccioConfig.packages ?? {}; } public get urlPrefix(): string { - return this.config.url_prefix ?? ""; - } + return this.verdaccioConfig.url_prefix ?? ""; + } + + private getConfigValue(key: keyof OpenIDConfig, schema: Schema): T { + const valueOrEnvironmentName = + this.config[key] ?? + this.verdaccioConfig.auth?.[pluginKey]?.[key] ?? + this.verdaccioConfig.middlewares?.[pluginKey]?.[key]; + + /** + * If the value is not defined in the config, use the plugin name and key as the environment variable name. + * + * eg. client-id -> `VERDACCIO_OPENID_CLIENT_ID` + */ + const environmentName: string = + typeof valueOrEnvironmentName === "string" && CONFIG_ENV_NAME_REGEX.test(valueOrEnvironmentName) + ? valueOrEnvironmentName + : `${plugin.name}-${key}`.toUpperCase().replaceAll("-", "_"); + + /** + * Allow environment variables to be used as values. + */ + const value = getEnvironmentValue(environmentName) ?? valueOrEnvironmentName; + + try { + schema.validateSync(value); + } catch (error: any) { + handleValidationError(error, key); + } - private getConfigValue(key: keyof PluginConfig, schema: Schema): T { - return getOpenIdConfigValue(this.config, key, schema); + return value as T; } public get providerHost() { diff --git a/src/server/plugin/Plugin.ts b/src/server/plugin/Plugin.ts index b5fec8c..f7d46c7 100644 --- a/src/server/plugin/Plugin.ts +++ b/src/server/plugin/Plugin.ts @@ -4,13 +4,15 @@ import type { AllowAccess, AuthAccessCallback, AuthCallback, + IBasicAuth, IPluginAuth, IPluginMiddleware, - Logger, + PluginOptions, RemoteUser, } from "@verdaccio/types"; import type { Application } from "express"; +import { plugin } from "@/constants"; import { debug } from "@/server/debugger"; import { CliFlow, WebFlow } from "@/server/flows"; import logger, { setLogger } from "@/server/logger"; @@ -19,7 +21,7 @@ import { registerGlobalProxy } from "@/server/proxy-agent"; import { AuthCore, type User } from "./AuthCore"; import type { AuthProvider } from "./AuthProvider"; -import { type Config, type PackageAccess, ParsedPluginConfig } from "./Config"; +import { type OpenIDConfig, type PackageAccess, ParsedPluginConfig } from "./Config"; import { PatchHtml } from "./PatchHtml"; import { ServeStatic } from "./ServeStatic"; @@ -31,24 +33,34 @@ export class Plugin implements IPluginMiddleware, IPluginAuth { private readonly provider: AuthProvider; private readonly core: AuthCore; - constructor(config: Config, params: { logger: Logger }) { - setLogger(params.logger); + constructor(config: OpenIDConfig, options: PluginOptions) { + setLogger(options.logger); + + const verdaccioConfig = options.config; registerGlobalProxy({ - http_proxy: config.http_proxy, - https_proxy: config.https_proxy, - no_proxy: config.no_proxy, + http_proxy: verdaccioConfig.http_proxy, + https_proxy: verdaccioConfig.https_proxy, + no_proxy: verdaccioConfig.no_proxy, }); - this.config = new ParsedPluginConfig(config); - this.provider = new OpenIDConnectAuthProvider(this.config); - this.core = new AuthCore(this.config, this.provider); + const parsedConfig = new ParsedPluginConfig(config, verdaccioConfig); + const provider = new OpenIDConnectAuthProvider(parsedConfig); + const core = new AuthCore(parsedConfig, provider); + + this.config = parsedConfig; + this.provider = provider; + this.core = core; + } + + public get version(): string { + return plugin.version; } /** * IPluginMiddleware */ - register_middlewares(app: Application, auth) { + register_middlewares(app: Application, auth: IBasicAuth, _storage) { this.core.setAuth(auth as Auth); const children = [ @@ -119,7 +131,7 @@ export class Plugin implements IPluginMiddleware, IPluginAuth { const grant = this.checkPackageAccess(user, config.access); if (!grant) { - logger.info( + logger.debug( { username: user.name, package: config.name }, `user "@{username}" is not allowed to access "@{package}"`, );