@verdaccio/api 9.0.0-next-9.20 → 9.0.0-next-9.22

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.
Files changed (47) hide show
  1. package/build/dist-tags.d.ts +4 -4
  2. package/build/dist-tags.js +10 -6
  3. package/build/dist-tags.js.map +1 -1
  4. package/build/dist-tags.mjs +11 -7
  5. package/build/dist-tags.mjs.map +1 -1
  6. package/build/index.d.ts +4 -4
  7. package/build/index.js +1 -1
  8. package/build/index.js.map +1 -1
  9. package/build/index.mjs +1 -1
  10. package/build/index.mjs.map +1 -1
  11. package/build/package.d.ts +3 -3
  12. package/build/package.js +4 -3
  13. package/build/package.js.map +1 -1
  14. package/build/package.mjs +5 -4
  15. package/build/package.mjs.map +1 -1
  16. package/build/ping.d.ts +1 -1
  17. package/build/publish.d.ts +4 -4
  18. package/build/publish.js +8 -6
  19. package/build/publish.js.map +1 -1
  20. package/build/publish.mjs +9 -8
  21. package/build/publish.mjs.map +1 -1
  22. package/build/search.d.ts +1 -1
  23. package/build/user.d.ts +3 -3
  24. package/build/user.js +4 -3
  25. package/build/user.js.map +1 -1
  26. package/build/user.mjs +5 -4
  27. package/build/user.mjs.map +1 -1
  28. package/build/v1/login.d.ts +4 -4
  29. package/build/v1/login.js +6 -6
  30. package/build/v1/login.js.map +1 -1
  31. package/build/v1/login.mjs +7 -7
  32. package/build/v1/login.mjs.map +1 -1
  33. package/build/v1/profile.d.ts +3 -3
  34. package/build/v1/profile.js.map +1 -1
  35. package/build/v1/search.d.ts +4 -4
  36. package/build/v1/search.js +33 -13
  37. package/build/v1/search.js.map +1 -1
  38. package/build/v1/search.mjs +34 -14
  39. package/build/v1/search.mjs.map +1 -1
  40. package/build/v1/token.d.ts +4 -4
  41. package/build/v1/token.js +5 -3
  42. package/build/v1/token.js.map +1 -1
  43. package/build/v1/token.mjs +6 -4
  44. package/build/v1/token.mjs.map +1 -1
  45. package/build/whoami.d.ts +1 -1
  46. package/build/whoami.js.map +1 -1
  47. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"search.mjs","names":[],"sources":["../../src/v1/search.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport type { Auth } from '@verdaccio/auth';\nimport type { searchUtils } from '@verdaccio/core';\nimport { HTTP_STATUS } from '@verdaccio/core';\nimport { SEARCH_API_ENDPOINTS } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:api:search');\n\n/**\n * Endpoint for npm search v1\n * Empty value\n * - {\"objects\":[],\"total\":0,\"time\":\"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)\"}\n * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'\n */\nexport default function (route, auth: Auth, storage: Storage, logger: Logger): void {\n function checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n }\n\n route.get(SEARCH_API_ENDPOINTS.search, async (req, res, next) => {\n const { query, url } = req;\n let [size, from] = ['size', 'from'].map((k) => query[k]);\n let data;\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n\n try {\n size = parseInt(Number.isFinite(size) ? size : '20', 10);\n from = parseInt(Number.isFinite(from) ? from : '0', 10);\n\n debug('storage search initiated');\n data = await storage.search({\n query,\n url,\n abort,\n });\n debug('storage items tota: %o', data.length);\n const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final: searchUtils.SearchItemPkg[] = checkAccessPromises\n .filter((i) => i !== null)\n .slice(from, from + size);\n logger.debug(`search results ${final?.length}`);\n\n const response: searchUtils.SearchResults = {\n objects: final,\n total: final.length,\n time: new Date().toUTCString(),\n };\n\n res.status(HTTP_STATUS.OK).json(response);\n } catch (error) {\n logger.error({ error }, 'search endpoint has failed @{error.message}');\n next(error);\n return;\n } finally {\n req.socket.off('close', onClientClose);\n }\n });\n}\n"],"mappings":";;;;AASA,IAAM,QAAQ,WAAW,sBAAsB;;;;;;;AAQ/C,SAAA,eAAyB,OAAO,MAAY,SAAkB,QAAsB;CAClF,SAAS,YAAY,KAAU,MAAW,YAAsC;EAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;IACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,EAAE,MAAM,SAAS,GAGlD,OAAO,QAAQ,IAAI;SAEnB,OAAO,GAAG;SAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;GAEvC,CAAC;EACH,CAAC;CACH;CAEA,MAAM,IAAI,qBAAqB,QAAQ,OAAO,KAAK,KAAK,SAAS;EAC/D,MAAM,EAAE,OAAO,QAAQ;EACvB,IAAI,CAAC,MAAM,QAAQ,CAAC,QAAQ,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE;EACvD,IAAI;EACJ,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,MAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EAEpC,IAAI;GACF,OAAO,SAAS,OAAO,SAAS,IAAI,IAAI,OAAO,MAAM,EAAE;GACvD,OAAO,SAAS,OAAO,SAAS,IAAI,IAAI,OAAO,KAAK,EAAE;GAEtD,MAAM,0BAA0B;GAChC,OAAO,MAAM,QAAQ,OAAO;IAC1B;IACA;IACA;GACF,CAAC;GACD,MAAM,0BAA0B,KAAK,MAAM;GAO3C,MAAM,SAAqC,MANoB,QAAQ,IACrE,KAAK,KAAK,YAAY;IACpB,OAAO,YAAY,SAAS,MAAM,IAAI,WAAW;GACnD,CAAC,CACH,GAGG,QAAQ,MAAM,MAAM,IAAI,EACxB,MAAM,MAAM,OAAO,IAAI;GAC1B,OAAO,MAAM,kBAAkB,OAAO,QAAQ;GAE9C,MAAM,WAAsC;IAC1C,SAAS;IACT,OAAO,MAAM;IACb,uBAAM,IAAI,KAAK,GAAE,YAAY;GAC/B;GAEA,IAAI,OAAO,YAAY,EAAE,EAAE,KAAK,QAAQ;EAC1C,SAAS,OAAO;GACd,OAAO,MAAM,EAAE,MAAM,GAAG,6CAA6C;GACrE,KAAK,KAAK;GACV;EACF,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"search.mjs","names":[],"sources":["../../src/v1/search.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport type { Auth } from '@verdaccio/auth';\nimport type { searchUtils } from '@verdaccio/core';\nimport { HTTP_STATUS } from '@verdaccio/core';\nimport { SEARCH_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:api:search');\n\nconst DEFAULT_SIZE = 20;\n// the public npm registry caps page size at 250 as well\nconst MAX_SIZE = 250;\n// upper bound for the pagination offset so a single request cannot force\n// an access check on an arbitrarily large slice of the catalog\nconst MAX_FROM = 10_000;\n// access checks run in batches so the scan can stop early once the\n// requested page is filled\nconst CHECK_ACCESS_BATCH_SIZE = 50;\n\nfunction parseQueryInt(value: unknown, defaultValue: number, max: number): number {\n const parsed = Number.parseInt(String(value), 10);\n if (Number.isNaN(parsed) || parsed < 0) {\n return defaultValue;\n }\n return Math.min(parsed, max);\n}\n\n/**\n * Endpoint for npm search v1\n * Empty value\n * - {\"objects\":[],\"total\":0,\"time\":\"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)\"}\n * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'\n */\nexport default function (\n route,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n function checkAccess(pkg: any, auth: any, remoteUser): Promise<searchUtils.SearchItemPkg | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n }\n\n route.get(\n SEARCH_API_ENDPOINTS.search,\n rateLimit(config?.userRateLimit),\n async (req, res, next) => {\n const { query, url } = req;\n // `size` and `from` are attacker-controlled: clamp them so a single\n // request cannot demand unbounded work\n const size = parseQueryInt(query.size, DEFAULT_SIZE, MAX_SIZE);\n const from = parseQueryInt(query.from, 0, MAX_FROM);\n // rebuild the query and relative url from the clamped values so the\n // bounds hold end-to-end: storage plugins read `query.size`/`query.from`\n // and the uplink proxy forwards `url` verbatim to the remote registry\n const safeQuery = { ...query, size, from };\n const searchParams = new URLSearchParams(query);\n searchParams.set('size', String(size));\n searchParams.set('from', String(from));\n const safeUrl = `${url.split('?')[0]}?${searchParams.toString()}`;\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n\n try {\n debug('storage search initiated');\n const data = await storage.search({\n query: safeQuery,\n url: safeUrl,\n abort,\n });\n debug('storage items total: %o', data.length);\n\n // evaluate access in batches and stop as soon as the requested page\n // is filled, instead of running an auth check on every result\n const requested = from + size;\n const allowed: searchUtils.SearchItemPkg[] = [];\n for (\n let i = 0;\n i < data.length && allowed.length < requested && !abort.signal.aborted;\n i += CHECK_ACCESS_BATCH_SIZE\n ) {\n const batch = await Promise.all(\n data\n .slice(i, i + CHECK_ACCESS_BATCH_SIZE)\n .map((pkgItem) => checkAccess(pkgItem, auth, req.remote_user))\n );\n for (const item of batch) {\n if (item !== null) {\n allowed.push(item);\n }\n }\n }\n\n const final: searchUtils.SearchItemPkg[] = allowed.slice(from, requested);\n logger.debug(`search results ${final?.length}`);\n\n const response: searchUtils.SearchResults = {\n objects: final,\n total: final.length,\n time: new Date().toUTCString(),\n };\n\n res.status(HTTP_STATUS.OK).json(response);\n } catch (error) {\n logger.error({ error }, 'search endpoint has failed @{error.message}');\n next(error);\n return;\n } finally {\n req.socket.off('close', onClientClose);\n }\n }\n );\n}\n"],"mappings":";;;;AASA,IAAM,QAAQ,WAAW,sBAAsB;AAE/C,IAAM,eAAe;AAErB,IAAM,WAAW;AAGjB,IAAM,WAAW;AAGjB,IAAM,0BAA0B;AAEhC,SAAS,cAAc,OAAgB,cAAsB,KAAqB;CAChF,MAAM,SAAS,OAAO,SAAS,OAAO,KAAK,GAAG,EAAE;CAChD,IAAI,OAAO,MAAM,MAAM,KAAK,SAAS,GACnC,OAAO;CAET,OAAO,KAAK,IAAI,QAAQ,GAAG;AAC7B;;;;;;;AAQA,SAAA,eACE,OACA,MACA,SACA,QACA,QACM;CACN,SAAS,YAAY,KAAU,MAAW,YAAuD;EAC/F,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;IACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,CAAC,CAAC,MAAM,SAAS,GAGlD,OAAO,QAAQ,IAAI;SAEnB,OAAO,GAAG;SAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;GAEvC,CAAC;EACH,CAAC;CACH;CAEA,MAAM,IACJ,qBAAqB,QACrB,UAAU,QAAQ,aAAa,GAC/B,OAAO,KAAK,KAAK,SAAS;EACxB,MAAM,EAAE,OAAO,QAAQ;EAGvB,MAAM,OAAO,cAAc,MAAM,MAAM,cAAc,QAAQ;EAC7D,MAAM,OAAO,cAAc,MAAM,MAAM,GAAG,QAAQ;EAIlD,MAAM,YAAY;GAAE,GAAG;GAAO;GAAM;EAAK;EACzC,MAAM,eAAe,IAAI,gBAAgB,KAAK;EAC9C,aAAa,IAAI,QAAQ,OAAO,IAAI,CAAC;EACrC,aAAa,IAAI,QAAQ,OAAO,IAAI,CAAC;EACrC,MAAM,UAAU,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,aAAa,SAAS;EAC9D,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,MAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EAEpC,IAAI;GACF,MAAM,0BAA0B;GAChC,MAAM,OAAO,MAAM,QAAQ,OAAO;IAChC,OAAO;IACP,KAAK;IACL;GACF,CAAC;GACD,MAAM,2BAA2B,KAAK,MAAM;GAI5C,MAAM,YAAY,OAAO;GACzB,MAAM,UAAuC,CAAC;GAC9C,KACE,IAAI,IAAI,GACR,IAAI,KAAK,UAAU,QAAQ,SAAS,aAAa,CAAC,MAAM,OAAO,SAC/D,KAAK,yBACL;IACA,MAAM,QAAQ,MAAM,QAAQ,IAC1B,KACG,MAAM,GAAG,IAAI,uBAAuB,CAAC,CACrC,KAAK,YAAY,YAAY,SAAS,MAAM,IAAI,WAAW,CAAC,CACjE;IACA,KAAK,MAAM,QAAQ,OACjB,IAAI,SAAS,MACX,QAAQ,KAAK,IAAI;GAGvB;GAEA,MAAM,QAAqC,QAAQ,MAAM,MAAM,SAAS;GACxE,OAAO,MAAM,kBAAkB,OAAO,QAAQ;GAE9C,MAAM,WAAsC;IAC1C,SAAS;IACT,OAAO,MAAM;IACb,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;GAC/B;GAEA,IAAI,OAAO,YAAY,EAAE,CAAC,CAAC,KAAK,QAAQ;EAC1C,SAAS,OAAO;GACd,OAAO,MAAM,EAAE,MAAM,GAAG,6CAA6C;GACrE,KAAK,KAAK;GACV;EACF,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CACF;AACF"}
@@ -1,7 +1,7 @@
1
- import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
3
- import { Storage } from '@verdaccio/store';
4
- import { Config, Logger, Token } from '@verdaccio/types';
1
+ import type { Router } from 'express';
2
+ import type { Auth } from '@verdaccio/auth';
3
+ import type { Storage } from '@verdaccio/store';
4
+ import type { Config, Logger, Token } from '@verdaccio/types';
5
5
  export type NormalizeToken = Token & {
6
6
  cidr_whitelist: string[];
7
7
  created: string;
package/build/v1/token.js CHANGED
@@ -61,6 +61,8 @@ function token_default(route, auth, storage, config, logger) {
61
61
  const key = _verdaccio_core.cryptoUtils.generateRandomHexString(16);
62
62
  const token = await (0, _verdaccio_auth.getApiToken)(auth, config, user, password, { tokenKey: key });
63
63
  if (!token) throw _verdaccio_core.errorUtils.getInternalError();
64
+ const maskedToken = _verdaccio_core.cryptoUtils.mask(token, 5);
65
+ const created = (/* @__PURE__ */ new Date()).getTime();
64
66
  /**
65
67
  * cidr_whitelist: is not being used, we pass it through
66
68
  * token: we do not store the real token (it is generated once and retrieved
@@ -68,11 +70,11 @@ function token_default(route, auth, storage, config, logger) {
68
70
  */
69
71
  const saveToken = {
70
72
  user: name,
71
- token: _verdaccio_core.cryptoUtils.mask(token, 5),
73
+ token: maskedToken,
72
74
  key,
73
75
  cidr: cidr_whitelist,
74
76
  readonly,
75
- created: (/* @__PURE__ */ new Date()).getTime()
77
+ created
76
78
  };
77
79
  await storage.saveToken(saveToken);
78
80
  logger.debug({
@@ -95,7 +97,7 @@ function token_default(route, auth, storage, config, logger) {
95
97
  });
96
98
  });
97
99
  route.delete(_verdaccio_middleware.TOKEN_API_ENDPOINTS.delete_token, (0, _verdaccio_middleware.rateLimit)(config?.userRateLimit), async (req, res, next) => {
98
- const { params: { tokenKey } } = req;
100
+ const tokenKey = _verdaccio_core.reqUtils.paramToString(req.params.tokenKey);
99
101
  const { name } = req.remote_user;
100
102
  if ((0, lodash_es.isNil)(name) === false) {
101
103
  logger.debug({ name }, "@{name} has requested remove a token");
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","names":[],"sources":["../../src/v1/token.ts"],"sourcesContent":["import type { Response, Router } from 'express';\nimport { isBoolean, isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { getApiToken } from '@verdaccio/auth';\nimport { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils } from '@verdaccio/core';\nimport { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, RemoteUser, Token } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../../types/custom';\n\n// Granular access token options (npm >= 11) that Verdaccio does not implement\n// yet. They are accepted so `npm token create` succeeds, but the restrictions\n// they express are NOT enforced — warn so the operator is not misled.\nconst UNSUPPORTED_TOKEN_OPTIONS = [\n 'packages_and_scopes_permission',\n 'packages',\n 'packages_all',\n 'scopes',\n 'orgs',\n 'orgs_permission',\n 'expires',\n 'description',\n 'bypass_2fa',\n];\n\nexport type NormalizeToken = Token & {\n cidr_whitelist: string[];\n created: string;\n};\n\n// npm expects \"cidr_whitelist\" for token list\nfunction normalizeToken(token: Token): NormalizeToken {\n return {\n ...token,\n cidr_whitelist: token.cidr || [],\n created: new Date(token.created).toISOString(),\n };\n}\n\n// https://github.com/npm/npm-profile/blob/latest/lib/index.js\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n route.get(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n try {\n const tokens = await storage.readTokens({ user: name });\n const totalTokens = tokens.length;\n logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');\n\n res.status(HTTP_STATUS.OK);\n return next({\n objects: tokens.map(normalizeToken),\n urls: {\n next: '', // TODO: pagination?\n },\n });\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token list has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n\n route.post(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { password } = req.body;\n const { name } = req.remote_user;\n // `readonly` and `cidr_whitelist` are optional: npm >= 11 rewrote\n // `npm token create` to omit them (and only sends `cidr_whitelist` with\n // `--cidr`), so default them rather than rejecting the request. A wrong\n // type is still rejected.\n const readonly = req.body.readonly ?? false;\n const cidr_whitelist = req.body.cidr_whitelist ?? [];\n\n if (!isBoolean(readonly) || !Array.isArray(cidr_whitelist)) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));\n }\n\n const unsupportedOptions = UNSUPPORTED_TOKEN_OPTIONS.filter(\n (option) => isNil(req.body[option]) === false\n );\n if (unsupportedOptions.length > 0) {\n logger.warn(\n { options: unsupportedOptions.join(', '), userAgent: req.get('user-agent') ?? 'unknown' },\n 'granular access token options are not supported yet and will be ignored: @{options} (client: @{userAgent})'\n );\n }\n\n auth.authenticate(name, password, async (err, user?: RemoteUser) => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n return next(errorUtils.getCode(errorCode, err.message));\n }\n\n req.remote_user = user;\n\n if (typeof storage.saveToken !== 'function') {\n return next(\n errorUtils.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)\n );\n }\n\n try {\n const key = cryptoUtils.generateRandomHexString(16);\n const token = await getApiToken(auth, config, user as RemoteUser, password, {\n tokenKey: key,\n });\n if (!token) {\n throw errorUtils.getInternalError();\n }\n\n // TODO: use a utility here\n const maskedToken = cryptoUtils.mask(token, 5);\n const created = new Date().getTime();\n\n /**\n * cidr_whitelist: is not being used, we pass it through\n * token: we do not store the real token (it is generated once and retrieved\n * to the user), just a mask of it.\n */\n const saveToken: Token = {\n user: name,\n token: maskedToken,\n key,\n cidr: cidr_whitelist,\n readonly,\n created,\n };\n\n await storage.saveToken(saveToken);\n logger.debug({ key, name }, 'token @{key} was created for user @{name}');\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next(\n normalizeToken({\n token,\n user: name,\n key: saveToken.key,\n cidr: cidr_whitelist,\n readonly,\n created: saveToken.created,\n })\n );\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getInternalError(error.message));\n }\n });\n }\n );\n\n route.delete(\n TOKEN_API_ENDPOINTS.delete_token,\n rateLimit(config?.userRateLimit),\n async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {\n const {\n params: { tokenKey },\n } = req;\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n logger.debug({ name }, '@{name} has requested remove a token');\n try {\n await storage.deleteToken(name, tokenKey);\n logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');\n return next({});\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../src/v1/token.ts"],"sourcesContent":["import type { Response, Router } from 'express';\nimport { isBoolean, isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { getApiToken } from '@verdaccio/auth';\nimport {\n HEADERS,\n HTTP_STATUS,\n SUPPORT_ERRORS,\n cryptoUtils,\n errorUtils,\n reqUtils,\n} from '@verdaccio/core';\nimport { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, RemoteUser, Token } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../../types/custom';\n\n// Granular access token options (npm >= 11) that Verdaccio does not implement\n// yet. They are accepted so `npm token create` succeeds, but the restrictions\n// they express are NOT enforced — warn so the operator is not misled.\nconst UNSUPPORTED_TOKEN_OPTIONS = [\n 'packages_and_scopes_permission',\n 'packages',\n 'packages_all',\n 'scopes',\n 'orgs',\n 'orgs_permission',\n 'expires',\n 'description',\n 'bypass_2fa',\n];\n\nexport type NormalizeToken = Token & {\n cidr_whitelist: string[];\n created: string;\n};\n\n// npm expects \"cidr_whitelist\" for token list\nfunction normalizeToken(token: Token): NormalizeToken {\n return {\n ...token,\n cidr_whitelist: token.cidr || [],\n created: new Date(token.created).toISOString(),\n };\n}\n\n// https://github.com/npm/npm-profile/blob/latest/lib/index.js\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n route.get(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n try {\n const tokens = await storage.readTokens({ user: name });\n const totalTokens = tokens.length;\n logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');\n\n res.status(HTTP_STATUS.OK);\n return next({\n objects: tokens.map(normalizeToken),\n urls: {\n next: '', // TODO: pagination?\n },\n });\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token list has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n\n route.post(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { password } = req.body;\n const { name } = req.remote_user;\n // `readonly` and `cidr_whitelist` are optional: npm >= 11 rewrote\n // `npm token create` to omit them (and only sends `cidr_whitelist` with\n // `--cidr`), so default them rather than rejecting the request. A wrong\n // type is still rejected.\n const readonly = req.body.readonly ?? false;\n const cidr_whitelist = req.body.cidr_whitelist ?? [];\n\n if (!isBoolean(readonly) || !Array.isArray(cidr_whitelist)) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));\n }\n\n const unsupportedOptions = UNSUPPORTED_TOKEN_OPTIONS.filter(\n (option) => isNil(req.body[option]) === false\n );\n if (unsupportedOptions.length > 0) {\n logger.warn(\n { options: unsupportedOptions.join(', '), userAgent: req.get('user-agent') ?? 'unknown' },\n 'granular access token options are not supported yet and will be ignored: @{options} (client: @{userAgent})'\n );\n }\n\n auth.authenticate(name, password, async (err, user?: RemoteUser) => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n return next(errorUtils.getCode(errorCode, err.message));\n }\n\n req.remote_user = user;\n\n if (typeof storage.saveToken !== 'function') {\n return next(\n errorUtils.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)\n );\n }\n\n try {\n const key = cryptoUtils.generateRandomHexString(16);\n const token = await getApiToken(auth, config, user as RemoteUser, password, {\n tokenKey: key,\n });\n if (!token) {\n throw errorUtils.getInternalError();\n }\n\n // TODO: use a utility here\n const maskedToken = cryptoUtils.mask(token, 5);\n const created = new Date().getTime();\n\n /**\n * cidr_whitelist: is not being used, we pass it through\n * token: we do not store the real token (it is generated once and retrieved\n * to the user), just a mask of it.\n */\n const saveToken: Token = {\n user: name,\n token: maskedToken,\n key,\n cidr: cidr_whitelist,\n readonly,\n created,\n };\n\n await storage.saveToken(saveToken);\n logger.debug({ key, name }, 'token @{key} was created for user @{name}');\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next(\n normalizeToken({\n token,\n user: name,\n key: saveToken.key,\n cidr: cidr_whitelist,\n readonly,\n created: saveToken.created,\n })\n );\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getInternalError(error.message));\n }\n });\n }\n );\n\n route.delete(\n TOKEN_API_ENDPOINTS.delete_token,\n rateLimit(config?.userRateLimit),\n async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {\n const tokenKey = reqUtils.paramToString(req.params.tokenKey);\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n logger.debug({ name }, '@{name} has requested remove a token');\n try {\n await storage.deleteToken(name, tokenKey);\n logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');\n return next({});\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { TOKEN_API_ENDPOINTS, rateLimit } from "@verdaccio/middleware";
2
- import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils } from "@verdaccio/core";
2
+ import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils, reqUtils } from "@verdaccio/core";
3
3
  import { getApiToken } from "@verdaccio/auth";
4
4
  import { isBoolean, isNil } from "lodash-es";
5
5
  //#region src/v1/token.ts
@@ -61,6 +61,8 @@ function token_default(route, auth, storage, config, logger) {
61
61
  const key = cryptoUtils.generateRandomHexString(16);
62
62
  const token = await getApiToken(auth, config, user, password, { tokenKey: key });
63
63
  if (!token) throw errorUtils.getInternalError();
64
+ const maskedToken = cryptoUtils.mask(token, 5);
65
+ const created = (/* @__PURE__ */ new Date()).getTime();
64
66
  /**
65
67
  * cidr_whitelist: is not being used, we pass it through
66
68
  * token: we do not store the real token (it is generated once and retrieved
@@ -68,11 +70,11 @@ function token_default(route, auth, storage, config, logger) {
68
70
  */
69
71
  const saveToken = {
70
72
  user: name,
71
- token: cryptoUtils.mask(token, 5),
73
+ token: maskedToken,
72
74
  key,
73
75
  cidr: cidr_whitelist,
74
76
  readonly,
75
- created: (/* @__PURE__ */ new Date()).getTime()
77
+ created
76
78
  };
77
79
  await storage.saveToken(saveToken);
78
80
  logger.debug({
@@ -95,7 +97,7 @@ function token_default(route, auth, storage, config, logger) {
95
97
  });
96
98
  });
97
99
  route.delete(TOKEN_API_ENDPOINTS.delete_token, rateLimit(config?.userRateLimit), async (req, res, next) => {
98
- const { params: { tokenKey } } = req;
100
+ const tokenKey = reqUtils.paramToString(req.params.tokenKey);
99
101
  const { name } = req.remote_user;
100
102
  if (isNil(name) === false) {
101
103
  logger.debug({ name }, "@{name} has requested remove a token");
@@ -1 +1 @@
1
- {"version":3,"file":"token.mjs","names":[],"sources":["../../src/v1/token.ts"],"sourcesContent":["import type { Response, Router } from 'express';\nimport { isBoolean, isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { getApiToken } from '@verdaccio/auth';\nimport { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils } from '@verdaccio/core';\nimport { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, RemoteUser, Token } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../../types/custom';\n\n// Granular access token options (npm >= 11) that Verdaccio does not implement\n// yet. They are accepted so `npm token create` succeeds, but the restrictions\n// they express are NOT enforced — warn so the operator is not misled.\nconst UNSUPPORTED_TOKEN_OPTIONS = [\n 'packages_and_scopes_permission',\n 'packages',\n 'packages_all',\n 'scopes',\n 'orgs',\n 'orgs_permission',\n 'expires',\n 'description',\n 'bypass_2fa',\n];\n\nexport type NormalizeToken = Token & {\n cidr_whitelist: string[];\n created: string;\n};\n\n// npm expects \"cidr_whitelist\" for token list\nfunction normalizeToken(token: Token): NormalizeToken {\n return {\n ...token,\n cidr_whitelist: token.cidr || [],\n created: new Date(token.created).toISOString(),\n };\n}\n\n// https://github.com/npm/npm-profile/blob/latest/lib/index.js\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n route.get(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n try {\n const tokens = await storage.readTokens({ user: name });\n const totalTokens = tokens.length;\n logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');\n\n res.status(HTTP_STATUS.OK);\n return next({\n objects: tokens.map(normalizeToken),\n urls: {\n next: '', // TODO: pagination?\n },\n });\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token list has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n\n route.post(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { password } = req.body;\n const { name } = req.remote_user;\n // `readonly` and `cidr_whitelist` are optional: npm >= 11 rewrote\n // `npm token create` to omit them (and only sends `cidr_whitelist` with\n // `--cidr`), so default them rather than rejecting the request. A wrong\n // type is still rejected.\n const readonly = req.body.readonly ?? false;\n const cidr_whitelist = req.body.cidr_whitelist ?? [];\n\n if (!isBoolean(readonly) || !Array.isArray(cidr_whitelist)) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));\n }\n\n const unsupportedOptions = UNSUPPORTED_TOKEN_OPTIONS.filter(\n (option) => isNil(req.body[option]) === false\n );\n if (unsupportedOptions.length > 0) {\n logger.warn(\n { options: unsupportedOptions.join(', '), userAgent: req.get('user-agent') ?? 'unknown' },\n 'granular access token options are not supported yet and will be ignored: @{options} (client: @{userAgent})'\n );\n }\n\n auth.authenticate(name, password, async (err, user?: RemoteUser) => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n return next(errorUtils.getCode(errorCode, err.message));\n }\n\n req.remote_user = user;\n\n if (typeof storage.saveToken !== 'function') {\n return next(\n errorUtils.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)\n );\n }\n\n try {\n const key = cryptoUtils.generateRandomHexString(16);\n const token = await getApiToken(auth, config, user as RemoteUser, password, {\n tokenKey: key,\n });\n if (!token) {\n throw errorUtils.getInternalError();\n }\n\n // TODO: use a utility here\n const maskedToken = cryptoUtils.mask(token, 5);\n const created = new Date().getTime();\n\n /**\n * cidr_whitelist: is not being used, we pass it through\n * token: we do not store the real token (it is generated once and retrieved\n * to the user), just a mask of it.\n */\n const saveToken: Token = {\n user: name,\n token: maskedToken,\n key,\n cidr: cidr_whitelist,\n readonly,\n created,\n };\n\n await storage.saveToken(saveToken);\n logger.debug({ key, name }, 'token @{key} was created for user @{name}');\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next(\n normalizeToken({\n token,\n user: name,\n key: saveToken.key,\n cidr: cidr_whitelist,\n readonly,\n created: saveToken.created,\n })\n );\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getInternalError(error.message));\n }\n });\n }\n );\n\n route.delete(\n TOKEN_API_ENDPOINTS.delete_token,\n rateLimit(config?.userRateLimit),\n async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {\n const {\n params: { tokenKey },\n } = req;\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n logger.debug({ name }, '@{name} has requested remove a token');\n try {\n await storage.deleteToken(name, tokenKey);\n logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');\n return next({});\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"token.mjs","names":[],"sources":["../../src/v1/token.ts"],"sourcesContent":["import type { Response, Router } from 'express';\nimport { isBoolean, isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { getApiToken } from '@verdaccio/auth';\nimport {\n HEADERS,\n HTTP_STATUS,\n SUPPORT_ERRORS,\n cryptoUtils,\n errorUtils,\n reqUtils,\n} from '@verdaccio/core';\nimport { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, RemoteUser, Token } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../../types/custom';\n\n// Granular access token options (npm >= 11) that Verdaccio does not implement\n// yet. They are accepted so `npm token create` succeeds, but the restrictions\n// they express are NOT enforced — warn so the operator is not misled.\nconst UNSUPPORTED_TOKEN_OPTIONS = [\n 'packages_and_scopes_permission',\n 'packages',\n 'packages_all',\n 'scopes',\n 'orgs',\n 'orgs_permission',\n 'expires',\n 'description',\n 'bypass_2fa',\n];\n\nexport type NormalizeToken = Token & {\n cidr_whitelist: string[];\n created: string;\n};\n\n// npm expects \"cidr_whitelist\" for token list\nfunction normalizeToken(token: Token): NormalizeToken {\n return {\n ...token,\n cidr_whitelist: token.cidr || [],\n created: new Date(token.created).toISOString(),\n };\n}\n\n// https://github.com/npm/npm-profile/blob/latest/lib/index.js\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n route.get(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n try {\n const tokens = await storage.readTokens({ user: name });\n const totalTokens = tokens.length;\n logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');\n\n res.status(HTTP_STATUS.OK);\n return next({\n objects: tokens.map(normalizeToken),\n urls: {\n next: '', // TODO: pagination?\n },\n });\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token list has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n\n route.post(\n TOKEN_API_ENDPOINTS.get_tokens,\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {\n const { password } = req.body;\n const { name } = req.remote_user;\n // `readonly` and `cidr_whitelist` are optional: npm >= 11 rewrote\n // `npm token create` to omit them (and only sends `cidr_whitelist` with\n // `--cidr`), so default them rather than rejecting the request. A wrong\n // type is still rejected.\n const readonly = req.body.readonly ?? false;\n const cidr_whitelist = req.body.cidr_whitelist ?? [];\n\n if (!isBoolean(readonly) || !Array.isArray(cidr_whitelist)) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));\n }\n\n const unsupportedOptions = UNSUPPORTED_TOKEN_OPTIONS.filter(\n (option) => isNil(req.body[option]) === false\n );\n if (unsupportedOptions.length > 0) {\n logger.warn(\n { options: unsupportedOptions.join(', '), userAgent: req.get('user-agent') ?? 'unknown' },\n 'granular access token options are not supported yet and will be ignored: @{options} (client: @{userAgent})'\n );\n }\n\n auth.authenticate(name, password, async (err, user?: RemoteUser) => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n return next(errorUtils.getCode(errorCode, err.message));\n }\n\n req.remote_user = user;\n\n if (typeof storage.saveToken !== 'function') {\n return next(\n errorUtils.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)\n );\n }\n\n try {\n const key = cryptoUtils.generateRandomHexString(16);\n const token = await getApiToken(auth, config, user as RemoteUser, password, {\n tokenKey: key,\n });\n if (!token) {\n throw errorUtils.getInternalError();\n }\n\n // TODO: use a utility here\n const maskedToken = cryptoUtils.mask(token, 5);\n const created = new Date().getTime();\n\n /**\n * cidr_whitelist: is not being used, we pass it through\n * token: we do not store the real token (it is generated once and retrieved\n * to the user), just a mask of it.\n */\n const saveToken: Token = {\n user: name,\n token: maskedToken,\n key,\n cidr: cidr_whitelist,\n readonly,\n created,\n };\n\n await storage.saveToken(saveToken);\n logger.debug({ key, name }, 'token @{key} was created for user @{name}');\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next(\n normalizeToken({\n token,\n user: name,\n key: saveToken.key,\n cidr: cidr_whitelist,\n readonly,\n created: saveToken.created,\n })\n );\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getInternalError(error.message));\n }\n });\n }\n );\n\n route.delete(\n TOKEN_API_ENDPOINTS.delete_token,\n rateLimit(config?.userRateLimit),\n async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {\n const tokenKey = reqUtils.paramToString(req.params.tokenKey);\n const { name } = req.remote_user;\n\n if (isNil(name) === false) {\n logger.debug({ name }, '@{name} has requested remove a token');\n try {\n await storage.deleteToken(name, tokenKey);\n logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');\n return next({});\n } catch (error: any) {\n logger.error({ error: error.msg }, 'token creation has failed: @{error}');\n return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));\n }\n }\n return next(errorUtils.getUnauthorized());\n }\n );\n}\n"],"mappings":"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"}
package/build/whoami.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { Router } from 'express';
1
+ import type { Router } from 'express';
2
2
  export default function (route: Router): void;
@@ -1 +1 @@
1
- {"version":3,"file":"whoami.js","names":[],"sources":["../src/whoami.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Response, Router } from 'express';\n\nimport { errorUtils } from '@verdaccio/core';\nimport { USER_API_ENDPOINTS } from '@verdaccio/middleware';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:user');\n\nexport default function (route: Router): void {\n route.get(\n USER_API_ENDPOINTS.whoami,\n (req: $RequestExtend, _res: Response, next: $NextFunctionVer): any => {\n // remote_user is set by the auth middleware\n const username = req?.remote_user?.name;\n if (!username) {\n debug('whoami: user not found');\n return next(errorUtils.getUnauthorized('Unauthorized'));\n }\n\n debug('whoami: response %o', username);\n return next({ username: username });\n }\n );\n}\n"],"mappings":";;;;;;AAQA,IAAM,WAAA,GAAA,MAAA,SAAmB,oBAAoB;AAE7C,SAAA,eAAyB,OAAqB;CAC5C,MAAM,IACJ,sBAAA,mBAAmB,SAClB,KAAqB,MAAgB,SAAgC;EAEpE,MAAM,WAAW,KAAK,aAAa;EACnC,IAAI,CAAC,UAAU;GACb,QAAM,wBAAwB;GAC9B,OAAO,KAAK,gBAAA,WAAW,gBAAgB,cAAc,CAAC;EACxD;EAEA,QAAM,uBAAuB,QAAQ;EACrC,OAAO,KAAK,EAAY,SAAS,CAAC;CACpC,CACF;AACF"}
1
+ {"version":3,"file":"whoami.js","names":[],"sources":["../src/whoami.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Response, Router } from 'express';\n\nimport { errorUtils } from '@verdaccio/core';\nimport { USER_API_ENDPOINTS } from '@verdaccio/middleware';\n\nimport type { $NextFunctionVer, $RequestExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:user');\n\nexport default function (route: Router): void {\n route.get(\n USER_API_ENDPOINTS.whoami,\n (req: $RequestExtend, _res: Response, next: $NextFunctionVer): any => {\n // remote_user is set by the auth middleware\n const username = req?.remote_user?.name;\n if (!username) {\n debug('whoami: user not found');\n return next(errorUtils.getUnauthorized('Unauthorized'));\n }\n\n debug('whoami: response %o', username);\n return next({ username: username });\n }\n );\n}\n"],"mappings":";;;;;;AAQA,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,oBAAoB;AAE7C,SAAA,eAAyB,OAAqB;CAC5C,MAAM,IACJ,sBAAA,mBAAmB,SAClB,KAAqB,MAAgB,SAAgC;EAEpE,MAAM,WAAW,KAAK,aAAa;EACnC,IAAI,CAAC,UAAU;GACb,QAAM,wBAAwB;GAC9B,OAAO,KAAK,gBAAA,WAAW,gBAAgB,cAAc,CAAC;EACxD;EAEA,QAAM,uBAAuB,QAAQ;EACrC,OAAO,KAAK,EAAY,SAAS,CAAC;CACpC,CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/api",
3
- "version": "9.0.0-next-9.20",
3
+ "version": "9.0.0-next-9.22",
4
4
  "description": "Verdaccio Registry API",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -33,27 +33,27 @@
33
33
  },
34
34
  "license": "MIT",
35
35
  "dependencies": {
36
- "@verdaccio/auth": "9.0.0-next-9.20",
37
- "@verdaccio/config": "9.0.0-next-9.20",
38
- "@verdaccio/core": "9.0.0-next-9.20",
39
- "@verdaccio/hooks": "9.0.0-next-9.20",
40
- "@verdaccio/logger": "9.0.0-next-9.20",
41
- "@verdaccio/middleware": "9.0.0-next-9.20",
42
- "@verdaccio/store": "9.0.0-next-9.20",
36
+ "@verdaccio/auth": "9.0.0-next-9.22",
37
+ "@verdaccio/config": "9.0.0-next-9.22",
38
+ "@verdaccio/core": "9.0.0-next-9.22",
39
+ "@verdaccio/hooks": "9.0.0-next-9.22",
40
+ "@verdaccio/logger": "9.0.0-next-9.22",
41
+ "@verdaccio/middleware": "9.0.0-next-9.22",
42
+ "@verdaccio/store": "9.0.0-next-9.22",
43
43
  "debug": "4.4.3",
44
44
  "express": "5.2.1",
45
45
  "lodash-es": "4.18.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/lodash-es": "4.17.12",
49
- "@verdaccio/test-helper": "5.0.0-next-9.21",
49
+ "@verdaccio/test-helper": "5.0.0-next-9.23",
50
50
  "@verdaccio/types": "14.0.0-next-9.11",
51
51
  "mockdate": "3.0.5",
52
52
  "nock": "13.5.6",
53
53
  "rimraf": "6.1.3",
54
54
  "supertest": "7.1.4",
55
- "vite": "8.0.14",
56
- "vitest": "4.1.7"
55
+ "vite": "8.1.4",
56
+ "vitest": "4.1.10"
57
57
  },
58
58
  "funding": {
59
59
  "type": "opencollective",