@verdaccio/web 9.0.0-next-9.29 → 9.0.0-next-9.31

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.
@@ -17,7 +17,7 @@ var api_default = (auth, storage, config) => {
17
17
  route.use(_verdaccio_middleware.WebUrlsNamespace.data, require_package.default(storage, auth, config));
18
18
  route.use(_verdaccio_middleware.WebUrlsNamespace.data, require_search.default(storage, auth));
19
19
  route.use(_verdaccio_middleware.WebUrlsNamespace.data, require_sidebar.default(config, storage, auth));
20
- route.use(_verdaccio_middleware.WebUrlsNamespace.data, require_readme.default(storage, auth));
20
+ route.use(_verdaccio_middleware.WebUrlsNamespace.data, require_readme.default(storage, auth, config));
21
21
  if (require_web_utils.hasLogin(config)) route.use(_verdaccio_middleware.WebUrlsNamespace.sec, require_user.default(auth, config, storage));
22
22
  return route;
23
23
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,SAAA,GAAQ,QAAA,OAAA,CAAO;CACrB,MAAM,IACJ,sBAAA,iBAAiB,OAAA,GACjB,sBAAA,UAAA,CAAU;EACR,UAAU;EACV,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,IAAI,kBAAA,SAAS,MAAM,GACjB,MAAM,IAAI,sBAAA,iBAAiB,KAAK,aAAA,QAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth, config));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,SAAA,GAAQ,QAAA,OAAA,CAAO;CACrB,MAAM,IACJ,sBAAA,iBAAiB,OAAA,GACjB,sBAAA,UAAA,CAAU;EACR,UAAU;EACV,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,MAAM,MAAM,CAAC;CAC9D,IAAI,kBAAA,SAAS,MAAM,GACjB,MAAM,IAAI,sBAAA,iBAAiB,KAAK,aAAA,QAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
@@ -17,7 +17,7 @@ var api_default = (auth, storage, config) => {
17
17
  route.use(WebUrlsNamespace.data, addPackageWebApi(storage, auth, config));
18
18
  route.use(WebUrlsNamespace.data, addSearchWebApi(storage, auth));
19
19
  route.use(WebUrlsNamespace.data, addSidebarWebApi(config, storage, auth));
20
- route.use(WebUrlsNamespace.data, addReadmeWebApi(storage, auth));
20
+ route.use(WebUrlsNamespace.data, addReadmeWebApi(storage, auth, config));
21
21
  if (hasLogin(config)) route.use(WebUrlsNamespace.sec, addUserAuthApi(auth, config, storage));
22
22
  return route;
23
23
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,QAAQ,OAAO;CACrB,MAAM,IACJ,iBAAiB,MACjB,UAAU;EACR,UAAU;EACV,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,iBAAiB,MAAM,iBAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,iBAAiB,MAAM,gBAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,iBAAiB,MAAM,iBAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,iBAAiB,MAAM,gBAAO,SAAS,IAAI,CAAC;CACtD,IAAI,SAAS,MAAM,GACjB,MAAM,IAAI,iBAAiB,KAAK,eAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth, config));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,QAAQ,OAAO;CACrB,MAAM,IACJ,iBAAiB,MACjB,UAAU;EACR,UAAU;EACV,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,iBAAiB,MAAM,iBAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,iBAAiB,MAAM,gBAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,iBAAiB,MAAM,iBAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,iBAAiB,MAAM,gBAAO,SAAS,MAAM,MAAM,CAAC;CAC9D,IAAI,SAAS,MAAM,GACjB,MAAM,IAAI,iBAAiB,KAAK,eAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
@@ -2,7 +2,8 @@ import { Router } from 'express';
2
2
  import type { Auth } from '@verdaccio/auth';
3
3
  import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
4
4
  import type { Storage } from '@verdaccio/store';
5
+ import type { Config } from '@verdaccio/types';
5
6
  export { $RequestExtend, $ResponseExtend, $NextFunctionVer };
6
7
  export declare const NOT_README_FOUND = "ERROR: No README data found!";
7
- declare function addReadmeWebApi(storage: Storage, auth: Auth): Router;
8
+ declare function addReadmeWebApi(storage: Storage, auth: Auth, config: Config): Router;
8
9
  export default addReadmeWebApi;
@@ -1,10 +1,10 @@
1
1
  const require_runtime = require("../_virtual/_rolldown/runtime.js");
2
2
  const require_web_utils = require("../web-utils.js");
3
+ const require_scoped_access = require("./scoped-access.js");
3
4
  let debug = require("debug");
4
5
  debug = require_runtime.__toESM(debug);
5
6
  let express = require("express");
6
7
  let _verdaccio_core = require("@verdaccio/core");
7
- let _verdaccio_logger = require("@verdaccio/logger");
8
8
  let _verdaccio_middleware = require("@verdaccio/middleware");
9
9
  //#region src/api/readme.ts
10
10
  var NOT_README_FOUND = "ERROR: No README data found!";
@@ -32,18 +32,12 @@ var getReadmeFromManifest = (manifest, v) => {
32
32
  debug$1("readme: %o %o", v, id);
33
33
  return readme;
34
34
  };
35
- function addReadmeWebApi(storage, auth) {
35
+ function addReadmeWebApi(storage, auth, config) {
36
36
  debug$1("initialized readme web api");
37
- const can = (0, _verdaccio_middleware.allow)(auth, {
38
- beforeAll: (a, b) => _verdaccio_logger.logger.trace(a, b),
39
- afterAll: (a, b) => _verdaccio_logger.logger.trace(a, b)
40
- });
41
37
  const pkgRouter = (0, express.Router)();
42
- pkgRouter.get([_verdaccio_middleware.WebUrls.readme_package_scoped_version, _verdaccio_middleware.WebUrls.readme_package_version], can("access"), async function(req, res, next) {
38
+ pkgRouter.get([_verdaccio_middleware.WebUrls.readme_package_scoped_version, _verdaccio_middleware.WebUrls.readme_package_version], require_scoped_access.scopedPackageAccess(auth, config), async function(req, res, next) {
43
39
  debug$1("readme hit");
44
- const scope = _verdaccio_core.reqUtils.paramToString(req.params.scope).replace(/^@/, "");
45
- const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
46
- const name = scope ? require_web_utils.addScope(scope, packageName) : packageName;
40
+ const name = req.scopedPackageName;
47
41
  debug$1("readme name %o", name);
48
42
  const requestOptions = (0, _verdaccio_middleware.getRequestOptions)(req);
49
43
  try {
@@ -54,9 +48,19 @@ function addReadmeWebApi(storage, auth) {
54
48
  requestOptions
55
49
  });
56
50
  debug$1("readme pkg %o", manifest?.name);
57
- res.set(_verdaccio_core.HEADER_TYPE.CONTENT_TYPE, _verdaccio_core.HEADERS.TEXT_PLAIN_UTF8);
58
51
  const { v } = req.query;
59
- next(getReadme(getReadmeFromManifest(manifest, v)));
52
+ let requestedVersion;
53
+ if (typeof v === "string") {
54
+ requestedVersion = require_web_utils.resolveVersion(manifest, v);
55
+ if (!requestedVersion) {
56
+ debug$1("version %o not found for %o", v, name);
57
+ res.status(_verdaccio_core.HTTP_STATUS.NOT_FOUND);
58
+ res.end();
59
+ return;
60
+ }
61
+ }
62
+ res.set(_verdaccio_core.HEADER_TYPE.CONTENT_TYPE, _verdaccio_core.HEADERS.TEXT_PLAIN_UTF8);
63
+ next(getReadme(getReadmeFromManifest(manifest, requestedVersion)));
60
64
  } catch (err) {
61
65
  next(err);
62
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"readme.js","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,kBAAA,eAAe,UAAU,SAAS,gBAAA,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,gBAAA,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,QAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,QAAM,4BAA4B;CAClC,MAAM,OAAA,GAAM,sBAAA,MAAA,CAAM,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,aAAA,GAAY,QAAA,OAAA,CAAO;CAEzB,UAAU,IACR,CAAC,sBAAA,QAAQ,+BAA+B,sBAAA,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,QAAM,YAAY;EAClB,MAAM,QAAQ,gBAAA,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,kBAAA,SAAS,OAAO,WAAW,IAAI;EACpD,QAAM,kBAAkB,IAAI;EAC5B,MAAM,kBAAA,GAAiB,sBAAA,kBAAA,CAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,QAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,gBAAA,YAAY,cAAc,gBAAA,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"readme.js","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Manifest } from '@verdaccio/types';\n\nimport { isVersionValid, resolveVersion } from '../web-utils';\nimport { scopedPackageAccess } from './scoped-access';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth, config: Config): Router {\n debug('initialized readme web api');\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n scopedPackageAccess(auth, config),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const name = (req as $RequestExtend & { scopedPackageName: string }).scopedPackageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n // TODO: sanitize query\n const { v } = req.query;\n // `v` may be a version or a dist-tag; anything else is a 404\n let requestedVersion: string | undefined;\n if (typeof v === 'string') {\n requestedVersion = resolveVersion(manifest, v);\n if (!requestedVersion) {\n debug('version %o not found for %o', v, name);\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n }\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n const readme = getReadmeFromManifest(manifest, requestedVersion);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;;;AAoBA,IAAa,mBAAmB;AAEhC,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,kBAAA,eAAe,UAAU,SAAS,gBAAA,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,gBAAA,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,QAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAY,QAAwB;CAC7E,QAAM,4BAA4B;CAClC,MAAM,aAAA,GAAY,QAAA,OAAA,CAAO;CAEzB,UAAU,IACR,CAAC,sBAAA,QAAQ,+BAA+B,sBAAA,QAAQ,sBAAsB,GACtE,sBAAA,oBAAoB,MAAM,MAAM,GAChC,eACE,KACA,KACA,MACe;EACf,QAAM,YAAY;EAClB,MAAM,OAAQ,IAAuD;EACrE,QAAM,kBAAkB,IAAI;EAC5B,MAAM,kBAAA,GAAiB,sBAAA,kBAAA,CAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,QAAM,iBAAiB,UAAU,IAAI;GAErC,MAAM,EAAE,MAAM,IAAI;GAElB,IAAI;GACJ,IAAI,OAAO,MAAM,UAAU;IACzB,mBAAmB,kBAAA,eAAe,UAAU,CAAC;IAC7C,IAAI,CAAC,kBAAkB;KACrB,QAAM,+BAA+B,GAAG,IAAI;KAC5C,IAAI,OAAO,gBAAA,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;GACF;GACA,IAAI,IAAI,gBAAA,YAAY,cAAc,gBAAA,QAAQ,eAAe;GAEzD,KAAK,UADU,sBAAsB,UAAU,gBAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
@@ -1,9 +1,9 @@
1
- import { addScope, isVersionValid } from "../web-utils.mjs";
1
+ import { isVersionValid, resolveVersion } from "../web-utils.mjs";
2
+ import { scopedPackageAccess } from "./scoped-access.mjs";
2
3
  import buildDebug from "debug";
3
4
  import { Router } from "express";
4
- import { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from "@verdaccio/core";
5
- import { logger } from "@verdaccio/logger";
6
- import { WebUrls, allow, getRequestOptions } from "@verdaccio/middleware";
5
+ import { DIST_TAGS, HEADERS, HEADER_TYPE, HTTP_STATUS } from "@verdaccio/core";
6
+ import { WebUrls, getRequestOptions } from "@verdaccio/middleware";
7
7
  //#region src/api/readme.ts
8
8
  var NOT_README_FOUND = "ERROR: No README data found!";
9
9
  var debug = buildDebug("verdaccio:web:api:readme");
@@ -30,18 +30,12 @@ var getReadmeFromManifest = (manifest, v) => {
30
30
  debug("readme: %o %o", v, id);
31
31
  return readme;
32
32
  };
33
- function addReadmeWebApi(storage, auth) {
33
+ function addReadmeWebApi(storage, auth, config) {
34
34
  debug("initialized readme web api");
35
- const can = allow(auth, {
36
- beforeAll: (a, b) => logger.trace(a, b),
37
- afterAll: (a, b) => logger.trace(a, b)
38
- });
39
35
  const pkgRouter = Router();
40
- pkgRouter.get([WebUrls.readme_package_scoped_version, WebUrls.readme_package_version], can("access"), async function(req, res, next) {
36
+ pkgRouter.get([WebUrls.readme_package_scoped_version, WebUrls.readme_package_version], scopedPackageAccess(auth, config), async function(req, res, next) {
41
37
  debug("readme hit");
42
- const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, "");
43
- const packageName = reqUtils.paramToString(req.params.package);
44
- const name = scope ? addScope(scope, packageName) : packageName;
38
+ const name = req.scopedPackageName;
45
39
  debug("readme name %o", name);
46
40
  const requestOptions = getRequestOptions(req);
47
41
  try {
@@ -52,9 +46,19 @@ function addReadmeWebApi(storage, auth) {
52
46
  requestOptions
53
47
  });
54
48
  debug("readme pkg %o", manifest?.name);
55
- res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);
56
49
  const { v } = req.query;
57
- next(getReadme(getReadmeFromManifest(manifest, v)));
50
+ let requestedVersion;
51
+ if (typeof v === "string") {
52
+ requestedVersion = resolveVersion(manifest, v);
53
+ if (!requestedVersion) {
54
+ debug("version %o not found for %o", v, name);
55
+ res.status(HTTP_STATUS.NOT_FOUND);
56
+ res.end();
57
+ return;
58
+ }
59
+ }
60
+ res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);
61
+ next(getReadme(getReadmeFromManifest(manifest, requestedVersion)));
58
62
  } catch (err) {
59
63
  next(err);
60
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"readme.mjs","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,eAAe,UAAU,SAAS,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,MAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,4BAA4B;CAClC,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,YAAY,OAAO;CAEzB,UAAU,IACR,CAAC,QAAQ,+BAA+B,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,YAAY;EAClB,MAAM,QAAQ,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,kBAAkB,IAAI;EAC5B,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,MAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,YAAY,cAAc,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"readme.mjs","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Manifest } from '@verdaccio/types';\n\nimport { isVersionValid, resolveVersion } from '../web-utils';\nimport { scopedPackageAccess } from './scoped-access';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth, config: Config): Router {\n debug('initialized readme web api');\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n scopedPackageAccess(auth, config),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const name = (req as $RequestExtend & { scopedPackageName: string }).scopedPackageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n // TODO: sanitize query\n const { v } = req.query;\n // `v` may be a version or a dist-tag; anything else is a 404\n let requestedVersion: string | undefined;\n if (typeof v === 'string') {\n requestedVersion = resolveVersion(manifest, v);\n if (!requestedVersion) {\n debug('version %o not found for %o', v, name);\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n }\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n const readme = getReadmeFromManifest(manifest, requestedVersion);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;AAoBA,IAAa,mBAAmB;AAEhC,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,eAAe,UAAU,SAAS,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,MAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAY,QAAwB;CAC7E,MAAM,4BAA4B;CAClC,MAAM,YAAY,OAAO;CAEzB,UAAU,IACR,CAAC,QAAQ,+BAA+B,QAAQ,sBAAsB,GACtE,oBAAoB,MAAM,MAAM,GAChC,eACE,KACA,KACA,MACe;EACf,MAAM,YAAY;EAClB,MAAM,OAAQ,IAAuD;EACrE,MAAM,kBAAkB,IAAI;EAC5B,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,MAAM,iBAAiB,UAAU,IAAI;GAErC,MAAM,EAAE,MAAM,IAAI;GAElB,IAAI;GACJ,IAAI,OAAO,MAAM,UAAU;IACzB,mBAAmB,eAAe,UAAU,CAAC;IAC7C,IAAI,CAAC,kBAAkB;KACrB,MAAM,+BAA+B,GAAG,IAAI;KAC5C,IAAI,OAAO,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;GACF;GACA,IAAI,IAAI,YAAY,cAAc,QAAQ,eAAe;GAEzD,KAAK,UADU,sBAAsB,UAAU,gBAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
@@ -0,0 +1,14 @@
1
+ import type { Auth } from '@verdaccio/auth';
2
+ import type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
3
+ import type { Config } from '@verdaccio/types';
4
+ /**
5
+ * Builds the package name from the route params; returns null for a malformed
6
+ * `:scope` segment.
7
+ */
8
+ type Param = string | string[] | undefined;
9
+ export declare function resolveScopedName(rawScope: Param, rawPackage: Param): string | null;
10
+ /**
11
+ * Validates the route params and checks access before the handlers run.
12
+ */
13
+ export declare function scopedPackageAccess(auth: Auth, config: Config): (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer) => void;
14
+ export {};
@@ -0,0 +1,33 @@
1
+ const require_web_utils = require("../web-utils.js");
2
+ let _verdaccio_core = require("@verdaccio/core");
3
+ let _verdaccio_config = require("@verdaccio/config");
4
+ //#region src/api/scoped-access.ts
5
+ function resolveScopedName(rawScope, rawPackage) {
6
+ const packageName = _verdaccio_core.reqUtils.paramToString(rawPackage);
7
+ const scope = _verdaccio_core.reqUtils.paramToString(rawScope);
8
+ if (!scope) return packageName;
9
+ if (scope[0] !== "@") return null;
10
+ const name = require_web_utils.addScope(scope.slice(1), packageName);
11
+ return _verdaccio_core.validationUtils.validatePackage(name) && !name.includes("*") ? name : null;
12
+ }
13
+ /**
14
+ * Validates the route params and checks access before the handlers run.
15
+ */
16
+ function scopedPackageAccess(auth, config) {
17
+ return function(req, _res, next) {
18
+ const packageName = resolveScopedName(req.params.scope, req.params.package);
19
+ if (packageName === null) return next(_verdaccio_core.errorUtils.getNotFound());
20
+ req.scopedPackageName = packageName;
21
+ const remoteUser = require_web_utils.hasLogin(config) ? req.remote_user : (0, _verdaccio_config.createAnonymousRemoteUser)();
22
+ auth.allow_access({ packageName }, remoteUser, (err, allowed) => {
23
+ if (err) return next(err);
24
+ if (allowed) return next();
25
+ return next(_verdaccio_core.errorUtils.getForbidden());
26
+ });
27
+ };
28
+ }
29
+ //#endregion
30
+ exports.resolveScopedName = resolveScopedName;
31
+ exports.scopedPackageAccess = scopedPackageAccess;
32
+
33
+ //# sourceMappingURL=scoped-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-access.js","names":[],"sources":["../../src/api/scoped-access.ts"],"sourcesContent":["import type { Auth } from '@verdaccio/auth';\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport { errorUtils, reqUtils, validationUtils } from '@verdaccio/core';\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';\nimport type { Config, RemoteUser } from '@verdaccio/types';\n\nimport { addScope, hasLogin } from '../web-utils';\n\n/**\n * Builds the package name from the route params; returns null for a malformed\n * `:scope` segment.\n */\ntype Param = string | string[] | undefined;\n\nexport function resolveScopedName(rawScope: Param, rawPackage: Param): string | null {\n const packageName = reqUtils.paramToString(rawPackage);\n const scope = reqUtils.paramToString(rawScope);\n if (!scope) {\n return packageName;\n }\n if (scope[0] !== '@') {\n return null;\n }\n const name = addScope(scope.slice(1), packageName);\n return validationUtils.validatePackage(name) && !name.includes('*') ? name : null;\n}\n\n/**\n * Validates the route params and checks access before the handlers run.\n */\nexport function scopedPackageAccess(auth: Auth, config: Config) {\n return function (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer): void {\n const packageName = resolveScopedName(req.params.scope, req.params.package);\n if (packageName === null) {\n return next(errorUtils.getNotFound());\n }\n (req as $RequestExtend & { scopedPackageName?: string }).scopedPackageName = packageName;\n const remoteUser: RemoteUser = hasLogin(config) ? req.remote_user : createAnonymousRemoteUser();\n auth.allow_access({ packageName }, remoteUser, (err, allowed): void => {\n if (err) {\n return next(err);\n }\n if (allowed) {\n return next();\n }\n return next(errorUtils.getForbidden());\n });\n };\n}\n"],"mappings":";;;;AAcA,SAAgB,kBAAkB,UAAiB,YAAkC;CACnF,MAAM,cAAc,gBAAA,SAAS,cAAc,UAAU;CACrD,MAAM,QAAQ,gBAAA,SAAS,cAAc,QAAQ;CAC7C,IAAI,CAAC,OACH,OAAO;CAET,IAAI,MAAM,OAAO,KACf,OAAO;CAET,MAAM,OAAO,kBAAA,SAAS,MAAM,MAAM,CAAC,GAAG,WAAW;CACjD,OAAO,gBAAA,gBAAgB,gBAAgB,IAAI,KAAK,CAAC,KAAK,SAAS,GAAG,IAAI,OAAO;AAC/E;;;;AAKA,SAAgB,oBAAoB,MAAY,QAAgB;CAC9D,OAAO,SAAU,KAAqB,MAAuB,MAA8B;EACzF,MAAM,cAAc,kBAAkB,IAAI,OAAO,OAAO,IAAI,OAAO,OAAO;EAC1E,IAAI,gBAAgB,MAClB,OAAO,KAAK,gBAAA,WAAW,YAAY,CAAC;EAEtC,IAAyD,oBAAoB;EAC7E,MAAM,aAAyB,kBAAA,SAAS,MAAM,IAAI,IAAI,eAAA,GAAc,kBAAA,0BAAA,CAA0B;EAC9F,KAAK,aAAa,EAAE,YAAY,GAAG,aAAa,KAAK,YAAkB;GACrE,IAAI,KACF,OAAO,KAAK,GAAG;GAEjB,IAAI,SACF,OAAO,KAAK;GAEd,OAAO,KAAK,gBAAA,WAAW,aAAa,CAAC;EACvC,CAAC;CACH;AACF"}
@@ -0,0 +1,32 @@
1
+ import { addScope, hasLogin } from "../web-utils.mjs";
2
+ import { errorUtils, reqUtils, validationUtils } from "@verdaccio/core";
3
+ import { createAnonymousRemoteUser } from "@verdaccio/config";
4
+ //#region src/api/scoped-access.ts
5
+ function resolveScopedName(rawScope, rawPackage) {
6
+ const packageName = reqUtils.paramToString(rawPackage);
7
+ const scope = reqUtils.paramToString(rawScope);
8
+ if (!scope) return packageName;
9
+ if (scope[0] !== "@") return null;
10
+ const name = addScope(scope.slice(1), packageName);
11
+ return validationUtils.validatePackage(name) && !name.includes("*") ? name : null;
12
+ }
13
+ /**
14
+ * Validates the route params and checks access before the handlers run.
15
+ */
16
+ function scopedPackageAccess(auth, config) {
17
+ return function(req, _res, next) {
18
+ const packageName = resolveScopedName(req.params.scope, req.params.package);
19
+ if (packageName === null) return next(errorUtils.getNotFound());
20
+ req.scopedPackageName = packageName;
21
+ const remoteUser = hasLogin(config) ? req.remote_user : createAnonymousRemoteUser();
22
+ auth.allow_access({ packageName }, remoteUser, (err, allowed) => {
23
+ if (err) return next(err);
24
+ if (allowed) return next();
25
+ return next(errorUtils.getForbidden());
26
+ });
27
+ };
28
+ }
29
+ //#endregion
30
+ export { resolveScopedName, scopedPackageAccess };
31
+
32
+ //# sourceMappingURL=scoped-access.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-access.mjs","names":[],"sources":["../../src/api/scoped-access.ts"],"sourcesContent":["import type { Auth } from '@verdaccio/auth';\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport { errorUtils, reqUtils, validationUtils } from '@verdaccio/core';\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';\nimport type { Config, RemoteUser } from '@verdaccio/types';\n\nimport { addScope, hasLogin } from '../web-utils';\n\n/**\n * Builds the package name from the route params; returns null for a malformed\n * `:scope` segment.\n */\ntype Param = string | string[] | undefined;\n\nexport function resolveScopedName(rawScope: Param, rawPackage: Param): string | null {\n const packageName = reqUtils.paramToString(rawPackage);\n const scope = reqUtils.paramToString(rawScope);\n if (!scope) {\n return packageName;\n }\n if (scope[0] !== '@') {\n return null;\n }\n const name = addScope(scope.slice(1), packageName);\n return validationUtils.validatePackage(name) && !name.includes('*') ? name : null;\n}\n\n/**\n * Validates the route params and checks access before the handlers run.\n */\nexport function scopedPackageAccess(auth: Auth, config: Config) {\n return function (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer): void {\n const packageName = resolveScopedName(req.params.scope, req.params.package);\n if (packageName === null) {\n return next(errorUtils.getNotFound());\n }\n (req as $RequestExtend & { scopedPackageName?: string }).scopedPackageName = packageName;\n const remoteUser: RemoteUser = hasLogin(config) ? req.remote_user : createAnonymousRemoteUser();\n auth.allow_access({ packageName }, remoteUser, (err, allowed): void => {\n if (err) {\n return next(err);\n }\n if (allowed) {\n return next();\n }\n return next(errorUtils.getForbidden());\n });\n };\n}\n"],"mappings":";;;;AAcA,SAAgB,kBAAkB,UAAiB,YAAkC;CACnF,MAAM,cAAc,SAAS,cAAc,UAAU;CACrD,MAAM,QAAQ,SAAS,cAAc,QAAQ;CAC7C,IAAI,CAAC,OACH,OAAO;CAET,IAAI,MAAM,OAAO,KACf,OAAO;CAET,MAAM,OAAO,SAAS,MAAM,MAAM,CAAC,GAAG,WAAW;CACjD,OAAO,gBAAgB,gBAAgB,IAAI,KAAK,CAAC,KAAK,SAAS,GAAG,IAAI,OAAO;AAC/E;;;;AAKA,SAAgB,oBAAoB,MAAY,QAAgB;CAC9D,OAAO,SAAU,KAAqB,MAAuB,MAA8B;EACzF,MAAM,cAAc,kBAAkB,IAAI,OAAO,OAAO,IAAI,OAAO,OAAO;EAC1E,IAAI,gBAAgB,MAClB,OAAO,KAAK,WAAW,YAAY,CAAC;EAEtC,IAAyD,oBAAoB;EAC7E,MAAM,aAAyB,SAAS,MAAM,IAAI,IAAI,cAAc,0BAA0B;EAC9F,KAAK,aAAa,EAAE,YAAY,GAAG,aAAa,KAAK,YAAkB;GACrE,IAAI,KACF,OAAO,KAAK,GAAG;GAEjB,IAAI,SACF,OAAO,KAAK;GAEd,OAAO,KAAK,WAAW,aAAa,CAAC;EACvC,CAAC;CACH;AACF"}
@@ -1,11 +1,11 @@
1
1
  const require_runtime = require("../_virtual/_rolldown/runtime.js");
2
2
  const require_web_utils = require("../web-utils.js");
3
3
  const require_author_utils = require("../author-utils.js");
4
+ const require_scoped_access = require("./scoped-access.js");
4
5
  let debug = require("debug");
5
6
  debug = require_runtime.__toESM(debug);
6
7
  let express = require("express");
7
8
  let _verdaccio_core = require("@verdaccio/core");
8
- let _verdaccio_logger = require("@verdaccio/logger");
9
9
  let _verdaccio_middleware = require("@verdaccio/middleware");
10
10
  let _verdaccio_tarball = require("@verdaccio/tarball");
11
11
  //#region src/api/sidebar.ts
@@ -13,14 +13,8 @@ var debug$1 = (0, debug.default)("verdaccio:web:api:sidebar");
13
13
  function addSidebarWebApi(config, storage, auth) {
14
14
  debug$1("initialized sidebar web api");
15
15
  const router = (0, express.Router)();
16
- const can = (0, _verdaccio_middleware.allow)(auth, {
17
- beforeAll: (a, b) => _verdaccio_logger.logger.trace(a, b),
18
- afterAll: (a, b) => _verdaccio_logger.logger.trace(a, b)
19
- });
20
- router.get([_verdaccio_middleware.WebUrls.sidebar_scopped_package, _verdaccio_middleware.WebUrls.sidebar_package], can("access"), async function(req, res, next) {
21
- const scope = _verdaccio_core.reqUtils.paramToString(req.params.scope).replace(/^@/, "");
22
- const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
23
- const name = scope ? require_web_utils.addScope(scope, packageName) : packageName;
16
+ router.get([_verdaccio_middleware.WebUrls.sidebar_scopped_package, _verdaccio_middleware.WebUrls.sidebar_package], require_scoped_access.scopedPackageAccess(auth, config), async function(req, res, next) {
17
+ const name = req.scopedPackageName;
24
18
  const requestOptions = (0, _verdaccio_middleware.getRequestOptions)(req);
25
19
  try {
26
20
  const info = await storage.getPackageByOptions({
@@ -30,15 +24,31 @@ function addSidebarWebApi(config, storage, auth) {
30
24
  requestOptions
31
25
  });
32
26
  const { v } = req.query;
27
+ let requestedVersion;
28
+ if (typeof v === "string") {
29
+ requestedVersion = require_web_utils.resolveVersion(info, v);
30
+ if (!requestedVersion) {
31
+ debug$1("version %o not found for %o", v, name);
32
+ res.status(_verdaccio_core.HTTP_STATUS.NOT_FOUND);
33
+ res.end();
34
+ return;
35
+ }
36
+ }
33
37
  let sideBarInfo = { ...info };
34
38
  sideBarInfo.versions = (0, _verdaccio_tarball.convertDistRemoteToLocalTarballUrls)(info, requestOptions, config.url_prefix).versions;
35
- if (typeof v === "string" && require_web_utils.isVersionValid(info, v)) {
36
- sideBarInfo.latest = sideBarInfo.versions[v];
37
- sideBarInfo.latest.author = require_author_utils.formatAuthor(sideBarInfo.latest.author);
38
- } else {
39
- sideBarInfo.latest = sideBarInfo.versions[info[_verdaccio_core.DIST_TAGS].latest];
40
- sideBarInfo.latest.author = require_author_utils.formatAuthor(sideBarInfo.latest.author);
39
+ if (requestedVersion) sideBarInfo.latest = sideBarInfo.versions[requestedVersion];
40
+ else {
41
+ const latestTag = info[_verdaccio_core.DIST_TAGS]?.latest;
42
+ const versionKeys = Object.keys(sideBarInfo.versions);
43
+ const latestVersion = typeof latestTag === "string" && sideBarInfo.versions[latestTag] ? latestTag : _verdaccio_core.pkgUtils.semverSort(versionKeys).pop() ?? versionKeys.pop();
44
+ if (!latestVersion) {
45
+ res.status(_verdaccio_core.HTTP_STATUS.NOT_FOUND);
46
+ res.end();
47
+ return;
48
+ }
49
+ sideBarInfo.latest = sideBarInfo.versions[latestVersion];
41
50
  }
51
+ sideBarInfo.latest.author = require_author_utils.formatAuthor(sideBarInfo.latest.author);
42
52
  sideBarInfo = require_web_utils.deleteProperties([
43
53
  "readme",
44
54
  "_attachments",
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.js","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;;;AAuBA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,QAAM,6BAA6B;CACnC,MAAM,UAAA,GAAS,QAAA,OAAA,CAAO;CACtB,MAAM,OAAA,GAAM,sBAAA,MAAA,CAAM,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,sBAAA,QAAQ,yBAAyB,sBAAA,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,QAAQ,gBAAA,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,kBAAA,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,kBAAA,GAAiB,sBAAA,kBAAA,CAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,YAAA,GAAW,mBAAA,oCAAA,CACrB,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,gBAAA,UAAU,CAAC;IAC1D,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,kBAAA,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,qBAAA,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,qBAAA,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,gBAAA,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"sidebar.js","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, pkgUtils } from '@verdaccio/core';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { deleteProperties, resolveVersion } from '../web-utils';\nimport { scopedPackageAccess } from './scoped-access';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n scopedPackageAccess(auth, config),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const name = (req as $RequestExtend & { scopedPackageName: string }).scopedPackageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n const { v } = req.query;\n // `v` may be a version or a dist-tag; anything else is a 404\n let requestedVersion: string | undefined;\n if (typeof v === 'string') {\n requestedVersion = resolveVersion(info, v);\n if (!requestedVersion) {\n debug('version %o not found for %o', v, name);\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n }\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (requestedVersion) {\n sideBarInfo.latest = sideBarInfo.versions[requestedVersion];\n } else {\n // a manifest without a resolvable dist-tags.latest (corrupt or partial\n // uplink data) used to crash here and surface as a 404 for a package\n // that exists; fall back to the highest available version — semver\n // sorted, since key insertion order reflects publish order, and if\n // every key is invalid semver (fully corrupt manifest), last key wins\n const latestTag = info[DIST_TAGS]?.latest;\n const versionKeys = Object.keys(sideBarInfo.versions);\n const latestVersion =\n typeof latestTag === 'string' && sideBarInfo.versions[latestTag]\n ? latestTag\n : (pkgUtils.semverSort(versionKeys).pop() ?? versionKeys.pop());\n if (!latestVersion) {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n sideBarInfo.latest = sideBarInfo.versions[latestVersion];\n }\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;;;AAsBA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,QAAM,6BAA6B;CACnC,MAAM,UAAA,GAAS,QAAA,OAAA,CAAO;CAEtB,OAAO,IACL,CAAC,sBAAA,QAAQ,yBAAyB,sBAAA,QAAQ,eAAe,GACzD,sBAAA,oBAAoB,MAAM,MAAM,GAChC,eACE,KACA,KACA,MACe;EACf,MAAM,OAAQ,IAAuD;EACrE,MAAM,kBAAA,GAAiB,sBAAA,kBAAA,CAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GACD,MAAM,EAAE,MAAM,IAAI;GAElB,IAAI;GACJ,IAAI,OAAO,MAAM,UAAU;IACzB,mBAAmB,kBAAA,eAAe,MAAM,CAAC;IACzC,IAAI,CAAC,kBAAkB;KACrB,QAAM,+BAA+B,GAAG,IAAI;KAC5C,IAAI,OAAO,gBAAA,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;GACF;GACA,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,YAAA,GAAW,mBAAA,oCAAA,CACrB,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,kBACF,YAAY,SAAS,YAAY,SAAS;QACrC;IAML,MAAM,YAAY,KAAK,gBAAA,UAAU,EAAE;IACnC,MAAM,cAAc,OAAO,KAAK,YAAY,QAAQ;IACpD,MAAM,gBACJ,OAAO,cAAc,YAAY,YAAY,SAAS,aAClD,YACC,gBAAA,SAAS,WAAW,WAAW,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI;IACjE,IAAI,CAAC,eAAe;KAClB,IAAI,OAAO,gBAAA,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;IACA,YAAY,SAAS,YAAY,SAAS;GAC5C;GACA,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GAClE,cAAc,kBAAA,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,qBAAA,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,qBAAA,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,gBAAA,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
@@ -1,24 +1,18 @@
1
- import { addScope, deleteProperties, isVersionValid } from "../web-utils.mjs";
1
+ import { deleteProperties, resolveVersion } from "../web-utils.mjs";
2
2
  import { addGravatarSupport, formatAuthor } from "../author-utils.mjs";
3
+ import { scopedPackageAccess } from "./scoped-access.mjs";
3
4
  import buildDebug from "debug";
4
5
  import { Router } from "express";
5
- import { DIST_TAGS, HTTP_STATUS, reqUtils } from "@verdaccio/core";
6
- import { logger } from "@verdaccio/logger";
7
- import { WebUrls, allow, getRequestOptions } from "@verdaccio/middleware";
6
+ import { DIST_TAGS, HTTP_STATUS, pkgUtils } from "@verdaccio/core";
7
+ import { WebUrls, getRequestOptions } from "@verdaccio/middleware";
8
8
  import { convertDistRemoteToLocalTarballUrls } from "@verdaccio/tarball";
9
9
  //#region src/api/sidebar.ts
10
10
  var debug = buildDebug("verdaccio:web:api:sidebar");
11
11
  function addSidebarWebApi(config, storage, auth) {
12
12
  debug("initialized sidebar web api");
13
13
  const router = Router();
14
- const can = allow(auth, {
15
- beforeAll: (a, b) => logger.trace(a, b),
16
- afterAll: (a, b) => logger.trace(a, b)
17
- });
18
- router.get([WebUrls.sidebar_scopped_package, WebUrls.sidebar_package], can("access"), async function(req, res, next) {
19
- const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, "");
20
- const packageName = reqUtils.paramToString(req.params.package);
21
- const name = scope ? addScope(scope, packageName) : packageName;
14
+ router.get([WebUrls.sidebar_scopped_package, WebUrls.sidebar_package], scopedPackageAccess(auth, config), async function(req, res, next) {
15
+ const name = req.scopedPackageName;
22
16
  const requestOptions = getRequestOptions(req);
23
17
  try {
24
18
  const info = await storage.getPackageByOptions({
@@ -28,15 +22,31 @@ function addSidebarWebApi(config, storage, auth) {
28
22
  requestOptions
29
23
  });
30
24
  const { v } = req.query;
25
+ let requestedVersion;
26
+ if (typeof v === "string") {
27
+ requestedVersion = resolveVersion(info, v);
28
+ if (!requestedVersion) {
29
+ debug("version %o not found for %o", v, name);
30
+ res.status(HTTP_STATUS.NOT_FOUND);
31
+ res.end();
32
+ return;
33
+ }
34
+ }
31
35
  let sideBarInfo = { ...info };
32
36
  sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(info, requestOptions, config.url_prefix).versions;
33
- if (typeof v === "string" && isVersionValid(info, v)) {
34
- sideBarInfo.latest = sideBarInfo.versions[v];
35
- sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
36
- } else {
37
- sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];
38
- sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
37
+ if (requestedVersion) sideBarInfo.latest = sideBarInfo.versions[requestedVersion];
38
+ else {
39
+ const latestTag = info[DIST_TAGS]?.latest;
40
+ const versionKeys = Object.keys(sideBarInfo.versions);
41
+ const latestVersion = typeof latestTag === "string" && sideBarInfo.versions[latestTag] ? latestTag : pkgUtils.semverSort(versionKeys).pop() ?? versionKeys.pop();
42
+ if (!latestVersion) {
43
+ res.status(HTTP_STATUS.NOT_FOUND);
44
+ res.end();
45
+ return;
46
+ }
47
+ sideBarInfo.latest = sideBarInfo.versions[latestVersion];
39
48
  }
49
+ sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
40
50
  sideBarInfo = deleteProperties([
41
51
  "readme",
42
52
  "_attachments",
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.mjs","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;AAuBA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,MAAM,6BAA6B;CACnC,MAAM,SAAS,OAAO;CACtB,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,QAAQ,yBAAyB,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,QAAQ,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,WAAW,oCACrB,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,OAAO,MAAM,YAAY,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,UAAU,CAAC;IAC1D,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"sidebar.mjs","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, pkgUtils } from '@verdaccio/core';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { deleteProperties, resolveVersion } from '../web-utils';\nimport { scopedPackageAccess } from './scoped-access';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n scopedPackageAccess(auth, config),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const name = (req as $RequestExtend & { scopedPackageName: string }).scopedPackageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n const { v } = req.query;\n // `v` may be a version or a dist-tag; anything else is a 404\n let requestedVersion: string | undefined;\n if (typeof v === 'string') {\n requestedVersion = resolveVersion(info, v);\n if (!requestedVersion) {\n debug('version %o not found for %o', v, name);\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n }\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (requestedVersion) {\n sideBarInfo.latest = sideBarInfo.versions[requestedVersion];\n } else {\n // a manifest without a resolvable dist-tags.latest (corrupt or partial\n // uplink data) used to crash here and surface as a 404 for a package\n // that exists; fall back to the highest available version — semver\n // sorted, since key insertion order reflects publish order, and if\n // every key is invalid semver (fully corrupt manifest), last key wins\n const latestTag = info[DIST_TAGS]?.latest;\n const versionKeys = Object.keys(sideBarInfo.versions);\n const latestVersion =\n typeof latestTag === 'string' && sideBarInfo.versions[latestTag]\n ? latestTag\n : (pkgUtils.semverSort(versionKeys).pop() ?? versionKeys.pop());\n if (!latestVersion) {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n return;\n }\n sideBarInfo.latest = sideBarInfo.versions[latestVersion];\n }\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;AAsBA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,MAAM,6BAA6B;CACnC,MAAM,SAAS,OAAO;CAEtB,OAAO,IACL,CAAC,QAAQ,yBAAyB,QAAQ,eAAe,GACzD,oBAAoB,MAAM,MAAM,GAChC,eACE,KACA,KACA,MACe;EACf,MAAM,OAAQ,IAAuD;EACrE,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GACD,MAAM,EAAE,MAAM,IAAI;GAElB,IAAI;GACJ,IAAI,OAAO,MAAM,UAAU;IACzB,mBAAmB,eAAe,MAAM,CAAC;IACzC,IAAI,CAAC,kBAAkB;KACrB,MAAM,+BAA+B,GAAG,IAAI;KAC5C,IAAI,OAAO,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;GACF;GACA,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,WAAW,oCACrB,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,kBACF,YAAY,SAAS,YAAY,SAAS;QACrC;IAML,MAAM,YAAY,KAAK,UAAU,EAAE;IACnC,MAAM,cAAc,OAAO,KAAK,YAAY,QAAQ;IACpD,MAAM,gBACJ,OAAO,cAAc,YAAY,YAAY,SAAS,aAClD,YACC,SAAS,WAAW,WAAW,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI;IACjE,IAAI,CAAC,eAAe;KAClB,IAAI,OAAO,YAAY,SAAS;KAChC,IAAI,IAAI;KACR;IACF;IACA,YAAY,SAAS,YAAY,SAAS;GAC5C;GACA,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GAClE,cAAc,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { type Auth } from '@verdaccio/auth';
2
+ import type { Auth } from '@verdaccio/auth';
3
3
  import type { Config } from '@verdaccio/types';
4
4
  declare function addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router;
5
5
  export default addUserAuthApi;
package/build/api/user.js CHANGED
@@ -5,7 +5,6 @@ let express = require("express");
5
5
  let lodash_es = require("lodash-es");
6
6
  let _verdaccio_core = require("@verdaccio/core");
7
7
  let _verdaccio_middleware = require("@verdaccio/middleware");
8
- let _verdaccio_auth = require("@verdaccio/auth");
9
8
  //#region src/api/user.ts
10
9
  var debug$1 = (0, debug.default)("verdaccio:web:api:user");
11
10
  var WEB_LOGIN_SESSION_ID = "web-login-sessionId";
@@ -68,8 +67,9 @@ function addUserAuthApi(auth, config, storage) {
68
67
  res.json({ token: tokens[0].token });
69
68
  return;
70
69
  }
71
- const token = name && password ? await (0, _verdaccio_auth.getApiToken)(auth, config, user, password) : void 0;
72
- if (token) {
70
+ if (name && password) {
71
+ const jWTSignOptions = config.security.web.sign;
72
+ const token = await auth.jwtEncrypt(user, jWTSignOptions);
73
73
  debug$1("adduser: new token %o", _verdaccio_core.cryptoUtils.mask(token, 4));
74
74
  return next({
75
75
  token,
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.js","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n // the UI stores this token as its web session: it must be the web\n // JWT the login endpoint issues, not the opaque npm API token\n if (name && password) {\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n const token = await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions);\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
@@ -3,7 +3,6 @@ import { Router } from "express";
3
3
  import { isNil } from "lodash-es";
4
4
  import { API_ERROR, APP_ERROR, HEADERS, HTTP_STATUS, TOKEN_BEARER, cryptoUtils, errorUtils, validationUtils } from "@verdaccio/core";
5
5
  import { WebUrls, rateLimit } from "@verdaccio/middleware";
6
- import { getApiToken } from "@verdaccio/auth";
7
6
  //#region src/api/user.ts
8
7
  var debug = buildDebug("verdaccio:web:api:user");
9
8
  var WEB_LOGIN_SESSION_ID = "web-login-sessionId";
@@ -66,8 +65,9 @@ function addUserAuthApi(auth, config, storage) {
66
65
  res.json({ token: tokens[0].token });
67
66
  return;
68
67
  }
69
- const token = name && password ? await getApiToken(auth, config, user, password) : void 0;
70
- if (token) {
68
+ if (name && password) {
69
+ const jWTSignOptions = config.security.web.sign;
70
+ const token = await auth.jwtEncrypt(user, jWTSignOptions);
71
71
  debug("adduser: new token %o", cryptoUtils.mask(token, 4));
72
72
  return next({
73
73
  token,
@@ -1 +1 @@
1
- {"version":3,"file":"user.mjs","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.mjs","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n // the UI stores this token as its web session: it must be the web\n // JWT the login endpoint issues, not the opaque npm API token\n if (name && password) {\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n const token = await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions);\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
package/build/index.js CHANGED
@@ -15,5 +15,6 @@ exports.formatAuthor = require_author_utils.formatAuthor;
15
15
  exports.generateGravatarUrl = require_author_utils.generateGravatarUrl;
16
16
  exports.hasLogin = require_web_utils.hasLogin;
17
17
  exports.isVersionValid = require_web_utils.isVersionValid;
18
+ exports.resolveVersion = require_web_utils.resolveVersion;
18
19
  exports.sortByName = require_web_utils.sortByName;
19
20
  exports.sortByTime = require_web_utils.sortByTime;
package/build/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { addScope, deleteProperties, hasLogin, isVersionValid, sortByName, sortByTime } from "./web-utils.mjs";
1
+ import { addScope, deleteProperties, hasLogin, isVersionValid, resolveVersion, sortByName, sortByTime } from "./web-utils.mjs";
2
2
  import { GENERIC_AVATAR, addGravatarSupport, formatAuthor, generateGravatarUrl } from "./author-utils.mjs";
3
3
  import middleware_default, { DEFAULT_PLUGIN_UI_THEME } from "./middleware.mjs";
4
- export { DEFAULT_PLUGIN_UI_THEME, GENERIC_AVATAR, addGravatarSupport, addScope, middleware_default as default, deleteProperties, formatAuthor, generateGravatarUrl, hasLogin, isVersionValid, sortByName, sortByTime };
4
+ export { DEFAULT_PLUGIN_UI_THEME, GENERIC_AVATAR, addGravatarSupport, addScope, middleware_default as default, deleteProperties, formatAuthor, generateGravatarUrl, hasLogin, isVersionValid, resolveVersion, sortByName, sortByTime };
@@ -5,3 +5,9 @@ export declare function sortByTime(packages: any[], orderAscending?: boolean | v
5
5
  export declare function addScope(scope: string, packageName: string): string;
6
6
  export declare function deleteProperties(propertiesToDelete: string[], objectItem: any): any;
7
7
  export declare function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean;
8
+ /**
9
+ * Resolve a user-supplied version or dist-tag to a concrete version of the
10
+ * manifest, or undefined. Own-property checks only: `__proto__` must never
11
+ * resolve against inherited properties.
12
+ */
13
+ export declare function resolveVersion(packageMeta: Manifest, version: string): string | undefined;
@@ -1,4 +1,5 @@
1
1
  let lodash_es = require("lodash-es");
2
+ let _verdaccio_core = require("@verdaccio/core");
2
3
  //#region src/web-utils.ts
3
4
  function hasLogin(config) {
4
5
  return (0, lodash_es.isNil)(config?.web?.login) || config?.web?.login === true;
@@ -26,13 +27,27 @@ function deleteProperties(propertiesToDelete, objectItem) {
26
27
  }
27
28
  function isVersionValid(packageMeta, packageVersion) {
28
29
  if (!(typeof packageVersion !== "undefined")) return false;
29
- return Object.keys(packageMeta.versions).includes(packageVersion);
30
+ return Object.hasOwn(packageMeta.versions, packageVersion);
31
+ }
32
+ /**
33
+ * Resolve a user-supplied version or dist-tag to a concrete version of the
34
+ * manifest, or undefined. Own-property checks only: `__proto__` must never
35
+ * resolve against inherited properties.
36
+ */
37
+ function resolveVersion(packageMeta, version) {
38
+ if (isVersionValid(packageMeta, version)) return version;
39
+ const distTags = packageMeta[_verdaccio_core.DIST_TAGS];
40
+ if (distTags && Object.hasOwn(distTags, version)) {
41
+ const resolved = distTags[version];
42
+ if (typeof resolved === "string" && isVersionValid(packageMeta, resolved)) return resolved;
43
+ }
30
44
  }
31
45
  //#endregion
32
46
  exports.addScope = addScope;
33
47
  exports.deleteProperties = deleteProperties;
34
48
  exports.hasLogin = hasLogin;
35
49
  exports.isVersionValid = isVersionValid;
50
+ exports.resolveVersion = resolveVersion;
36
51
  exports.sortByName = sortByName;
37
52
  exports.sortByTime = sortByTime;
38
53
 
@@ -1 +1 @@
1
- {"version":3,"file":"web-utils.js","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,QAAA,GAAO,UAAA,MAAA,CAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,CAAA,GAAA,UAAA,QAAA,CAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,CAAC,CAAC,SAAS,cAC5D;AACT"}
1
+ {"version":3,"file":"web-utils.js","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport { DIST_TAGS } from '@verdaccio/core';\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n // own-property check: the version may come from user input, and values like\n // `__proto__` must never validate against inherited properties\n return Object.hasOwn(packageMeta.versions, packageVersion);\n}\n\n/**\n * Resolve a user-supplied version or dist-tag to a concrete version of the\n * manifest, or undefined. Own-property checks only: `__proto__` must never\n * resolve against inherited properties.\n */\nexport function resolveVersion(packageMeta: Manifest, version: string): string | undefined {\n if (isVersionValid(packageMeta, version)) {\n return version;\n }\n\n const distTags = packageMeta[DIST_TAGS];\n if (distTags && Object.hasOwn(distTags, version)) {\n const resolved = distTags[version];\n if (typeof resolved === 'string' && isVersionValid(packageMeta, resolved)) {\n return resolved;\n }\n }\n\n return undefined;\n}\n"],"mappings":";;;AAKA,SAAgB,SAAS,QAAoB;CAC3C,QAAA,GAAO,UAAA,MAAA,CAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,CAAA,GAAA,UAAA,QAAA,CAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAKT,OAAO,OAAO,OAAO,YAAY,UAAU,cAAc;AAC3D;;;;;;AAOA,SAAgB,eAAe,aAAuB,SAAqC;CACzF,IAAI,eAAe,aAAa,OAAO,GACrC,OAAO;CAGT,MAAM,WAAW,YAAY,gBAAA;CAC7B,IAAI,YAAY,OAAO,OAAO,UAAU,OAAO,GAAG;EAChD,MAAM,WAAW,SAAS;EAC1B,IAAI,OAAO,aAAa,YAAY,eAAe,aAAa,QAAQ,GACtE,OAAO;CAEX;AAGF"}
@@ -1,4 +1,5 @@
1
1
  import { forEach, isNil } from "lodash-es";
2
+ import { DIST_TAGS } from "@verdaccio/core";
2
3
  //#region src/web-utils.ts
3
4
  function hasLogin(config) {
4
5
  return isNil(config?.web?.login) || config?.web?.login === true;
@@ -26,9 +27,22 @@ function deleteProperties(propertiesToDelete, objectItem) {
26
27
  }
27
28
  function isVersionValid(packageMeta, packageVersion) {
28
29
  if (!(typeof packageVersion !== "undefined")) return false;
29
- return Object.keys(packageMeta.versions).includes(packageVersion);
30
+ return Object.hasOwn(packageMeta.versions, packageVersion);
31
+ }
32
+ /**
33
+ * Resolve a user-supplied version or dist-tag to a concrete version of the
34
+ * manifest, or undefined. Own-property checks only: `__proto__` must never
35
+ * resolve against inherited properties.
36
+ */
37
+ function resolveVersion(packageMeta, version) {
38
+ if (isVersionValid(packageMeta, version)) return version;
39
+ const distTags = packageMeta[DIST_TAGS];
40
+ if (distTags && Object.hasOwn(distTags, version)) {
41
+ const resolved = distTags[version];
42
+ if (typeof resolved === "string" && isVersionValid(packageMeta, resolved)) return resolved;
43
+ }
30
44
  }
31
45
  //#endregion
32
- export { addScope, deleteProperties, hasLogin, isVersionValid, sortByName, sortByTime };
46
+ export { addScope, deleteProperties, hasLogin, isVersionValid, resolveVersion, sortByName, sortByTime };
33
47
 
34
48
  //# sourceMappingURL=web-utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-utils.mjs","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,OAAO,MAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,QAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,CAAC,CAAC,SAAS,cAC5D;AACT"}
1
+ {"version":3,"file":"web-utils.mjs","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport { DIST_TAGS } from '@verdaccio/core';\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n // own-property check: the version may come from user input, and values like\n // `__proto__` must never validate against inherited properties\n return Object.hasOwn(packageMeta.versions, packageVersion);\n}\n\n/**\n * Resolve a user-supplied version or dist-tag to a concrete version of the\n * manifest, or undefined. Own-property checks only: `__proto__` must never\n * resolve against inherited properties.\n */\nexport function resolveVersion(packageMeta: Manifest, version: string): string | undefined {\n if (isVersionValid(packageMeta, version)) {\n return version;\n }\n\n const distTags = packageMeta[DIST_TAGS];\n if (distTags && Object.hasOwn(distTags, version)) {\n const resolved = distTags[version];\n if (typeof resolved === 'string' && isVersionValid(packageMeta, resolved)) {\n return resolved;\n }\n }\n\n return undefined;\n}\n"],"mappings":";;;AAKA,SAAgB,SAAS,QAAoB;CAC3C,OAAO,MAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,QAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAKT,OAAO,OAAO,OAAO,YAAY,UAAU,cAAc;AAC3D;;;;;;AAOA,SAAgB,eAAe,aAAuB,SAAqC;CACzF,IAAI,eAAe,aAAa,OAAO,GACrC,OAAO;CAGT,MAAM,WAAW,YAAY;CAC7B,IAAI,YAAY,OAAO,OAAO,UAAU,OAAO,GAAG;EAChD,MAAM,WAAW,SAAS;EAC1B,IAAI,OAAO,aAAa,YAAY,eAAe,aAAa,QAAQ,GACtE,OAAO;CAEX;AAGF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/web",
3
- "version": "9.0.0-next-9.29",
3
+ "version": "9.0.0-next-9.31",
4
4
  "description": "Verdaccio Web Middleware",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -28,29 +28,29 @@
28
28
  },
29
29
  "license": "MIT",
30
30
  "dependencies": {
31
- "@verdaccio/auth": "9.0.0-next-9.29",
32
- "@verdaccio/config": "9.0.0-next-9.29",
33
- "@verdaccio/core": "9.0.0-next-9.29",
34
- "@verdaccio/loaders": "9.0.0-next-9.29",
35
- "@verdaccio/logger": "9.0.0-next-9.29",
36
- "@verdaccio/middleware": "9.0.0-next-9.29",
37
- "@verdaccio/store": "9.0.0-next-9.29",
38
- "@verdaccio/tarball": "14.0.0-next-9.29",
39
- "@verdaccio/ui-theme": "9.0.0-next-9.29",
31
+ "@verdaccio/auth": "9.0.0-next-9.31",
32
+ "@verdaccio/config": "9.0.0-next-9.31",
33
+ "@verdaccio/core": "9.0.0-next-9.31",
34
+ "@verdaccio/loaders": "9.0.0-next-9.31",
35
+ "@verdaccio/logger": "9.0.0-next-9.31",
36
+ "@verdaccio/middleware": "9.0.0-next-9.31",
37
+ "@verdaccio/store": "9.0.0-next-9.31",
38
+ "@verdaccio/tarball": "14.0.0-next-9.31",
39
+ "@verdaccio/ui-theme": "9.0.0-next-9.31",
40
40
  "debug": "4.4.3",
41
41
  "express": "5.2.1",
42
42
  "lodash-es": "4.18.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/lodash-es": "4.17.12",
46
- "@verdaccio/api": "9.0.0-next-9.29",
47
- "@verdaccio/test-helper": "5.0.0-next-9.30",
48
- "@verdaccio/types": "14.0.0-next-9.13",
46
+ "@verdaccio/api": "9.0.0-next-9.31",
47
+ "@verdaccio/test-helper": "5.0.0-next-9.32",
48
+ "@verdaccio/types": "14.0.0-next-9.14",
49
49
  "jsdom": "29.1.1",
50
50
  "nock": "13.5.6",
51
51
  "supertest": "7.1.4",
52
52
  "rimraf": "6.1.3",
53
- "vite": "8.2.1",
53
+ "vite": "8.2.2",
54
54
  "vitest": "4.1.10"
55
55
  },
56
56
  "funding": {