@verdaccio/api 9.0.0-next-9.27 → 9.0.0-next-9.29
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.
- package/build/dist-tags.d.ts +4 -2
- package/build/dist-tags.js +4 -4
- package/build/dist-tags.js.map +1 -1
- package/build/dist-tags.mjs +4 -4
- package/build/dist-tags.mjs.map +1 -1
- package/build/index.js +30 -5
- package/build/index.js.map +1 -1
- package/build/index.mjs +30 -5
- package/build/index.mjs.map +1 -1
- package/build/publish.d.ts +4 -2
- package/build/publish.js +5 -5
- package/build/publish.js.map +1 -1
- package/build/publish.mjs +5 -5
- package/build/publish.mjs.map +1 -1
- package/build/require-otp.d.ts +55 -0
- package/build/require-otp.js +92 -0
- package/build/require-otp.js.map +1 -0
- package/build/require-otp.mjs +88 -0
- package/build/require-otp.mjs.map +1 -0
- package/build/stage.d.ts +17 -0
- package/build/stage.js +263 -0
- package/build/stage.js.map +1 -0
- package/build/stage.mjs +261 -0
- package/build/stage.mjs.map +1 -0
- package/build/user.d.ts +4 -2
- package/build/user.js +20 -14
- package/build/user.js.map +1 -1
- package/build/user.mjs +20 -14
- package/build/user.mjs.map +1 -1
- package/build/v1/login.js +1 -1
- package/build/v1/login.mjs +1 -1
- package/build/v1/profile.d.ts +12 -5
- package/build/v1/profile.js +65 -10
- package/build/v1/profile.js.map +1 -1
- package/build/v1/profile.mjs +65 -10
- package/build/v1/profile.mjs.map +1 -1
- package/build/v1/token.d.ts +4 -2
- package/build/v1/token.js +11 -4
- package/build/v1/token.js.map +1 -1
- package/build/v1/token.mjs +11 -4
- package/build/v1/token.mjs.map +1 -1
- package/package.json +11 -10
package/build/dist-tags.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { Router } from 'express';
|
|
1
|
+
import type { RequestHandler, Router } from 'express';
|
|
2
2
|
import type { Auth } from '@verdaccio/auth';
|
|
3
3
|
import type { Storage } from '@verdaccio/store';
|
|
4
4
|
import type { Logger } from '@verdaccio/types';
|
|
5
|
-
export default function (route: Router, auth: Auth, storage: Storage, logger: Logger
|
|
5
|
+
export default function (route: Router, auth: Auth, storage: Storage, logger: Logger,
|
|
6
|
+
/** No-op unless the caller has two-factor enabled for this operation. */
|
|
7
|
+
requireOtp?: RequestHandler): void;
|
package/build/dist-tags.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
let _verdaccio_middleware = require("@verdaccio/middleware");
|
|
2
2
|
let _verdaccio_core = require("@verdaccio/core");
|
|
3
3
|
//#region src/dist-tags.ts
|
|
4
|
-
function dist_tags_default(route, auth, storage, logger) {
|
|
4
|
+
function dist_tags_default(route, auth, storage, logger, requireOtp = (_req, _res, next) => next()) {
|
|
5
5
|
const can = (0, _verdaccio_middleware.allow)(auth, {
|
|
6
6
|
beforeAll: (a, b) => logger.trace(a, b),
|
|
7
7
|
afterAll: (a, b) => logger.trace(a, b)
|
|
@@ -20,9 +20,9 @@ function dist_tags_default(route, auth, storage, logger) {
|
|
|
20
20
|
next(err);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
route.put(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging, can("publish"), (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
24
|
-
route.put(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
25
|
-
route.delete(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), async function(req, res, next) {
|
|
23
|
+
route.put(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging, can("publish"), requireOtp, (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
24
|
+
route.put(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), requireOtp, (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
25
|
+
route.delete(_verdaccio_middleware.DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), requireOtp, async function(req, res, next) {
|
|
26
26
|
const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
|
|
27
27
|
const tag = _verdaccio_core.reqUtils.paramToString(req.params.tag);
|
|
28
28
|
const tags = {};
|
package/build/dist-tags.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dist-tags.js","names":[],"sources":["../src/dist-tags.ts"],"sourcesContent":["import type { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { HEADERS, constants, errorUtils, reqUtils } from '@verdaccio/core';\nimport { DIST_TAGS_API_ENDPOINTS, allow, getRequestOptions, media } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Logger } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nexport default function (route: Router
|
|
1
|
+
{"version":3,"file":"dist-tags.js","names":[],"sources":["../src/dist-tags.ts"],"sourcesContent":["import type { RequestHandler, Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { HEADERS, constants, errorUtils, reqUtils } from '@verdaccio/core';\nimport { DIST_TAGS_API_ENDPOINTS, allow, getRequestOptions, media } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Logger } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n logger: Logger,\n /** No-op unless the caller has two-factor enabled for this operation. */\n requireOtp: RequestHandler = (_req, _res, next) => next()\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const addTagPackageVersionMiddleware = async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<$NextFunctionVer> {\n if (typeof req.body !== 'string') {\n return next(errorUtils.getBadRequest('version is missing'));\n }\n const packageName = reqUtils.paramToString(req.params.package);\n const tag = reqUtils.paramToString(req.params.tag);\n const tags = {};\n tags[tag] = req.body;\n try {\n await storage.mergeTagsNext(packageName, tags);\n res.status(constants.HTTP_STATUS.CREATED);\n return next({\n ok: constants.API_MESSAGE.TAG_ADDED,\n });\n } catch (err) {\n next(err);\n }\n };\n\n // tagging a package.\n route.put(\n DIST_TAGS_API_ENDPOINTS.tagging,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n addTagPackageVersionMiddleware\n );\n\n route.put(\n DIST_TAGS_API_ENDPOINTS.tagging_package,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n addTagPackageVersionMiddleware\n );\n\n route.delete(\n DIST_TAGS_API_ENDPOINTS.tagging_package,\n can('publish'),\n requireOtp,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const tag = reqUtils.paramToString(req.params.tag);\n const tags = {};\n tags[tag] = null;\n try {\n await storage.mergeTagsNext(packageName, tags);\n res.status(constants.HTTP_STATUS.CREATED);\n return next({\n ok: constants.API_MESSAGE.TAG_REMOVED,\n });\n } catch (err) {\n next(err);\n }\n }\n );\n\n route.get(\n DIST_TAGS_API_ENDPOINTS.get_dist_tags,\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = await storage.getPackageByOptions({\n name: packageName,\n uplinksLook: true,\n requestOptions,\n });\n next(manifest[constants.DIST_TAGS]);\n } catch (err) {\n next(err);\n }\n }\n );\n}\n"],"mappings":";;;AAUA,SAAA,kBACE,OACA,MACA,SACA,QAEA,cAA8B,MAAM,MAAM,SAAS,KAAK,GAClD;CACN,MAAM,OAAA,GAAM,sBAAA,MAAA,CAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,iCAAiC,eACrC,KACA,KACA,MAC2B;EAC3B,IAAI,OAAO,IAAI,SAAS,UACtB,OAAO,KAAK,gBAAA,WAAW,cAAc,oBAAoB,CAAC;EAE5D,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,MAAM,gBAAA,SAAS,cAAc,IAAI,OAAO,GAAG;EACjD,MAAM,OAAO,CAAC;EACd,KAAK,OAAO,IAAI;EAChB,IAAI;GACF,MAAM,QAAQ,cAAc,aAAa,IAAI;GAC7C,IAAI,OAAO,gBAAA,UAAU,YAAY,OAAO;GACxC,OAAO,KAAK,EACV,IAAI,gBAAA,UAAU,YAAY,UAC5B,CAAC;EACH,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF;CAGA,MAAM,IACJ,sBAAA,wBAAwB,SACxB,IAAI,SAAS,GACb,aAAA,GACA,sBAAA,MAAA,CAAM,gBAAA,QAAQ,IAAI,GAClB,8BACF;CAEA,MAAM,IACJ,sBAAA,wBAAwB,iBACxB,IAAI,SAAS,GACb,aAAA,GACA,sBAAA,MAAA,CAAM,gBAAA,QAAQ,IAAI,GAClB,8BACF;CAEA,MAAM,OACJ,sBAAA,wBAAwB,iBACxB,IAAI,SAAS,GACb,YACA,eACE,KACA,KACA,MACe;EACf,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,MAAM,gBAAA,SAAS,cAAc,IAAI,OAAO,GAAG;EACjD,MAAM,OAAO,CAAC;EACd,KAAK,OAAO;EACZ,IAAI;GACF,MAAM,QAAQ,cAAc,aAAa,IAAI;GAC7C,IAAI,OAAO,gBAAA,UAAU,YAAY,OAAO;GACxC,OAAO,KAAK,EACV,IAAI,gBAAA,UAAU,YAAY,YAC5B,CAAC;EACH,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CAEA,MAAM,IACJ,sBAAA,wBAAwB,eACxB,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,kBAAA,GAAiB,sBAAA,kBAAA,CAAkB,GAAG;EAC5C,IAAI;GAMF,MAAK,MALkB,QAAQ,oBAAoB;IACjD,MAAM;IACN,aAAa;IACb;GACF,CAAC,EAAA,CACa,gBAAA,UAAU,UAAU;EACpC,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;AACF"}
|
package/build/dist-tags.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DIST_TAGS_API_ENDPOINTS, allow, getRequestOptions, media } from "@verdaccio/middleware";
|
|
2
2
|
import { HEADERS, constants, errorUtils, reqUtils } from "@verdaccio/core";
|
|
3
3
|
//#region src/dist-tags.ts
|
|
4
|
-
function dist_tags_default(route, auth, storage, logger) {
|
|
4
|
+
function dist_tags_default(route, auth, storage, logger, requireOtp = (_req, _res, next) => next()) {
|
|
5
5
|
const can = allow(auth, {
|
|
6
6
|
beforeAll: (a, b) => logger.trace(a, b),
|
|
7
7
|
afterAll: (a, b) => logger.trace(a, b)
|
|
@@ -20,9 +20,9 @@ function dist_tags_default(route, auth, storage, logger) {
|
|
|
20
20
|
next(err);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
route.put(DIST_TAGS_API_ENDPOINTS.tagging, can("publish"), media(HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
24
|
-
route.put(DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), media(HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
25
|
-
route.delete(DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), async function(req, res, next) {
|
|
23
|
+
route.put(DIST_TAGS_API_ENDPOINTS.tagging, can("publish"), requireOtp, media(HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
24
|
+
route.put(DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), requireOtp, media(HEADERS.JSON), addTagPackageVersionMiddleware);
|
|
25
|
+
route.delete(DIST_TAGS_API_ENDPOINTS.tagging_package, can("publish"), requireOtp, async function(req, res, next) {
|
|
26
26
|
const packageName = reqUtils.paramToString(req.params.package);
|
|
27
27
|
const tag = reqUtils.paramToString(req.params.tag);
|
|
28
28
|
const tags = {};
|
package/build/dist-tags.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dist-tags.mjs","names":[],"sources":["../src/dist-tags.ts"],"sourcesContent":["import type { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { HEADERS, constants, errorUtils, reqUtils } from '@verdaccio/core';\nimport { DIST_TAGS_API_ENDPOINTS, allow, getRequestOptions, media } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Logger } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nexport default function (route: Router
|
|
1
|
+
{"version":3,"file":"dist-tags.mjs","names":[],"sources":["../src/dist-tags.ts"],"sourcesContent":["import type { RequestHandler, Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { HEADERS, constants, errorUtils, reqUtils } from '@verdaccio/core';\nimport { DIST_TAGS_API_ENDPOINTS, allow, getRequestOptions, media } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Logger } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nexport default function (\n route: Router,\n auth: Auth,\n storage: Storage,\n logger: Logger,\n /** No-op unless the caller has two-factor enabled for this operation. */\n requireOtp: RequestHandler = (_req, _res, next) => next()\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const addTagPackageVersionMiddleware = async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<$NextFunctionVer> {\n if (typeof req.body !== 'string') {\n return next(errorUtils.getBadRequest('version is missing'));\n }\n const packageName = reqUtils.paramToString(req.params.package);\n const tag = reqUtils.paramToString(req.params.tag);\n const tags = {};\n tags[tag] = req.body;\n try {\n await storage.mergeTagsNext(packageName, tags);\n res.status(constants.HTTP_STATUS.CREATED);\n return next({\n ok: constants.API_MESSAGE.TAG_ADDED,\n });\n } catch (err) {\n next(err);\n }\n };\n\n // tagging a package.\n route.put(\n DIST_TAGS_API_ENDPOINTS.tagging,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n addTagPackageVersionMiddleware\n );\n\n route.put(\n DIST_TAGS_API_ENDPOINTS.tagging_package,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n addTagPackageVersionMiddleware\n );\n\n route.delete(\n DIST_TAGS_API_ENDPOINTS.tagging_package,\n can('publish'),\n requireOtp,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const tag = reqUtils.paramToString(req.params.tag);\n const tags = {};\n tags[tag] = null;\n try {\n await storage.mergeTagsNext(packageName, tags);\n res.status(constants.HTTP_STATUS.CREATED);\n return next({\n ok: constants.API_MESSAGE.TAG_REMOVED,\n });\n } catch (err) {\n next(err);\n }\n }\n );\n\n route.get(\n DIST_TAGS_API_ENDPOINTS.get_dist_tags,\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = await storage.getPackageByOptions({\n name: packageName,\n uplinksLook: true,\n requestOptions,\n });\n next(manifest[constants.DIST_TAGS]);\n } catch (err) {\n next(err);\n }\n }\n );\n}\n"],"mappings":";;;AAUA,SAAA,kBACE,OACA,MACA,SACA,QAEA,cAA8B,MAAM,MAAM,SAAS,KAAK,GAClD;CACN,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,iCAAiC,eACrC,KACA,KACA,MAC2B;EAC3B,IAAI,OAAO,IAAI,SAAS,UACtB,OAAO,KAAK,WAAW,cAAc,oBAAoB,CAAC;EAE5D,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,MAAM,SAAS,cAAc,IAAI,OAAO,GAAG;EACjD,MAAM,OAAO,CAAC;EACd,KAAK,OAAO,IAAI;EAChB,IAAI;GACF,MAAM,QAAQ,cAAc,aAAa,IAAI;GAC7C,IAAI,OAAO,UAAU,YAAY,OAAO;GACxC,OAAO,KAAK,EACV,IAAI,UAAU,YAAY,UAC5B,CAAC;EACH,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF;CAGA,MAAM,IACJ,wBAAwB,SACxB,IAAI,SAAS,GACb,YACA,MAAM,QAAQ,IAAI,GAClB,8BACF;CAEA,MAAM,IACJ,wBAAwB,iBACxB,IAAI,SAAS,GACb,YACA,MAAM,QAAQ,IAAI,GAClB,8BACF;CAEA,MAAM,OACJ,wBAAwB,iBACxB,IAAI,SAAS,GACb,YACA,eACE,KACA,KACA,MACe;EACf,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,MAAM,SAAS,cAAc,IAAI,OAAO,GAAG;EACjD,MAAM,OAAO,CAAC;EACd,KAAK,OAAO;EACZ,IAAI;GACF,MAAM,QAAQ,cAAc,aAAa,IAAI;GAC7C,IAAI,OAAO,UAAU,YAAY,OAAO;GACxC,OAAO,KAAK,EACV,IAAI,UAAU,YAAY,YAC5B,CAAC;EACH,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CAEA,MAAM,IACJ,wBAAwB,eACxB,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GAMF,MAAK,MALkB,QAAQ,oBAAoB;IACjD,MAAM;IACN,aAAa;IACb;GACF,CAAC,EAAA,CACa,UAAU,UAAU;EACpC,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;AACF"}
|
package/build/index.js
CHANGED
|
@@ -7,7 +7,9 @@ const require_dist_tags = require("./dist-tags.js");
|
|
|
7
7
|
const require_package = require("./package.js");
|
|
8
8
|
const require_ping = require("./ping.js");
|
|
9
9
|
const require_publish = require("./publish.js");
|
|
10
|
+
const require_require_otp = require("./require-otp.js");
|
|
10
11
|
const require_search = require("./search.js");
|
|
12
|
+
const require_stage = require("./stage.js");
|
|
11
13
|
const require_user = require("./user.js");
|
|
12
14
|
const require_login = require("./v1/login.js");
|
|
13
15
|
const require_profile = require("./v1/profile.js");
|
|
@@ -17,6 +19,7 @@ const require_whoami = require("./whoami.js");
|
|
|
17
19
|
let express = require("express");
|
|
18
20
|
express = require_runtime.__toESM(express);
|
|
19
21
|
let _verdaccio_middleware = require("@verdaccio/middleware");
|
|
22
|
+
let _verdaccio_auth = require("@verdaccio/auth");
|
|
20
23
|
//#region src/index.ts
|
|
21
24
|
function src_default(config, auth, storage, logger) {
|
|
22
25
|
const app = express.default.Router();
|
|
@@ -35,19 +38,41 @@ function src_default(config, auth, storage, logger) {
|
|
|
35
38
|
if (req.remote_user ?? res.locals.remote_user) return next();
|
|
36
39
|
return apiJwtMiddleware(req, res, next);
|
|
37
40
|
});
|
|
41
|
+
let tfaStore;
|
|
42
|
+
if (config.flags?.tfa) {
|
|
43
|
+
require_require_otp.assertTokenStoreSupport(storage, logger);
|
|
44
|
+
tfaStore = new _verdaccio_auth.TfaStore(storage, config.secret, logger);
|
|
45
|
+
}
|
|
46
|
+
const otpForAuth = require_require_otp.requireOtp({
|
|
47
|
+
tfaStore,
|
|
48
|
+
scope: "auth",
|
|
49
|
+
logger
|
|
50
|
+
});
|
|
51
|
+
const otpForLogin = require_require_otp.requireOtp({
|
|
52
|
+
tfaStore,
|
|
53
|
+
scope: "auth",
|
|
54
|
+
logger,
|
|
55
|
+
getUsername: (req) => typeof req.body?.name === "string" ? req.body.name : void 0
|
|
56
|
+
});
|
|
57
|
+
const otpForWrites = require_require_otp.requireOtp({
|
|
58
|
+
tfaStore,
|
|
59
|
+
scope: "write",
|
|
60
|
+
logger
|
|
61
|
+
});
|
|
38
62
|
app.use((0, _verdaccio_middleware.enforceGeneratedTokenMetadata)(storage, logger));
|
|
39
63
|
app.use((0, _verdaccio_middleware.antiLoop)(config));
|
|
40
64
|
app.use(_verdaccio_middleware.makeURLrelative);
|
|
41
65
|
app.use(_verdaccio_middleware.encodeScopePackage);
|
|
42
66
|
require_whoami.default(app);
|
|
43
|
-
require_profile.default(app, auth, config);
|
|
67
|
+
require_profile.default(app, auth, config, storage, logger, otpForAuth);
|
|
44
68
|
require_search.default(app, logger);
|
|
45
|
-
require_user.default(app, auth, config, logger);
|
|
46
|
-
require_dist_tags.default(app, auth, storage, logger);
|
|
47
|
-
require_publish.default(app, auth, storage, config, logger);
|
|
69
|
+
require_user.default(app, auth, config, logger, otpForLogin);
|
|
70
|
+
require_dist_tags.default(app, auth, storage, logger, otpForWrites);
|
|
71
|
+
require_publish.default(app, auth, storage, config, logger, otpForWrites);
|
|
48
72
|
require_ping.default(app);
|
|
49
73
|
require_search$1.default(app, auth, storage, config, logger);
|
|
50
|
-
require_token.default(app, auth, storage, config, logger);
|
|
74
|
+
require_token.default(app, auth, storage, config, logger, otpForAuth);
|
|
75
|
+
if (config.flags?.stage) require_stage.default(app, auth, storage, config, logger, otpForWrites);
|
|
51
76
|
require_package.default(app, auth, storage, logger);
|
|
52
77
|
if (config.flags?.webLogin) require_login.default(app, auth, storage, config, logger);
|
|
53
78
|
return app;
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Router } from 'express';\nimport express from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport {\n WebUrlsNamespace,\n antiLoop,\n encodeScopePackage,\n enforceGeneratedTokenMetadata,\n makeURLrelative,\n match,\n registerBodyParser,\n validateName,\n validatePackage,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger } from '@verdaccio/types';\n\nimport distTags from './dist-tags';\nimport pkg from './package';\nimport ping from './ping';\nimport publish from './publish';\nimport search from './search';\nimport user from './user';\nimport login from './v1/login';\nimport profile from './v1/profile';\nimport v1Search from './v1/search';\nimport token from './v1/token';\nimport whoami from './whoami';\n\nexport default function (config: Config, auth: Auth, storage: Storage, logger: Logger): Router {\n /* eslint new-cap:off */\n const app = express.Router();\n\n // validate all of these params as a package name\n // this might be too harsh, so ask if it causes trouble\n app.param('package', validatePackage);\n app.param('filename', validateName);\n app.param('tag', validateName);\n app.param('version', validateName);\n app.param('revision', validateName);\n app.param('token', validateName);\n\n // Express route parameter names must be valid JavaScript identifiers, which means\n // they cannot start with a hyphen (-) or contain special characters like dots (.)\n app.param('_rev', match(/^-rev$/));\n app.param('org_couchdb_user', match(/^org\\.couchdb\\.user:/));\n\n // Body parser must be registered before JWT middleware which pauses/resumes the stream\n registerBodyParser(app, config);\n\n app.use(WebUrlsNamespace.endpoints, (_req, _res, next) => next('router'));\n\n // Avoid executing JWT twice when the parent app already registered the JWT middleware\n const apiJwtMiddleware = auth.apiJWTmiddleware();\n app.use((req, res, next) => {\n const remoteUser = (req as any).remote_user ?? (res.locals as any).remote_user;\n if (remoteUser) {\n return next();\n }\n return apiJwtMiddleware(req, res, next);\n });\n\n app.use(enforceGeneratedTokenMetadata(storage, logger));\n app.use(antiLoop(config));\n app.use(makeURLrelative);\n // encode / in a scoped package name to be matched as a single parameter in routes\n app.use(encodeScopePackage);\n // for \"npm whoami\"\n whoami(app);\n profile(app, auth, config);\n search(app, logger);\n user(app, auth, config, logger);\n distTags(app, auth, storage, logger);\n publish(app, auth, storage, config, logger);\n ping(app);\n v1Search(app, auth, storage, config, logger);\n token(app, auth, storage, config, logger);\n pkg(app, auth, storage, logger);\n if (config.flags?.webLogin) {\n login(app, auth, storage, config, logger);\n }\n return app;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Router } from 'express';\nimport express from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport {\n WebUrlsNamespace,\n antiLoop,\n encodeScopePackage,\n enforceGeneratedTokenMetadata,\n makeURLrelative,\n match,\n registerBodyParser,\n validateName,\n validatePackage,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger } from '@verdaccio/types';\n\nimport { TfaStore } from '@verdaccio/auth';\n\nimport distTags from './dist-tags';\nimport pkg from './package';\nimport ping from './ping';\nimport publish from './publish';\nimport { assertTokenStoreSupport, requireOtp } from './require-otp';\nimport search from './search';\nimport stage from './stage';\nimport user from './user';\nimport login from './v1/login';\nimport profile from './v1/profile';\nimport v1Search from './v1/search';\nimport token from './v1/token';\nimport whoami from './whoami';\n\nexport default function (config: Config, auth: Auth, storage: Storage, logger: Logger): Router {\n /* eslint new-cap:off */\n const app = express.Router();\n\n // validate all of these params as a package name\n // this might be too harsh, so ask if it causes trouble\n app.param('package', validatePackage);\n app.param('filename', validateName);\n app.param('tag', validateName);\n app.param('version', validateName);\n app.param('revision', validateName);\n app.param('token', validateName);\n\n // Express route parameter names must be valid JavaScript identifiers, which means\n // they cannot start with a hyphen (-) or contain special characters like dots (.)\n app.param('_rev', match(/^-rev$/));\n app.param('org_couchdb_user', match(/^org\\.couchdb\\.user:/));\n\n // Body parser must be registered before JWT middleware which pauses/resumes the stream\n registerBodyParser(app, config);\n\n app.use(WebUrlsNamespace.endpoints, (_req, _res, next) => next('router'));\n\n // Avoid executing JWT twice when the parent app already registered the JWT middleware\n const apiJwtMiddleware = auth.apiJWTmiddleware();\n app.use((req, res, next) => {\n const remoteUser = (req as any).remote_user ?? (res.locals as any).remote_user;\n if (remoteUser) {\n return next();\n }\n return apiJwtMiddleware(req, res, next);\n });\n\n // built once: without the flag nothing must reach the token store\n let tfaStore: TfaStore | undefined;\n if (config.flags?.tfa) {\n // refuse to start rather than answer 503 on every write later\n assertTokenStoreSupport(storage, logger);\n tfaStore = new TfaStore(storage, config.secret, logger);\n }\n const otpForAuth = requireOtp({ tfaStore, scope: 'auth', logger });\n // at login there is no authenticated user yet: the name is in the body\n const otpForLogin = requireOtp({\n tfaStore,\n scope: 'auth',\n logger,\n getUsername: (req) => (typeof req.body?.name === 'string' ? req.body.name : undefined),\n });\n const otpForWrites = requireOtp({ tfaStore, scope: 'write', logger });\n\n app.use(enforceGeneratedTokenMetadata(storage, logger));\n app.use(antiLoop(config));\n app.use(makeURLrelative);\n // encode / in a scoped package name to be matched as a single parameter in routes\n app.use(encodeScopePackage);\n // for \"npm whoami\"\n whoami(app);\n profile(app, auth, config, storage, logger, otpForAuth);\n search(app, logger);\n user(app, auth, config, logger, otpForLogin);\n distTags(app, auth, storage, logger, otpForWrites);\n publish(app, auth, storage, config, logger, otpForWrites);\n ping(app);\n v1Search(app, auth, storage, config, logger);\n token(app, auth, storage, config, logger, otpForAuth);\n // must stay before pkg(): its '/:package{/:version}' route would otherwise\n // swallow GET /-/stage\n if (config.flags?.stage) {\n stage(app, auth, storage, config, logger, otpForWrites);\n }\n pkg(app, auth, storage, logger);\n if (config.flags?.webLogin) {\n login(app, auth, storage, config, logger);\n }\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAA,YAAyB,QAAgB,MAAY,SAAkB,QAAwB;CAE7F,MAAM,MAAM,QAAA,QAAQ,OAAO;CAI3B,IAAI,MAAM,WAAW,sBAAA,eAAe;CACpC,IAAI,MAAM,YAAY,sBAAA,YAAY;CAClC,IAAI,MAAM,OAAO,sBAAA,YAAY;CAC7B,IAAI,MAAM,WAAW,sBAAA,YAAY;CACjC,IAAI,MAAM,YAAY,sBAAA,YAAY;CAClC,IAAI,MAAM,SAAS,sBAAA,YAAY;CAI/B,IAAI,MAAM,SAAA,GAAQ,sBAAA,MAAA,CAAM,QAAQ,CAAC;CACjC,IAAI,MAAM,qBAAA,GAAoB,sBAAA,MAAA,CAAM,sBAAsB,CAAC;CAG3D,CAAA,GAAA,sBAAA,mBAAA,CAAmB,KAAK,MAAM;CAE9B,IAAI,IAAI,sBAAA,iBAAiB,YAAY,MAAM,MAAM,SAAS,KAAK,QAAQ,CAAC;CAGxE,MAAM,mBAAmB,KAAK,iBAAiB;CAC/C,IAAI,KAAK,KAAK,KAAK,SAAS;EAE1B,IADoB,IAAY,eAAgB,IAAI,OAAe,aAEjE,OAAO,KAAK;EAEd,OAAO,iBAAiB,KAAK,KAAK,IAAI;CACxC,CAAC;CAGD,IAAI;CACJ,IAAI,OAAO,OAAO,KAAK;EAErB,oBAAA,wBAAwB,SAAS,MAAM;EACvC,WAAW,IAAI,gBAAA,SAAS,SAAS,OAAO,QAAQ,MAAM;CACxD;CACA,MAAM,aAAa,oBAAA,WAAW;EAAE;EAAU,OAAO;EAAQ;CAAO,CAAC;CAEjE,MAAM,cAAc,oBAAA,WAAW;EAC7B;EACA,OAAO;EACP;EACA,cAAc,QAAS,OAAO,IAAI,MAAM,SAAS,WAAW,IAAI,KAAK,OAAO,KAAA;CAC9E,CAAC;CACD,MAAM,eAAe,oBAAA,WAAW;EAAE;EAAU,OAAO;EAAS;CAAO,CAAC;CAEpE,IAAI,KAAA,GAAI,sBAAA,8BAAA,CAA8B,SAAS,MAAM,CAAC;CACtD,IAAI,KAAA,GAAI,sBAAA,SAAA,CAAS,MAAM,CAAC;CACxB,IAAI,IAAI,sBAAA,eAAe;CAEvB,IAAI,IAAI,sBAAA,kBAAkB;CAE1B,eAAA,QAAO,GAAG;CACV,gBAAA,QAAQ,KAAK,MAAM,QAAQ,SAAS,QAAQ,UAAU;CACtD,eAAA,QAAO,KAAK,MAAM;CAClB,aAAA,QAAK,KAAK,MAAM,QAAQ,QAAQ,WAAW;CAC3C,kBAAA,QAAS,KAAK,MAAM,SAAS,QAAQ,YAAY;CACjD,gBAAA,QAAQ,KAAK,MAAM,SAAS,QAAQ,QAAQ,YAAY;CACxD,aAAA,QAAK,GAAG;CACR,iBAAA,QAAS,KAAK,MAAM,SAAS,QAAQ,MAAM;CAC3C,cAAA,QAAM,KAAK,MAAM,SAAS,QAAQ,QAAQ,UAAU;CAGpD,IAAI,OAAO,OAAO,OAChB,cAAA,QAAM,KAAK,MAAM,SAAS,QAAQ,QAAQ,YAAY;CAExD,gBAAA,QAAI,KAAK,MAAM,SAAS,MAAM;CAC9B,IAAI,OAAO,OAAO,UAChB,cAAA,QAAM,KAAK,MAAM,SAAS,QAAQ,MAAM;CAE1C,OAAO;AACT"}
|
package/build/index.mjs
CHANGED
|
@@ -2,7 +2,9 @@ import dist_tags_default from "./dist-tags.mjs";
|
|
|
2
2
|
import package_default from "./package.mjs";
|
|
3
3
|
import ping_default from "./ping.mjs";
|
|
4
4
|
import publish from "./publish.mjs";
|
|
5
|
+
import { assertTokenStoreSupport, requireOtp } from "./require-otp.mjs";
|
|
5
6
|
import search_default from "./search.mjs";
|
|
7
|
+
import stage from "./stage.mjs";
|
|
6
8
|
import user_default from "./user.mjs";
|
|
7
9
|
import login_default from "./v1/login.mjs";
|
|
8
10
|
import profile_default from "./v1/profile.mjs";
|
|
@@ -11,6 +13,7 @@ import token_default from "./v1/token.mjs";
|
|
|
11
13
|
import whoami_default from "./whoami.mjs";
|
|
12
14
|
import express from "express";
|
|
13
15
|
import { WebUrlsNamespace, antiLoop, encodeScopePackage, enforceGeneratedTokenMetadata, makeURLrelative, match, registerBodyParser, validateName, validatePackage } from "@verdaccio/middleware";
|
|
16
|
+
import { TfaStore } from "@verdaccio/auth";
|
|
14
17
|
//#region src/index.ts
|
|
15
18
|
function src_default(config, auth, storage, logger) {
|
|
16
19
|
const app = express.Router();
|
|
@@ -29,19 +32,41 @@ function src_default(config, auth, storage, logger) {
|
|
|
29
32
|
if (req.remote_user ?? res.locals.remote_user) return next();
|
|
30
33
|
return apiJwtMiddleware(req, res, next);
|
|
31
34
|
});
|
|
35
|
+
let tfaStore;
|
|
36
|
+
if (config.flags?.tfa) {
|
|
37
|
+
assertTokenStoreSupport(storage, logger);
|
|
38
|
+
tfaStore = new TfaStore(storage, config.secret, logger);
|
|
39
|
+
}
|
|
40
|
+
const otpForAuth = requireOtp({
|
|
41
|
+
tfaStore,
|
|
42
|
+
scope: "auth",
|
|
43
|
+
logger
|
|
44
|
+
});
|
|
45
|
+
const otpForLogin = requireOtp({
|
|
46
|
+
tfaStore,
|
|
47
|
+
scope: "auth",
|
|
48
|
+
logger,
|
|
49
|
+
getUsername: (req) => typeof req.body?.name === "string" ? req.body.name : void 0
|
|
50
|
+
});
|
|
51
|
+
const otpForWrites = requireOtp({
|
|
52
|
+
tfaStore,
|
|
53
|
+
scope: "write",
|
|
54
|
+
logger
|
|
55
|
+
});
|
|
32
56
|
app.use(enforceGeneratedTokenMetadata(storage, logger));
|
|
33
57
|
app.use(antiLoop(config));
|
|
34
58
|
app.use(makeURLrelative);
|
|
35
59
|
app.use(encodeScopePackage);
|
|
36
60
|
whoami_default(app);
|
|
37
|
-
profile_default(app, auth, config);
|
|
61
|
+
profile_default(app, auth, config, storage, logger, otpForAuth);
|
|
38
62
|
search_default(app, logger);
|
|
39
|
-
user_default(app, auth, config, logger);
|
|
40
|
-
dist_tags_default(app, auth, storage, logger);
|
|
41
|
-
publish(app, auth, storage, config, logger);
|
|
63
|
+
user_default(app, auth, config, logger, otpForLogin);
|
|
64
|
+
dist_tags_default(app, auth, storage, logger, otpForWrites);
|
|
65
|
+
publish(app, auth, storage, config, logger, otpForWrites);
|
|
42
66
|
ping_default(app);
|
|
43
67
|
search_default$1(app, auth, storage, config, logger);
|
|
44
|
-
token_default(app, auth, storage, config, logger);
|
|
68
|
+
token_default(app, auth, storage, config, logger, otpForAuth);
|
|
69
|
+
if (config.flags?.stage) stage(app, auth, storage, config, logger, otpForWrites);
|
|
45
70
|
package_default(app, auth, storage, logger);
|
|
46
71
|
if (config.flags?.webLogin) login_default(app, auth, storage, config, logger);
|
|
47
72
|
return app;
|
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Router } from 'express';\nimport express from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport {\n WebUrlsNamespace,\n antiLoop,\n encodeScopePackage,\n enforceGeneratedTokenMetadata,\n makeURLrelative,\n match,\n registerBodyParser,\n validateName,\n validatePackage,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger } from '@verdaccio/types';\n\nimport distTags from './dist-tags';\nimport pkg from './package';\nimport ping from './ping';\nimport publish from './publish';\nimport search from './search';\nimport user from './user';\nimport login from './v1/login';\nimport profile from './v1/profile';\nimport v1Search from './v1/search';\nimport token from './v1/token';\nimport whoami from './whoami';\n\nexport default function (config: Config, auth: Auth, storage: Storage, logger: Logger): Router {\n /* eslint new-cap:off */\n const app = express.Router();\n\n // validate all of these params as a package name\n // this might be too harsh, so ask if it causes trouble\n app.param('package', validatePackage);\n app.param('filename', validateName);\n app.param('tag', validateName);\n app.param('version', validateName);\n app.param('revision', validateName);\n app.param('token', validateName);\n\n // Express route parameter names must be valid JavaScript identifiers, which means\n // they cannot start with a hyphen (-) or contain special characters like dots (.)\n app.param('_rev', match(/^-rev$/));\n app.param('org_couchdb_user', match(/^org\\.couchdb\\.user:/));\n\n // Body parser must be registered before JWT middleware which pauses/resumes the stream\n registerBodyParser(app, config);\n\n app.use(WebUrlsNamespace.endpoints, (_req, _res, next) => next('router'));\n\n // Avoid executing JWT twice when the parent app already registered the JWT middleware\n const apiJwtMiddleware = auth.apiJWTmiddleware();\n app.use((req, res, next) => {\n const remoteUser = (req as any).remote_user ?? (res.locals as any).remote_user;\n if (remoteUser) {\n return next();\n }\n return apiJwtMiddleware(req, res, next);\n });\n\n app.use(enforceGeneratedTokenMetadata(storage, logger));\n app.use(antiLoop(config));\n app.use(makeURLrelative);\n // encode / in a scoped package name to be matched as a single parameter in routes\n app.use(encodeScopePackage);\n // for \"npm whoami\"\n whoami(app);\n profile(app, auth, config);\n search(app, logger);\n user(app, auth, config, logger);\n distTags(app, auth, storage, logger);\n publish(app, auth, storage, config, logger);\n ping(app);\n v1Search(app, auth, storage, config, logger);\n token(app, auth, storage, config, logger);\n pkg(app, auth, storage, logger);\n if (config.flags?.webLogin) {\n login(app, auth, storage, config, logger);\n }\n return app;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Router } from 'express';\nimport express from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport {\n WebUrlsNamespace,\n antiLoop,\n encodeScopePackage,\n enforceGeneratedTokenMetadata,\n makeURLrelative,\n match,\n registerBodyParser,\n validateName,\n validatePackage,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger } from '@verdaccio/types';\n\nimport { TfaStore } from '@verdaccio/auth';\n\nimport distTags from './dist-tags';\nimport pkg from './package';\nimport ping from './ping';\nimport publish from './publish';\nimport { assertTokenStoreSupport, requireOtp } from './require-otp';\nimport search from './search';\nimport stage from './stage';\nimport user from './user';\nimport login from './v1/login';\nimport profile from './v1/profile';\nimport v1Search from './v1/search';\nimport token from './v1/token';\nimport whoami from './whoami';\n\nexport default function (config: Config, auth: Auth, storage: Storage, logger: Logger): Router {\n /* eslint new-cap:off */\n const app = express.Router();\n\n // validate all of these params as a package name\n // this might be too harsh, so ask if it causes trouble\n app.param('package', validatePackage);\n app.param('filename', validateName);\n app.param('tag', validateName);\n app.param('version', validateName);\n app.param('revision', validateName);\n app.param('token', validateName);\n\n // Express route parameter names must be valid JavaScript identifiers, which means\n // they cannot start with a hyphen (-) or contain special characters like dots (.)\n app.param('_rev', match(/^-rev$/));\n app.param('org_couchdb_user', match(/^org\\.couchdb\\.user:/));\n\n // Body parser must be registered before JWT middleware which pauses/resumes the stream\n registerBodyParser(app, config);\n\n app.use(WebUrlsNamespace.endpoints, (_req, _res, next) => next('router'));\n\n // Avoid executing JWT twice when the parent app already registered the JWT middleware\n const apiJwtMiddleware = auth.apiJWTmiddleware();\n app.use((req, res, next) => {\n const remoteUser = (req as any).remote_user ?? (res.locals as any).remote_user;\n if (remoteUser) {\n return next();\n }\n return apiJwtMiddleware(req, res, next);\n });\n\n // built once: without the flag nothing must reach the token store\n let tfaStore: TfaStore | undefined;\n if (config.flags?.tfa) {\n // refuse to start rather than answer 503 on every write later\n assertTokenStoreSupport(storage, logger);\n tfaStore = new TfaStore(storage, config.secret, logger);\n }\n const otpForAuth = requireOtp({ tfaStore, scope: 'auth', logger });\n // at login there is no authenticated user yet: the name is in the body\n const otpForLogin = requireOtp({\n tfaStore,\n scope: 'auth',\n logger,\n getUsername: (req) => (typeof req.body?.name === 'string' ? req.body.name : undefined),\n });\n const otpForWrites = requireOtp({ tfaStore, scope: 'write', logger });\n\n app.use(enforceGeneratedTokenMetadata(storage, logger));\n app.use(antiLoop(config));\n app.use(makeURLrelative);\n // encode / in a scoped package name to be matched as a single parameter in routes\n app.use(encodeScopePackage);\n // for \"npm whoami\"\n whoami(app);\n profile(app, auth, config, storage, logger, otpForAuth);\n search(app, logger);\n user(app, auth, config, logger, otpForLogin);\n distTags(app, auth, storage, logger, otpForWrites);\n publish(app, auth, storage, config, logger, otpForWrites);\n ping(app);\n v1Search(app, auth, storage, config, logger);\n token(app, auth, storage, config, logger, otpForAuth);\n // must stay before pkg(): its '/:package{/:version}' route would otherwise\n // swallow GET /-/stage\n if (config.flags?.stage) {\n stage(app, auth, storage, config, logger, otpForWrites);\n }\n pkg(app, auth, storage, logger);\n if (config.flags?.webLogin) {\n login(app, auth, storage, config, logger);\n }\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkCA,SAAA,YAAyB,QAAgB,MAAY,SAAkB,QAAwB;CAE7F,MAAM,MAAM,QAAQ,OAAO;CAI3B,IAAI,MAAM,WAAW,eAAe;CACpC,IAAI,MAAM,YAAY,YAAY;CAClC,IAAI,MAAM,OAAO,YAAY;CAC7B,IAAI,MAAM,WAAW,YAAY;CACjC,IAAI,MAAM,YAAY,YAAY;CAClC,IAAI,MAAM,SAAS,YAAY;CAI/B,IAAI,MAAM,QAAQ,MAAM,QAAQ,CAAC;CACjC,IAAI,MAAM,oBAAoB,MAAM,sBAAsB,CAAC;CAG3D,mBAAmB,KAAK,MAAM;CAE9B,IAAI,IAAI,iBAAiB,YAAY,MAAM,MAAM,SAAS,KAAK,QAAQ,CAAC;CAGxE,MAAM,mBAAmB,KAAK,iBAAiB;CAC/C,IAAI,KAAK,KAAK,KAAK,SAAS;EAE1B,IADoB,IAAY,eAAgB,IAAI,OAAe,aAEjE,OAAO,KAAK;EAEd,OAAO,iBAAiB,KAAK,KAAK,IAAI;CACxC,CAAC;CAGD,IAAI;CACJ,IAAI,OAAO,OAAO,KAAK;EAErB,wBAAwB,SAAS,MAAM;EACvC,WAAW,IAAI,SAAS,SAAS,OAAO,QAAQ,MAAM;CACxD;CACA,MAAM,aAAa,WAAW;EAAE;EAAU,OAAO;EAAQ;CAAO,CAAC;CAEjE,MAAM,cAAc,WAAW;EAC7B;EACA,OAAO;EACP;EACA,cAAc,QAAS,OAAO,IAAI,MAAM,SAAS,WAAW,IAAI,KAAK,OAAO,KAAA;CAC9E,CAAC;CACD,MAAM,eAAe,WAAW;EAAE;EAAU,OAAO;EAAS;CAAO,CAAC;CAEpE,IAAI,IAAI,8BAA8B,SAAS,MAAM,CAAC;CACtD,IAAI,IAAI,SAAS,MAAM,CAAC;CACxB,IAAI,IAAI,eAAe;CAEvB,IAAI,IAAI,kBAAkB;CAE1B,eAAO,GAAG;CACV,gBAAQ,KAAK,MAAM,QAAQ,SAAS,QAAQ,UAAU;CACtD,eAAO,KAAK,MAAM;CAClB,aAAK,KAAK,MAAM,QAAQ,QAAQ,WAAW;CAC3C,kBAAS,KAAK,MAAM,SAAS,QAAQ,YAAY;CACjD,QAAQ,KAAK,MAAM,SAAS,QAAQ,QAAQ,YAAY;CACxD,aAAK,GAAG;CACR,iBAAS,KAAK,MAAM,SAAS,QAAQ,MAAM;CAC3C,cAAM,KAAK,MAAM,SAAS,QAAQ,QAAQ,UAAU;CAGpD,IAAI,OAAO,OAAO,OAChB,MAAM,KAAK,MAAM,SAAS,QAAQ,QAAQ,YAAY;CAExD,gBAAI,KAAK,MAAM,SAAS,MAAM;CAC9B,IAAI,OAAO,OAAO,UAChB,cAAM,KAAK,MAAM,SAAS,QAAQ,MAAM;CAE1C,OAAO;AACT"}
|
package/build/publish.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Router } from 'express';
|
|
1
|
+
import type { RequestHandler, Router } from 'express';
|
|
2
2
|
import type { Auth } from '@verdaccio/auth';
|
|
3
3
|
import type { Storage } from '@verdaccio/store';
|
|
4
4
|
import type { Config, Logger } from '@verdaccio/types';
|
|
@@ -79,5 +79,7 @@ import type { Config, Logger } from '@verdaccio/types';
|
|
|
79
79
|
}
|
|
80
80
|
*
|
|
81
81
|
*/
|
|
82
|
-
export default function publish(router: Router, auth: Auth, storage: Storage, config: Config, logger: Logger
|
|
82
|
+
export default function publish(router: Router, auth: Auth, storage: Storage, config: Config, logger: Logger,
|
|
83
|
+
/** No-op unless the caller has two-factor in `auth-and-writes` mode. */
|
|
84
|
+
requireOtp?: RequestHandler): void;
|
|
83
85
|
export declare function publishPackage(storage: Storage, config: Config, logger: Logger, origin: string): any;
|
package/build/publish.js
CHANGED
|
@@ -83,13 +83,13 @@ npm verb exit [ 0, true ]
|
|
|
83
83
|
}
|
|
84
84
|
*
|
|
85
85
|
*/
|
|
86
|
-
function publish(router, auth, storage, config, logger) {
|
|
86
|
+
function publish(router, auth, storage, config, logger, requireOtp = (_req, _res, next) => next()) {
|
|
87
87
|
const can = (0, _verdaccio_middleware.allow)(auth, {
|
|
88
88
|
beforeAll: (a, b) => logger.trace(a, b),
|
|
89
89
|
afterAll: (a, b) => logger.trace(a, b)
|
|
90
90
|
});
|
|
91
|
-
router.put(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.add_package, can("publish"), (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), _verdaccio_middleware.expectJson, publishPackage(storage, config, logger, "publish one version"));
|
|
92
|
-
router.put(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), _verdaccio_middleware.expectJson, publishPackage(storage, config, logger, "publish with revision"));
|
|
91
|
+
router.put(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.add_package, can("publish"), requireOtp, (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), _verdaccio_middleware.expectJson, publishPackage(storage, config, logger, "publish one version"));
|
|
92
|
+
router.put(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), requireOtp, (0, _verdaccio_middleware.media)(_verdaccio_core.HEADERS.JSON), _verdaccio_middleware.expectJson, publishPackage(storage, config, logger, "publish with revision"));
|
|
93
93
|
/**
|
|
94
94
|
* Un-publishing an entire package.
|
|
95
95
|
*
|
|
@@ -107,7 +107,7 @@ function publish(router, auth, storage, config, logger) {
|
|
|
107
107
|
* and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)
|
|
108
108
|
* npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms
|
|
109
109
|
*/
|
|
110
|
-
router.delete(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), async function(req, res, next) {
|
|
110
|
+
router.delete(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), requireOtp, async function(req, res, next) {
|
|
111
111
|
const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
|
|
112
112
|
const rev = _verdaccio_core.reqUtils.paramToString(req.params.revision);
|
|
113
113
|
const username = req?.remote_user?.name;
|
|
@@ -127,7 +127,7 @@ function publish(router, auth, storage, config, logger) {
|
|
|
127
127
|
return next(err);
|
|
128
128
|
}
|
|
129
129
|
});
|
|
130
|
-
router.delete(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.remove_tarball, can("unpublish"), async function(req, res, next) {
|
|
130
|
+
router.delete(_verdaccio_middleware.PUBLISH_API_ENDPOINTS.remove_tarball, can("unpublish"), requireOtp, async function(req, res, next) {
|
|
131
131
|
const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
|
|
132
132
|
const filename = _verdaccio_core.reqUtils.paramToString(req.params.filename);
|
|
133
133
|
const revision = _verdaccio_core.reqUtils.paramToString(req.params.revision);
|
package/build/publish.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HEADERS, HTTP_STATUS, reqUtils, tarballUtils } from '@verdaccio/core';\nimport { notify } from '@verdaccio/hooks';\nimport {\n PUBLISH_API_ENDPOINTS,\n allow,\n expectJson,\n getRequestOptions,\n media,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package\n *\n * There are multiples scenarios here to be considered:\n *\n * 1. Publish scenario\n *\n * Publish a package consist of at least 1 step (PUT) with a metadata payload.\n * When a package is published, an _attachment property is present that contains the data\n * of the tarball.\n *\n * Example flow of publish.\n *\n * npm http fetch PUT 201 http://localhost:4873/@scope%2ftest1 9627ms\n npm info lifecycle @scope/test1@1.0.1~publish: @scope/test1@1.0.1\n npm info lifecycle @scope/test1@1.0.1~postpublish: @scope/test1@1.0.1\n + @scope/test1@1.0.1\n npm verb exit [ 0, true ]\n *\n *\n * 2. Unpublish scenario\n *\n * Unpublish consist in 3 steps.\n * 1. Try to fetch metadata -> if it fails, return 404\n * 2. Compute metadata locally (client side) and send a mutate payload excluding the version to\n * be unpublished\n * eg: if metadata reflects 1.0.1, 1.0.2 and 1.0.3, the computed metadata won't include 1.0.3.\n * 3. Once the second step has been successfully finished, delete the tarball.\n *\n * All these steps are consecutive and required, there is no transacions here, if step 3 fails,\n * metadata might get corrupted.\n *\n * Note the unpublish call will suffix in the url a /-rev/14-5d500cfce92f90fd revision number,\n * this not\n * used internally.\n *\n *\n * Example flow of unpublish.\n *\n * There are two possible flows:\n *\n * - Remove all packages (entirely)\n * eg: npm unpublish package-name@* --force\n * eg: npm unpublish package-name --force\n *\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * - Remove a specific version\n * eg: npm unpublish package-name@1.0.0 --force\n *\n * Get fresh manifest\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * Update manifest without the version to be unpublished\n * npm http fetch PUT 201 http://localhost:4873/custom-name/-rev/14-5d500cfce92f90fd 956606ms\n * Get fresh manifest (revision should be different)\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1601ms\n * Remove the tarball\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * 3. Change owners of a package\n *\n * Changing owners (maintainers) of a package uses the publish endpoint.\n *\n * The body includes a list of the new owners with the following format\n *\n * {\n \"_id\": pkgName,\n \"_rev\": \"4-b0cdaefc9bdb77c8\",\n \"maintainers\": [\n { \"name\": \"first owner\", \"email\": \"me@verdaccio.org\" },\n { \"name\": \"second owner\", \"email\": \"you@verdaccio.org\" },\n ...\n ]\n }\n *\n */\nexport default function publish(\n router: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n router.put(\n PUBLISH_API_ENDPOINTS.add_package,\n can('publish'),\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish one version')\n );\n\n router.put(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish with revision')\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when any of these scenarios happens:\n * - the first call detect there is only one version remaining\n * - no version is specified in the unpublish call\n * - all versions are removed npm unpublish package@*\n * - there is no versions on the metadata\n\n * then the client decides to DELETE the resource\n * Example:\n * Get fresh manifest (write=true is a flag to get the latest revision)\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * Send request to delete the package, this includes the revision number that must match\n * and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = reqUtils.paramToString(req.params.package);\n const rev = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug({ packageName }, `unpublishing @{packageName}`);\n try {\n await storage.removePackage(packageName, rev, username);\n debug('package %s unpublished', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of package removal\n const metadata: Partial<Manifest> = { name: packageName, _rev: rev };\n\n void notify(metadata, config, req.remote_user, packageName, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.PKG_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n\n /*\n Remove a tarball, this happens when npm unpublish a package unique version.\n npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.remove_tarball,\n can('unpublish'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const filename = reqUtils.paramToString(req.params.filename);\n const revision = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug(\n { packageName, filename, revision },\n `removing a tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n try {\n await storage.removeTarball(packageName, filename, revision, username);\n res.status(HTTP_STATUS.CREATED);\n\n logger.debug(\n { packageName, filename, revision },\n `success remove tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n\n // send notification of version removal\n // getVersionFromTarball returns undefined when the filename is not parseable;\n // fall back to the package name so we never report `name@undefined`\n const version = tarballUtils.getVersionFromTarball(filename);\n const metadata: Partial<Manifest> = { name: packageName, _rev: revision };\n const publishedPackage = version ? `${packageName}@${version}` : packageName;\n\n void notify(metadata, config, req.remote_user, publishedPackage, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(\n storage: Storage,\n config: Config,\n logger: Logger,\n origin: string\n): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug(origin);\n const ac = new AbortController();\n const packageName = reqUtils.paramToString(req.params.package);\n const revision = reqUtils.paramToString(req.params.revision);\n debug('publishing package %s', packageName);\n debug('revision %s', revision);\n if (debug.enabled) {\n debug('body %o', req.body);\n }\n const metadata = req.body;\n\n logger.debug({ packageName }, `publishing package @{packageName}`);\n\n const requestOptions = getRequestOptions(req);\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions,\n uplinksLook: false,\n });\n debug('package %s published', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of publication (notification step, non transactional)\n // a publish body is a packument; the published version is the single entry\n // under `versions` (see storage.updateManifest), not a top-level field\n const [version] = Object.keys(metadata.versions ?? {});\n void notify(\n metadata,\n config,\n req.remote_user,\n `${metadata.name}@${version}`,\n 'publish'\n ).catch((error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n });\n\n return next({\n success: true,\n ok: message,\n });\n } catch (err: any) {\n // TODO: review if we need the abort controller here\n next(err);\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"publish.js","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { RequestHandler, Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HEADERS, HTTP_STATUS, reqUtils, tarballUtils } from '@verdaccio/core';\nimport { notify } from '@verdaccio/hooks';\nimport {\n PUBLISH_API_ENDPOINTS,\n allow,\n expectJson,\n getRequestOptions,\n media,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package\n *\n * There are multiples scenarios here to be considered:\n *\n * 1. Publish scenario\n *\n * Publish a package consist of at least 1 step (PUT) with a metadata payload.\n * When a package is published, an _attachment property is present that contains the data\n * of the tarball.\n *\n * Example flow of publish.\n *\n * npm http fetch PUT 201 http://localhost:4873/@scope%2ftest1 9627ms\n npm info lifecycle @scope/test1@1.0.1~publish: @scope/test1@1.0.1\n npm info lifecycle @scope/test1@1.0.1~postpublish: @scope/test1@1.0.1\n + @scope/test1@1.0.1\n npm verb exit [ 0, true ]\n *\n *\n * 2. Unpublish scenario\n *\n * Unpublish consist in 3 steps.\n * 1. Try to fetch metadata -> if it fails, return 404\n * 2. Compute metadata locally (client side) and send a mutate payload excluding the version to\n * be unpublished\n * eg: if metadata reflects 1.0.1, 1.0.2 and 1.0.3, the computed metadata won't include 1.0.3.\n * 3. Once the second step has been successfully finished, delete the tarball.\n *\n * All these steps are consecutive and required, there is no transacions here, if step 3 fails,\n * metadata might get corrupted.\n *\n * Note the unpublish call will suffix in the url a /-rev/14-5d500cfce92f90fd revision number,\n * this not\n * used internally.\n *\n *\n * Example flow of unpublish.\n *\n * There are two possible flows:\n *\n * - Remove all packages (entirely)\n * eg: npm unpublish package-name@* --force\n * eg: npm unpublish package-name --force\n *\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * - Remove a specific version\n * eg: npm unpublish package-name@1.0.0 --force\n *\n * Get fresh manifest\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * Update manifest without the version to be unpublished\n * npm http fetch PUT 201 http://localhost:4873/custom-name/-rev/14-5d500cfce92f90fd 956606ms\n * Get fresh manifest (revision should be different)\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1601ms\n * Remove the tarball\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * 3. Change owners of a package\n *\n * Changing owners (maintainers) of a package uses the publish endpoint.\n *\n * The body includes a list of the new owners with the following format\n *\n * {\n \"_id\": pkgName,\n \"_rev\": \"4-b0cdaefc9bdb77c8\",\n \"maintainers\": [\n { \"name\": \"first owner\", \"email\": \"me@verdaccio.org\" },\n { \"name\": \"second owner\", \"email\": \"you@verdaccio.org\" },\n ...\n ]\n }\n *\n */\nexport default function publish(\n router: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger,\n /** No-op unless the caller has two-factor in `auth-and-writes` mode. */\n requireOtp: RequestHandler = (_req, _res, next) => next()\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n router.put(\n PUBLISH_API_ENDPOINTS.add_package,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish one version')\n );\n\n router.put(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n requireOtp,\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish with revision')\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when any of these scenarios happens:\n * - the first call detect there is only one version remaining\n * - no version is specified in the unpublish call\n * - all versions are removed npm unpublish package@*\n * - there is no versions on the metadata\n\n * then the client decides to DELETE the resource\n * Example:\n * Get fresh manifest (write=true is a flag to get the latest revision)\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * Send request to delete the package, this includes the revision number that must match\n * and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n requireOtp,\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = reqUtils.paramToString(req.params.package);\n const rev = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug({ packageName }, `unpublishing @{packageName}`);\n try {\n await storage.removePackage(packageName, rev, username);\n debug('package %s unpublished', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of package removal\n const metadata: Partial<Manifest> = { name: packageName, _rev: rev };\n\n void notify(metadata, config, req.remote_user, packageName, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.PKG_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n\n /*\n Remove a tarball, this happens when npm unpublish a package unique version.\n npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.remove_tarball,\n can('unpublish'),\n requireOtp,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const filename = reqUtils.paramToString(req.params.filename);\n const revision = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug(\n { packageName, filename, revision },\n `removing a tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n try {\n await storage.removeTarball(packageName, filename, revision, username);\n res.status(HTTP_STATUS.CREATED);\n\n logger.debug(\n { packageName, filename, revision },\n `success remove tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n\n // send notification of version removal\n // getVersionFromTarball returns undefined when the filename is not parseable;\n // fall back to the package name so we never report `name@undefined`\n const version = tarballUtils.getVersionFromTarball(filename);\n const metadata: Partial<Manifest> = { name: packageName, _rev: revision };\n const publishedPackage = version ? `${packageName}@${version}` : packageName;\n\n void notify(metadata, config, req.remote_user, publishedPackage, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(\n storage: Storage,\n config: Config,\n logger: Logger,\n origin: string\n): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug(origin);\n const ac = new AbortController();\n const packageName = reqUtils.paramToString(req.params.package);\n const revision = reqUtils.paramToString(req.params.revision);\n debug('publishing package %s', packageName);\n debug('revision %s', revision);\n if (debug.enabled) {\n debug('body %o', req.body);\n }\n const metadata = req.body;\n\n logger.debug({ packageName }, `publishing package @{packageName}`);\n\n const requestOptions = getRequestOptions(req);\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions,\n uplinksLook: false,\n });\n debug('package %s published', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of publication (notification step, non transactional)\n // a publish body is a packument; the published version is the single entry\n // under `versions` (see storage.updateManifest), not a top-level field\n const [version] = Object.keys(metadata.versions ?? {});\n void notify(\n metadata,\n config,\n req.remote_user,\n `${metadata.name}@${version}`,\n 'publish'\n ).catch((error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n });\n\n return next({\n success: true,\n ok: message,\n });\n } catch (err: any) {\n // TODO: review if we need the abort controller here\n next(err);\n }\n };\n}\n"],"mappings":"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"}
|
package/build/publish.mjs
CHANGED
|
@@ -81,13 +81,13 @@ npm verb exit [ 0, true ]
|
|
|
81
81
|
}
|
|
82
82
|
*
|
|
83
83
|
*/
|
|
84
|
-
function publish(router, auth, storage, config, logger) {
|
|
84
|
+
function publish(router, auth, storage, config, logger, requireOtp = (_req, _res, next) => next()) {
|
|
85
85
|
const can = allow(auth, {
|
|
86
86
|
beforeAll: (a, b) => logger.trace(a, b),
|
|
87
87
|
afterAll: (a, b) => logger.trace(a, b)
|
|
88
88
|
});
|
|
89
|
-
router.put(PUBLISH_API_ENDPOINTS.add_package, can("publish"), media(HEADERS.JSON), expectJson, publishPackage(storage, config, logger, "publish one version"));
|
|
90
|
-
router.put(PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), media(HEADERS.JSON), expectJson, publishPackage(storage, config, logger, "publish with revision"));
|
|
89
|
+
router.put(PUBLISH_API_ENDPOINTS.add_package, can("publish"), requireOtp, media(HEADERS.JSON), expectJson, publishPackage(storage, config, logger, "publish one version"));
|
|
90
|
+
router.put(PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), requireOtp, media(HEADERS.JSON), expectJson, publishPackage(storage, config, logger, "publish with revision"));
|
|
91
91
|
/**
|
|
92
92
|
* Un-publishing an entire package.
|
|
93
93
|
*
|
|
@@ -105,7 +105,7 @@ function publish(router, auth, storage, config, logger) {
|
|
|
105
105
|
* and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)
|
|
106
106
|
* npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms
|
|
107
107
|
*/
|
|
108
|
-
router.delete(PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), async function(req, res, next) {
|
|
108
|
+
router.delete(PUBLISH_API_ENDPOINTS.publish_package, can("unpublish"), requireOtp, async function(req, res, next) {
|
|
109
109
|
const packageName = reqUtils.paramToString(req.params.package);
|
|
110
110
|
const rev = reqUtils.paramToString(req.params.revision);
|
|
111
111
|
const username = req?.remote_user?.name;
|
|
@@ -125,7 +125,7 @@ function publish(router, auth, storage, config, logger) {
|
|
|
125
125
|
return next(err);
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
|
-
router.delete(PUBLISH_API_ENDPOINTS.remove_tarball, can("unpublish"), async function(req, res, next) {
|
|
128
|
+
router.delete(PUBLISH_API_ENDPOINTS.remove_tarball, can("unpublish"), requireOtp, async function(req, res, next) {
|
|
129
129
|
const packageName = reqUtils.paramToString(req.params.package);
|
|
130
130
|
const filename = reqUtils.paramToString(req.params.filename);
|
|
131
131
|
const revision = reqUtils.paramToString(req.params.revision);
|
package/build/publish.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.mjs","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HEADERS, HTTP_STATUS, reqUtils, tarballUtils } from '@verdaccio/core';\nimport { notify } from '@verdaccio/hooks';\nimport {\n PUBLISH_API_ENDPOINTS,\n allow,\n expectJson,\n getRequestOptions,\n media,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package\n *\n * There are multiples scenarios here to be considered:\n *\n * 1. Publish scenario\n *\n * Publish a package consist of at least 1 step (PUT) with a metadata payload.\n * When a package is published, an _attachment property is present that contains the data\n * of the tarball.\n *\n * Example flow of publish.\n *\n * npm http fetch PUT 201 http://localhost:4873/@scope%2ftest1 9627ms\n npm info lifecycle @scope/test1@1.0.1~publish: @scope/test1@1.0.1\n npm info lifecycle @scope/test1@1.0.1~postpublish: @scope/test1@1.0.1\n + @scope/test1@1.0.1\n npm verb exit [ 0, true ]\n *\n *\n * 2. Unpublish scenario\n *\n * Unpublish consist in 3 steps.\n * 1. Try to fetch metadata -> if it fails, return 404\n * 2. Compute metadata locally (client side) and send a mutate payload excluding the version to\n * be unpublished\n * eg: if metadata reflects 1.0.1, 1.0.2 and 1.0.3, the computed metadata won't include 1.0.3.\n * 3. Once the second step has been successfully finished, delete the tarball.\n *\n * All these steps are consecutive and required, there is no transacions here, if step 3 fails,\n * metadata might get corrupted.\n *\n * Note the unpublish call will suffix in the url a /-rev/14-5d500cfce92f90fd revision number,\n * this not\n * used internally.\n *\n *\n * Example flow of unpublish.\n *\n * There are two possible flows:\n *\n * - Remove all packages (entirely)\n * eg: npm unpublish package-name@* --force\n * eg: npm unpublish package-name --force\n *\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * - Remove a specific version\n * eg: npm unpublish package-name@1.0.0 --force\n *\n * Get fresh manifest\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * Update manifest without the version to be unpublished\n * npm http fetch PUT 201 http://localhost:4873/custom-name/-rev/14-5d500cfce92f90fd 956606ms\n * Get fresh manifest (revision should be different)\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1601ms\n * Remove the tarball\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * 3. Change owners of a package\n *\n * Changing owners (maintainers) of a package uses the publish endpoint.\n *\n * The body includes a list of the new owners with the following format\n *\n * {\n \"_id\": pkgName,\n \"_rev\": \"4-b0cdaefc9bdb77c8\",\n \"maintainers\": [\n { \"name\": \"first owner\", \"email\": \"me@verdaccio.org\" },\n { \"name\": \"second owner\", \"email\": \"you@verdaccio.org\" },\n ...\n ]\n }\n *\n */\nexport default function publish(\n router: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n router.put(\n PUBLISH_API_ENDPOINTS.add_package,\n can('publish'),\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish one version')\n );\n\n router.put(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish with revision')\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when any of these scenarios happens:\n * - the first call detect there is only one version remaining\n * - no version is specified in the unpublish call\n * - all versions are removed npm unpublish package@*\n * - there is no versions on the metadata\n\n * then the client decides to DELETE the resource\n * Example:\n * Get fresh manifest (write=true is a flag to get the latest revision)\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * Send request to delete the package, this includes the revision number that must match\n * and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = reqUtils.paramToString(req.params.package);\n const rev = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug({ packageName }, `unpublishing @{packageName}`);\n try {\n await storage.removePackage(packageName, rev, username);\n debug('package %s unpublished', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of package removal\n const metadata: Partial<Manifest> = { name: packageName, _rev: rev };\n\n void notify(metadata, config, req.remote_user, packageName, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.PKG_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n\n /*\n Remove a tarball, this happens when npm unpublish a package unique version.\n npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.remove_tarball,\n can('unpublish'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const filename = reqUtils.paramToString(req.params.filename);\n const revision = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug(\n { packageName, filename, revision },\n `removing a tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n try {\n await storage.removeTarball(packageName, filename, revision, username);\n res.status(HTTP_STATUS.CREATED);\n\n logger.debug(\n { packageName, filename, revision },\n `success remove tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n\n // send notification of version removal\n // getVersionFromTarball returns undefined when the filename is not parseable;\n // fall back to the package name so we never report `name@undefined`\n const version = tarballUtils.getVersionFromTarball(filename);\n const metadata: Partial<Manifest> = { name: packageName, _rev: revision };\n const publishedPackage = version ? `${packageName}@${version}` : packageName;\n\n void notify(metadata, config, req.remote_user, publishedPackage, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(\n storage: Storage,\n config: Config,\n logger: Logger,\n origin: string\n): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug(origin);\n const ac = new AbortController();\n const packageName = reqUtils.paramToString(req.params.package);\n const revision = reqUtils.paramToString(req.params.revision);\n debug('publishing package %s', packageName);\n debug('revision %s', revision);\n if (debug.enabled) {\n debug('body %o', req.body);\n }\n const metadata = req.body;\n\n logger.debug({ packageName }, `publishing package @{packageName}`);\n\n const requestOptions = getRequestOptions(req);\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions,\n uplinksLook: false,\n });\n debug('package %s published', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of publication (notification step, non transactional)\n // a publish body is a packument; the published version is the single entry\n // under `versions` (see storage.updateManifest), not a top-level field\n const [version] = Object.keys(metadata.versions ?? {});\n void notify(\n metadata,\n config,\n req.remote_user,\n `${metadata.name}@${version}`,\n 'publish'\n ).catch((error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n });\n\n return next({\n success: true,\n ok: message,\n });\n } catch (err: any) {\n // TODO: review if we need the abort controller here\n next(err);\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"publish.mjs","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { RequestHandler, Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HEADERS, HTTP_STATUS, reqUtils, tarballUtils } from '@verdaccio/core';\nimport { notify } from '@verdaccio/hooks';\nimport {\n PUBLISH_API_ENDPOINTS,\n allow,\n expectJson,\n getRequestOptions,\n media,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Config, Logger, Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package\n *\n * There are multiples scenarios here to be considered:\n *\n * 1. Publish scenario\n *\n * Publish a package consist of at least 1 step (PUT) with a metadata payload.\n * When a package is published, an _attachment property is present that contains the data\n * of the tarball.\n *\n * Example flow of publish.\n *\n * npm http fetch PUT 201 http://localhost:4873/@scope%2ftest1 9627ms\n npm info lifecycle @scope/test1@1.0.1~publish: @scope/test1@1.0.1\n npm info lifecycle @scope/test1@1.0.1~postpublish: @scope/test1@1.0.1\n + @scope/test1@1.0.1\n npm verb exit [ 0, true ]\n *\n *\n * 2. Unpublish scenario\n *\n * Unpublish consist in 3 steps.\n * 1. Try to fetch metadata -> if it fails, return 404\n * 2. Compute metadata locally (client side) and send a mutate payload excluding the version to\n * be unpublished\n * eg: if metadata reflects 1.0.1, 1.0.2 and 1.0.3, the computed metadata won't include 1.0.3.\n * 3. Once the second step has been successfully finished, delete the tarball.\n *\n * All these steps are consecutive and required, there is no transacions here, if step 3 fails,\n * metadata might get corrupted.\n *\n * Note the unpublish call will suffix in the url a /-rev/14-5d500cfce92f90fd revision number,\n * this not\n * used internally.\n *\n *\n * Example flow of unpublish.\n *\n * There are two possible flows:\n *\n * - Remove all packages (entirely)\n * eg: npm unpublish package-name@* --force\n * eg: npm unpublish package-name --force\n *\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * - Remove a specific version\n * eg: npm unpublish package-name@1.0.0 --force\n *\n * Get fresh manifest\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1680ms\n * Update manifest without the version to be unpublished\n * npm http fetch PUT 201 http://localhost:4873/custom-name/-rev/14-5d500cfce92f90fd 956606ms\n * Get fresh manifest (revision should be different)\n * npm http fetch GET 200 http://localhost:4873/custom-name?write=true 1601ms\n * Remove the tarball\n * npm http fetch DELETE 201 http://localhost:4873/custom-name/-/test1-1.0.3.tgz/-rev/16-e11c8db282b2d992 19ms\n *\n * 3. Change owners of a package\n *\n * Changing owners (maintainers) of a package uses the publish endpoint.\n *\n * The body includes a list of the new owners with the following format\n *\n * {\n \"_id\": pkgName,\n \"_rev\": \"4-b0cdaefc9bdb77c8\",\n \"maintainers\": [\n { \"name\": \"first owner\", \"email\": \"me@verdaccio.org\" },\n { \"name\": \"second owner\", \"email\": \"you@verdaccio.org\" },\n ...\n ]\n }\n *\n */\nexport default function publish(\n router: Router,\n auth: Auth,\n storage: Storage,\n config: Config,\n logger: Logger,\n /** No-op unless the caller has two-factor in `auth-and-writes` mode. */\n requireOtp: RequestHandler = (_req, _res, next) => next()\n): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n router.put(\n PUBLISH_API_ENDPOINTS.add_package,\n can('publish'),\n requireOtp,\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish one version')\n );\n\n router.put(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n requireOtp,\n media(HEADERS.JSON),\n expectJson,\n publishPackage(storage, config, logger, 'publish with revision')\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when any of these scenarios happens:\n * - the first call detect there is only one version remaining\n * - no version is specified in the unpublish call\n * - all versions are removed npm unpublish package@*\n * - there is no versions on the metadata\n\n * then the client decides to DELETE the resource\n * Example:\n * Get fresh manifest (write=true is a flag to get the latest revision)\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * Send request to delete the package, this includes the revision number that must match\n * and the package name, it will delete the entire package and all tarballs (or tarball depends the scenario)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.publish_package,\n can('unpublish'),\n requireOtp,\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = reqUtils.paramToString(req.params.package);\n const rev = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug({ packageName }, `unpublishing @{packageName}`);\n try {\n await storage.removePackage(packageName, rev, username);\n debug('package %s unpublished', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of package removal\n const metadata: Partial<Manifest> = { name: packageName, _rev: rev };\n\n void notify(metadata, config, req.remote_user, packageName, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.PKG_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n\n /*\n Remove a tarball, this happens when npm unpublish a package unique version.\n npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n PUBLISH_API_ENDPOINTS.remove_tarball,\n can('unpublish'),\n requireOtp,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = reqUtils.paramToString(req.params.package);\n const filename = reqUtils.paramToString(req.params.filename);\n const revision = reqUtils.paramToString(req.params.revision);\n const username = req?.remote_user?.name;\n\n logger.debug(\n { packageName, filename, revision },\n `removing a tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n try {\n await storage.removeTarball(packageName, filename, revision, username);\n res.status(HTTP_STATUS.CREATED);\n\n logger.debug(\n { packageName, filename, revision },\n `success remove tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n\n // send notification of version removal\n // getVersionFromTarball returns undefined when the filename is not parseable;\n // fall back to the package name so we never report `name@undefined`\n const version = tarballUtils.getVersionFromTarball(filename);\n const metadata: Partial<Manifest> = { name: packageName, _rev: revision };\n const publishedPackage = version ? `${packageName}@${version}` : packageName;\n\n void notify(metadata, config, req.remote_user, publishedPackage, 'unpublish').catch(\n (error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n }\n );\n\n return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(\n storage: Storage,\n config: Config,\n logger: Logger,\n origin: string\n): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug(origin);\n const ac = new AbortController();\n const packageName = reqUtils.paramToString(req.params.package);\n const revision = reqUtils.paramToString(req.params.revision);\n debug('publishing package %s', packageName);\n debug('revision %s', revision);\n if (debug.enabled) {\n debug('body %o', req.body);\n }\n const metadata = req.body;\n\n logger.debug({ packageName }, `publishing package @{packageName}`);\n\n const requestOptions = getRequestOptions(req);\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions,\n uplinksLook: false,\n });\n debug('package %s published', packageName);\n res.status(HTTP_STATUS.CREATED);\n\n // send notification of publication (notification step, non transactional)\n // a publish body is a packument; the published version is the single entry\n // under `versions` (see storage.updateManifest), not a top-level field\n const [version] = Object.keys(metadata.versions ?? {});\n void notify(\n metadata,\n config,\n req.remote_user,\n `${metadata.name}@${version}`,\n 'publish'\n ).catch((error: any) => {\n logger.error({ error: error?.message }, 'notify batch service has failed: @{error}');\n });\n\n return next({\n success: true,\n ok: message,\n });\n } catch (err: any) {\n // TODO: review if we need the abort controller here\n next(err);\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Request, RequestHandler } from 'express';
|
|
2
|
+
import type { TfaStore } from '@verdaccio/auth';
|
|
3
|
+
import type { Logger } from '@verdaccio/types';
|
|
4
|
+
/**
|
|
5
|
+
* Header carrying the one-time password.
|
|
6
|
+
*
|
|
7
|
+
* npm sets it from `opts.otp` (`npm-registry-fetch/lib/index.js`) and Yarn from
|
|
8
|
+
* `getOtpHeaders` (`plugin-npm/sources/npmHttpUtils.ts`); both spell it the same.
|
|
9
|
+
*/
|
|
10
|
+
export declare const OTP_HEADER = "npm-otp";
|
|
11
|
+
/**
|
|
12
|
+
* The challenge value clients look for.
|
|
13
|
+
*
|
|
14
|
+
* npm only raises `EOTP` when a 401 carries a `www-authenticate` header whose
|
|
15
|
+
* comma-separated values include `otp`, and Yarn's `isOtpError` does the same.
|
|
16
|
+
* Anything else — including the `Bearer` that `final` adds to every other 401 —
|
|
17
|
+
* surfaces as a plain authentication failure and the client never retries.
|
|
18
|
+
*/
|
|
19
|
+
export declare const OTP_CHALLENGE = "otp";
|
|
20
|
+
/**
|
|
21
|
+
* Which operations a mode covers.
|
|
22
|
+
*
|
|
23
|
+
* - `auth`: logging in and minting tokens, required by both modes
|
|
24
|
+
* - `write`: publishing and other mutations, required only by `auth-and-writes`
|
|
25
|
+
*/
|
|
26
|
+
export type OtpScope = 'auth' | 'write';
|
|
27
|
+
export interface RequireOtpOptions {
|
|
28
|
+
/** Absent when `flags.tfa` is off, which turns the middleware into a no-op. */
|
|
29
|
+
tfaStore?: TfaStore;
|
|
30
|
+
scope: OtpScope;
|
|
31
|
+
logger: Logger;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve whose two-factor configuration applies.
|
|
34
|
+
*
|
|
35
|
+
* Defaults to the authenticated user, but login has no authenticated user
|
|
36
|
+
* yet — there the name comes off the request body.
|
|
37
|
+
*/
|
|
38
|
+
getUsername?: (req: Request) => string | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Demand a one-time password from users who have two-factor enabled.
|
|
42
|
+
*
|
|
43
|
+
* A no-op when the flag is off, when the user has no two-factor configured, or
|
|
44
|
+
* when their mode does not cover this operation, so requests from everybody
|
|
45
|
+
* else are untouched.
|
|
46
|
+
*/
|
|
47
|
+
export declare function requireOtp(options: RequireOtpOptions): RequestHandler;
|
|
48
|
+
/**
|
|
49
|
+
* Fail fast when two-factor is enabled on a storage plugin that cannot hold it.
|
|
50
|
+
*
|
|
51
|
+
* `Storage.readTokens` rejects with 503 when the plugin lacks the token
|
|
52
|
+
* interface. Without this check that rejection would surface on every single
|
|
53
|
+
* write, for every user, including those who never enabled two-factor.
|
|
54
|
+
*/
|
|
55
|
+
export declare function assertTokenStoreSupport(storage: any, logger: Logger): void;
|