@verdaccio/api 7.0.0-next-7.16 → 7.0.0-next-7.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @verdaccio/api
2
2
 
3
+ ## 7.0.0-next-7.18
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [10dd81f]
8
+ - @verdaccio/middleware@7.0.0-next-7.18
9
+ - @verdaccio/config@7.0.0-next-7.18
10
+ - @verdaccio/auth@7.0.0-next-7.18
11
+ - @verdaccio/core@7.0.0-next-7.18
12
+ - @verdaccio/logger@7.0.0-next-7.18
13
+ - @verdaccio/store@7.0.0-next-7.18
14
+ - @verdaccio/utils@7.0.0-next-7.18
15
+
16
+ ## 7.0.0-next-7.17
17
+
18
+ ### Patch Changes
19
+
20
+ - 6e764e3: feat: add support for npm owner
21
+ - Updated dependencies [6e764e3]
22
+ - Updated dependencies [de6ff5c]
23
+ - @verdaccio/config@7.0.0-next-7.17
24
+ - @verdaccio/core@7.0.0-next-7.17
25
+ - @verdaccio/store@7.0.0-next-7.17
26
+ - @verdaccio/auth@7.0.0-next-7.17
27
+ - @verdaccio/logger@7.0.0-next-7.17
28
+ - @verdaccio/middleware@7.0.0-next-7.17
29
+ - @verdaccio/utils@7.0.0-next-7.17
30
+
3
31
  ## 7.0.0-next-7.16
4
32
 
5
33
  ### Patch Changes
package/build/package.js CHANGED
@@ -17,10 +17,12 @@ function _default(route, auth, storage) {
17
17
  afterAll: (a, b) => _logger.logger.trace(a, b)
18
18
  });
19
19
  route.get('/:package/:version?', can('access'), async function (req, _res, next) {
20
+ var _req$remote_user;
20
21
  debug('init package by version');
21
22
  const name = req.params.package;
22
23
  let version = req.params.version;
23
24
  const write = req.query.write === 'true';
25
+ const username = req === null || req === void 0 ? void 0 : (_req$remote_user = req.remote_user) === null || _req$remote_user === void 0 ? void 0 : _req$remote_user.name;
24
26
  const abbreviated = _core.stringUtils.getByQualityPriorityValue(req.get('Accept')) === _store.Storage.ABBREVIATED_HEADER;
25
27
  const requestOptions = {
26
28
  protocol: req.protocol,
@@ -28,7 +30,8 @@ function _default(route, auth, storage) {
28
30
  // FIXME: if we migrate to req.hostname, the port is not longer included.
29
31
  host: req.host,
30
32
  remoteAddress: req.socket.remoteAddress,
31
- byPassCache: write
33
+ byPassCache: write,
34
+ username
32
35
  };
33
36
  try {
34
37
  const manifest = await storage.getPackageByOptions({
@@ -1 +1 @@
1
- {"version":3,"file":"package.js","names":["_debug","_interopRequireDefault","require","_core","_logger","_middleware","_store","obj","__esModule","default","debug","buildDebug","_default","route","auth","storage","can","allow","beforeAll","a","b","logger","trace","afterAll","get","req","_res","next","name","params","package","version","write","query","abbreviated","stringUtils","getByQualityPriorityValue","Storage","ABBREVIATED_HEADER","requestOptions","protocol","headers","host","remoteAddress","socket","byPassCache","manifest","getPackageByOptions","uplinksLook","setHeader","HEADER_TYPE","CONTENT_TYPE","HEADERS","JSON_INSTALL_CHARSET","JSON","err","res","pkgName","filename","abort","AbortController","stream","getTarball","signal","on","size","header","CONTENT_LENGTH","once","locals","report_error","url","OCTET_STREAM","pipe"],"sources":["../src/package.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport { Auth } from '@verdaccio/auth';\nimport { HEADERS, HEADER_TYPE, stringUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { allow } from '@verdaccio/middleware';\nimport { Storage } from '@verdaccio/store';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:package');\n\nexport default function (route: Router, auth: Auth, storage: Storage): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n route.get(\n '/:package/:version?',\n can('access'),\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('init package by version');\n const name = req.params.package;\n let version = req.params.version;\n const write = req.query.write === 'true';\n const abbreviated =\n stringUtils.getByQualityPriorityValue(req.get('Accept')) === Storage.ABBREVIATED_HEADER;\n const requestOptions = {\n protocol: req.protocol,\n headers: req.headers as any,\n // FIXME: if we migrate to req.hostname, the port is not longer included.\n host: req.host,\n remoteAddress: req.socket.remoteAddress,\n byPassCache: write,\n };\n\n try {\n const manifest = await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated,\n version,\n requestOptions,\n });\n if (abbreviated) {\n _res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_INSTALL_CHARSET);\n } else {\n _res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);\n }\n\n next(manifest);\n } catch (err) {\n next(err);\n }\n }\n );\n\n route.get(\n '/:package/-/:filename',\n can('access'),\n async function (req: $RequestExtend, res: $ResponseExtend, next): Promise<void> {\n const { package: pkgName, filename } = req.params;\n const abort = new AbortController();\n try {\n const stream = (await storage.getTarball(pkgName, filename, {\n signal: abort.signal,\n // TODO: review why this param\n // enableRemote: true,\n })) as any;\n\n stream.on('content-length', (size) => {\n res.header(HEADER_TYPE.CONTENT_LENGTH, size);\n });\n\n stream.once('error', (err) => {\n res.locals.report_error(err);\n next(err);\n });\n\n req.on('abort', () => {\n debug('request aborted for %o', req.url);\n abort.abort();\n });\n\n res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);\n stream.pipe(res);\n } catch (err: any) {\n // console.log('catch API error request', err);\n res.locals.report_error(err);\n next(err);\n }\n }\n );\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAIA,IAAAC,KAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,WAAA,GAAAH,OAAA;AACA,IAAAI,MAAA,GAAAJ,OAAA;AAA2C,SAAAD,uBAAAM,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAI3C,MAAMG,KAAK,GAAG,IAAAC,cAAU,EAAC,uBAAuB,CAAC;AAElC,SAAAC,SAAUC,KAAa,EAAEC,IAAU,EAAEC,OAAgB,EAAQ;EAC1E,MAAMC,GAAG,GAAG,IAAAC,iBAAK,EAACH,IAAI,EAAE;IACtBI,SAAS,EAAEA,CAACC,CAAC,EAAEC,CAAC,KAAKC,cAAM,CAACC,KAAK,CAACH,CAAC,EAAEC,CAAC,CAAC;IACvCG,QAAQ,EAAEA,CAACJ,CAAC,EAAEC,CAAC,KAAKC,cAAM,CAACC,KAAK,CAACH,CAAC,EAAEC,CAAC;EACvC,CAAC,CAAC;EACFP,KAAK,CAACW,GAAG,CACP,qBAAqB,EACrBR,GAAG,CAAC,QAAQ,CAAC,EACb,gBACES,GAAmB,EACnBC,IAAqB,EACrBC,IAAsB,EACP;IACfjB,KAAK,CAAC,yBAAyB,CAAC;IAChC,MAAMkB,IAAI,GAAGH,GAAG,CAACI,MAAM,CAACC,OAAO;IAC/B,IAAIC,OAAO,GAAGN,GAAG,CAACI,MAAM,CAACE,OAAO;IAChC,MAAMC,KAAK,GAAGP,GAAG,CAACQ,KAAK,CAACD,KAAK,KAAK,MAAM;IACxC,MAAME,WAAW,GACfC,iBAAW,CAACC,yBAAyB,CAACX,GAAG,CAACD,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAKa,cAAO,CAACC,kBAAkB;IACzF,MAAMC,cAAc,GAAG;MACrBC,QAAQ,EAAEf,GAAG,CAACe,QAAQ;MACtBC,OAAO,EAAEhB,GAAG,CAACgB,OAAc;MAC3B;MACAC,IAAI,EAAEjB,GAAG,CAACiB,IAAI;MACdC,aAAa,EAAElB,GAAG,CAACmB,MAAM,CAACD,aAAa;MACvCE,WAAW,EAAEb;IACf,CAAC;IAED,IAAI;MACF,MAAMc,QAAQ,GAAG,MAAM/B,OAAO,CAACgC,mBAAmB,CAAC;QACjDnB,IAAI;QACJoB,WAAW,EAAE,IAAI;QACjBd,WAAW;QACXH,OAAO;QACPQ;MACF,CAAC,CAAC;MACF,IAAIL,WAAW,EAAE;QACfR,IAAI,CAACuB,SAAS,CAACC,iBAAW,CAACC,YAAY,EAAEC,aAAO,CAACC,oBAAoB,CAAC;MACxE,CAAC,MAAM;QACL3B,IAAI,CAACuB,SAAS,CAACC,iBAAW,CAACC,YAAY,EAAEC,aAAO,CAACE,IAAI,CAAC;MACxD;MAEA3B,IAAI,CAACmB,QAAQ,CAAC;IAChB,CAAC,CAAC,OAAOS,GAAG,EAAE;MACZ5B,IAAI,CAAC4B,GAAG,CAAC;IACX;EACF,CACF,CAAC;EAED1C,KAAK,CAACW,GAAG,CACP,uBAAuB,EACvBR,GAAG,CAAC,QAAQ,CAAC,EACb,gBAAgBS,GAAmB,EAAE+B,GAAoB,EAAE7B,IAAI,EAAiB;IAC9E,MAAM;MAAEG,OAAO,EAAE2B,OAAO;MAAEC;IAAS,CAAC,GAAGjC,GAAG,CAACI,MAAM;IACjD,MAAM8B,KAAK,GAAG,IAAIC,eAAe,CAAC,CAAC;IACnC,IAAI;MACF,MAAMC,MAAM,GAAI,MAAM9C,OAAO,CAAC+C,UAAU,CAACL,OAAO,EAAEC,QAAQ,EAAE;QAC1DK,MAAM,EAAEJ,KAAK,CAACI;QACd;QACA;MACF,CAAC,CAAS;MAEVF,MAAM,CAACG,EAAE,CAAC,gBAAgB,EAAGC,IAAI,IAAK;QACpCT,GAAG,CAACU,MAAM,CAAChB,iBAAW,CAACiB,cAAc,EAAEF,IAAI,CAAC;MAC9C,CAAC,CAAC;MAEFJ,MAAM,CAACO,IAAI,CAAC,OAAO,EAAGb,GAAG,IAAK;QAC5BC,GAAG,CAACa,MAAM,CAACC,YAAY,CAACf,GAAG,CAAC;QAC5B5B,IAAI,CAAC4B,GAAG,CAAC;MACX,CAAC,CAAC;MAEF9B,GAAG,CAACuC,EAAE,CAAC,OAAO,EAAE,MAAM;QACpBtD,KAAK,CAAC,wBAAwB,EAAEe,GAAG,CAAC8C,GAAG,CAAC;QACxCZ,KAAK,CAACA,KAAK,CAAC,CAAC;MACf,CAAC,CAAC;MAEFH,GAAG,CAACU,MAAM,CAACd,aAAO,CAACD,YAAY,EAAEC,aAAO,CAACoB,YAAY,CAAC;MACtDX,MAAM,CAACY,IAAI,CAACjB,GAAG,CAAC;IAClB,CAAC,CAAC,OAAOD,GAAQ,EAAE;MACjB;MACAC,GAAG,CAACa,MAAM,CAACC,YAAY,CAACf,GAAG,CAAC;MAC5B5B,IAAI,CAAC4B,GAAG,CAAC;IACX;EACF,CACF,CAAC;AACH"}
1
+ {"version":3,"file":"package.js","names":["_debug","_interopRequireDefault","require","_core","_logger","_middleware","_store","obj","__esModule","default","debug","buildDebug","_default","route","auth","storage","can","allow","beforeAll","a","b","logger","trace","afterAll","get","req","_res","next","_req$remote_user","name","params","package","version","write","query","username","remote_user","abbreviated","stringUtils","getByQualityPriorityValue","Storage","ABBREVIATED_HEADER","requestOptions","protocol","headers","host","remoteAddress","socket","byPassCache","manifest","getPackageByOptions","uplinksLook","setHeader","HEADER_TYPE","CONTENT_TYPE","HEADERS","JSON_INSTALL_CHARSET","JSON","err","res","pkgName","filename","abort","AbortController","stream","getTarball","signal","on","size","header","CONTENT_LENGTH","once","locals","report_error","url","OCTET_STREAM","pipe"],"sources":["../src/package.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport { Auth } from '@verdaccio/auth';\nimport { HEADERS, HEADER_TYPE, stringUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { allow } from '@verdaccio/middleware';\nimport { Storage } from '@verdaccio/store';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:package');\n\nexport default function (route: Router, auth: Auth, storage: Storage): void {\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n route.get(\n '/:package/:version?',\n can('access'),\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('init package by version');\n const name = req.params.package;\n let version = req.params.version;\n const write = req.query.write === 'true';\n const username = req?.remote_user?.name;\n const abbreviated =\n stringUtils.getByQualityPriorityValue(req.get('Accept')) === Storage.ABBREVIATED_HEADER;\n const requestOptions = {\n protocol: req.protocol,\n headers: req.headers as any,\n // FIXME: if we migrate to req.hostname, the port is not longer included.\n host: req.host,\n remoteAddress: req.socket.remoteAddress,\n byPassCache: write,\n username,\n };\n\n try {\n const manifest = await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated,\n version,\n requestOptions,\n });\n if (abbreviated) {\n _res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_INSTALL_CHARSET);\n } else {\n _res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);\n }\n\n next(manifest);\n } catch (err) {\n next(err);\n }\n }\n );\n\n route.get(\n '/:package/-/:filename',\n can('access'),\n async function (req: $RequestExtend, res: $ResponseExtend, next): Promise<void> {\n const { package: pkgName, filename } = req.params;\n const abort = new AbortController();\n try {\n const stream = (await storage.getTarball(pkgName, filename, {\n signal: abort.signal,\n // TODO: review why this param\n // enableRemote: true,\n })) as any;\n\n stream.on('content-length', (size) => {\n res.header(HEADER_TYPE.CONTENT_LENGTH, size);\n });\n\n stream.once('error', (err) => {\n res.locals.report_error(err);\n next(err);\n });\n\n req.on('abort', () => {\n debug('request aborted for %o', req.url);\n abort.abort();\n });\n\n res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);\n stream.pipe(res);\n } catch (err: any) {\n // console.log('catch API error request', err);\n res.locals.report_error(err);\n next(err);\n }\n }\n );\n}\n"],"mappings":"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"}
@@ -63,11 +63,11 @@ import { Storage } from '@verdaccio/store';
63
63
  *
64
64
  * 3. Star a package
65
65
  *
66
- * Permissions: start a package depends of the publish and unpublish permissions, there is no
67
- * specific flag for star or un start.
66
+ * Permissions: staring a package depends of the publish and unpublish permissions, there is no
67
+ * specific flag for star or unstar.
68
68
  * The URL for star is similar to the unpublish (change package format)
69
69
  *
70
- * npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference
70
+ * npm has no endpoint for staring a package, rather mutate the metadata and acts as, the difference
71
71
  * is the users property which is part of the payload and the body only includes
72
72
  *
73
73
  * {
@@ -76,7 +76,24 @@ import { Storage } from '@verdaccio/store';
76
76
  "users": {
77
77
  [username]: boolean value (true, false)
78
78
  }
79
- }
79
+ }
80
+ *
81
+ * 4. Change owners of a package
82
+ *
83
+ * Similar to staring a package, changing owners (maintainers) of a package uses the publish
84
+ * endpoint.
85
+ *
86
+ * The body includes a list of the new owners with the following format
87
+ *
88
+ * {
89
+ "_id": pkgName,
90
+ "_rev": "4-b0cdaefc9bdb77c8",
91
+ "maintainers": [
92
+ { "name": "first owner", "email": "me@verdaccio.org" },
93
+ { "name": "second owner", "email": "you@verdaccio.org" },
94
+ ...
95
+ ]
96
+ }
80
97
  *
81
98
  */
82
99
  export default function publish(router: Router, auth: Auth, storage: Storage): void;
package/build/publish.js CHANGED
@@ -77,11 +77,11 @@ const debug = (0, _debug.default)('verdaccio:api:publish');
77
77
  *
78
78
  * 3. Star a package
79
79
  *
80
- * Permissions: start a package depends of the publish and unpublish permissions, there is no
81
- * specific flag for star or un start.
80
+ * Permissions: staring a package depends of the publish and unpublish permissions, there is no
81
+ * specific flag for star or unstar.
82
82
  * The URL for star is similar to the unpublish (change package format)
83
83
  *
84
- * npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference
84
+ * npm has no endpoint for staring a package, rather mutate the metadata and acts as, the difference
85
85
  * is the users property which is part of the payload and the body only includes
86
86
  *
87
87
  * {
@@ -90,7 +90,24 @@ const debug = (0, _debug.default)('verdaccio:api:publish');
90
90
  "users": {
91
91
  [username]: boolean value (true, false)
92
92
  }
93
- }
93
+ }
94
+ *
95
+ * 4. Change owners of a package
96
+ *
97
+ * Similar to staring a package, changing owners (maintainers) of a package uses the publish
98
+ * endpoint.
99
+ *
100
+ * The body includes a list of the new owners with the following format
101
+ *
102
+ * {
103
+ "_id": pkgName,
104
+ "_rev": "4-b0cdaefc9bdb77c8",
105
+ "maintainers": [
106
+ { "name": "first owner", "email": "me@verdaccio.org" },
107
+ { "name": "second owner", "email": "you@verdaccio.org" },
108
+ ...
109
+ ]
110
+ }
94
111
  *
95
112
  */
96
113
  function publish(router, auth, storage) {
@@ -110,13 +127,15 @@ function publish(router, auth, storage) {
110
127
  * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms
111
128
  */
112
129
  router.delete('/:package/-rev/:revision', can('unpublish'), async function (req, res, next) {
130
+ var _req$remote_user;
113
131
  const packageName = req.params.package;
114
132
  const rev = req.params.revision;
133
+ const username = req === null || req === void 0 ? void 0 : (_req$remote_user = req.remote_user) === null || _req$remote_user === void 0 ? void 0 : _req$remote_user.name;
115
134
  _logger.logger.debug({
116
135
  packageName
117
136
  }, `unpublishing @{packageName}`);
118
137
  try {
119
- await storage.removePackage(packageName, rev);
138
+ await storage.removePackage(packageName, rev, username);
120
139
  debug('package %s unpublished', packageName);
121
140
  res.status(_core.HTTP_STATUS.CREATED);
122
141
  return next({
@@ -132,18 +151,20 @@ function publish(router, auth, storage) {
132
151
  npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms
133
152
  */
134
153
  router.delete('/:package/-/:filename/-rev/:revision', can('unpublish'), can('publish'), async function (req, res, next) {
154
+ var _req$remote_user2;
135
155
  const packageName = req.params.package;
136
156
  const {
137
157
  filename,
138
158
  revision
139
159
  } = req.params;
160
+ const username = req === null || req === void 0 ? void 0 : (_req$remote_user2 = req.remote_user) === null || _req$remote_user2 === void 0 ? void 0 : _req$remote_user2.name;
140
161
  _logger.logger.debug({
141
162
  packageName,
142
163
  filename,
143
164
  revision
144
165
  }, `removing a tarball for @{packageName}-@{tarballName}-@{revision}`);
145
166
  try {
146
- await storage.removeTarball(packageName, filename, revision);
167
+ await storage.removeTarball(packageName, filename, revision, username);
147
168
  res.status(_core.HTTP_STATUS.CREATED);
148
169
  _logger.logger.debug({
149
170
  packageName,
@@ -160,14 +181,19 @@ function publish(router, auth, storage) {
160
181
  }
161
182
  function publishPackage(storage) {
162
183
  return async function (req, res, next) {
163
- var _req$remote_user;
184
+ var _req$remote_user3;
164
185
  const ac = new AbortController();
165
186
  const packageName = req.params.package;
166
187
  const {
167
188
  revision
168
189
  } = req.params;
169
190
  const metadata = req.body;
170
- const username = req === null || req === void 0 ? void 0 : (_req$remote_user = req.remote_user) === null || _req$remote_user === void 0 ? void 0 : _req$remote_user.name;
191
+ const username = req === null || req === void 0 ? void 0 : (_req$remote_user3 = req.remote_user) === null || _req$remote_user3 === void 0 ? void 0 : _req$remote_user3.name;
192
+ debug('publishing package %o for user %o', packageName, username);
193
+ _logger.logger.debug({
194
+ packageName,
195
+ username
196
+ }, 'publishing package @{packageName} for user @{username}');
171
197
  try {
172
198
  const message = await storage.updateManifest(metadata, {
173
199
  name: packageName,
@@ -1 +1 @@
1
- {"version":3,"file":"publish.js","names":["_debug","_interopRequireDefault","require","_mime","_core","_logger","_middleware","obj","__esModule","default","debug","buildDebug","publish","router","auth","storage","can","allow","beforeAll","a","b","logger","trace","afterAll","put","media","mime","getType","expectJson","publishPackage","delete","req","res","next","packageName","params","package","rev","revision","removePackage","status","HTTP_STATUS","CREATED","ok","API_MESSAGE","PKG_REMOVED","err","filename","removeTarball","TARBALL_REMOVED","_req$remote_user","ac","AbortController","metadata","body","username","remote_user","name","message","updateManifest","signal","requestOptions","host","hostname","protocol","headers","uplinksLook","success"],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport mime from 'mime';\n\nimport { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HTTP_STATUS } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { allow, expectJson, media } from '@verdaccio/middleware';\nimport { Storage } from '@verdaccio/store';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\n// import star from './star';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package / un/start a 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. Star a package\n *\n * Permissions: start a package depends of the publish and unpublish permissions, there is no\n * specific flag for star or un start.\n * The URL for star is similar to the unpublish (change package format)\n *\n * npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference\n * is the users property which is part of the payload and the body only includes\n *\n * {\n\t\t \"_id\": pkgName,\n\t \t\"_rev\": \"3-b0cdaefc9bdb77c8\",\n\t\t \"users\": {\n\t\t [username]: boolean value (true, false)\n\t\t }\n\t}\n *\n */\nexport default function publish(router: Router, auth: Auth, storage: Storage): 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 '/:package',\n can('publish'),\n media(mime.getType('json')),\n expectJson,\n publishPackage(storage)\n );\n\n router.put(\n '/:package/-rev/:revision',\n can('unpublish'),\n media(mime.getType('json')),\n expectJson,\n publishPackage(storage)\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when the first call detect there is only one version remaining\n * in the metadata, then the client decides to DELETE the resource\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n '/:package/-rev/:revision',\n can('unpublish'),\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = req.params.package;\n const rev = req.params.revision;\n\n logger.debug({ packageName }, `unpublishing @{packageName}`);\n try {\n await storage.removePackage(packageName, rev);\n debug('package %s unpublished', packageName);\n res.status(HTTP_STATUS.CREATED);\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 '/:package/-/:filename/-rev/:revision',\n can('unpublish'),\n can('publish'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = req.params.package;\n const { filename, revision } = req.params;\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);\n res.status(HTTP_STATUS.CREATED);\n\n logger.debug(\n { packageName, filename, revision },\n `success remove tarball for @{packageName}-@{tarballName}-@{revision}`\n );\n return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(storage: Storage): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const ac = new AbortController();\n const packageName = req.params.package;\n const { revision } = req.params;\n const metadata = req.body;\n const username = req?.remote_user?.name;\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions: {\n host: req.hostname,\n protocol: req.protocol,\n headers: req.headers as { [key: string]: string },\n username,\n },\n uplinksLook: false,\n });\n\n res.status(HTTP_STATUS.CREATED);\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":["_debug","_interopRequireDefault","require","_mime","_core","_logger","_middleware","obj","__esModule","default","debug","buildDebug","publish","router","auth","storage","can","allow","beforeAll","a","b","logger","trace","afterAll","put","media","mime","getType","expectJson","publishPackage","delete","req","res","next","_req$remote_user","packageName","params","package","rev","revision","username","remote_user","name","removePackage","status","HTTP_STATUS","CREATED","ok","API_MESSAGE","PKG_REMOVED","err","_req$remote_user2","filename","removeTarball","TARBALL_REMOVED","_req$remote_user3","ac","AbortController","metadata","body","message","updateManifest","signal","requestOptions","host","hostname","protocol","headers","uplinksLook","success"],"sources":["../src/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport mime from 'mime';\n\nimport { Auth } from '@verdaccio/auth';\nimport { API_MESSAGE, HTTP_STATUS } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { allow, expectJson, media } from '@verdaccio/middleware';\nimport { Storage } from '@verdaccio/store';\n\nimport { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';\n\n// import star from './star';\n\nconst debug = buildDebug('verdaccio:api:publish');\n\n/**\n * Publish a package / update package / un/start a 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. Star a package\n *\n * Permissions: staring a package depends of the publish and unpublish permissions, there is no\n * specific flag for star or unstar.\n * The URL for star is similar to the unpublish (change package format)\n *\n * npm has no endpoint for staring a package, rather mutate the metadata and acts as, the difference\n * is the users property which is part of the payload and the body only includes\n *\n * {\n\t\t \"_id\": pkgName,\n\t \t\"_rev\": \"3-b0cdaefc9bdb77c8\",\n\t\t \"users\": {\n\t\t [username]: boolean value (true, false)\n\t\t }\n\t }\n *\n * 4. Change owners of a package\n *\n * Similar to staring a package, changing owners (maintainers) of a package uses the publish\n * endpoint. \n *\n * The body includes a list of the new owners with the following format\n *\n * {\n\t\t \"_id\": pkgName,\n\t \t\"_rev\": \"4-b0cdaefc9bdb77c8\",\n\t\t \"maintainers\": [\n { \"name\": \"first owner\", \"email\": \"me@verdaccio.org\" },\n { \"name\": \"second owner\", \"email\": \"you@verdaccio.org\" },\n ...\n\t\t ]\n\t }\n *\n */\nexport default function publish(router: Router, auth: Auth, storage: Storage): 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 '/:package',\n can('publish'),\n media(mime.getType('json')),\n expectJson,\n publishPackage(storage)\n );\n\n router.put(\n '/:package/-rev/:revision',\n can('unpublish'),\n media(mime.getType('json')),\n expectJson,\n publishPackage(storage)\n );\n\n /**\n * Un-publishing an entire package.\n *\n * This scenario happens when the first call detect there is only one version remaining\n * in the metadata, then the client decides to DELETE the resource\n * npm http fetch GET 304 http://localhost:4873/package-name?write=true 1076ms (from cache)\n * npm http fetch DELETE 201 http://localhost:4873/package-name/-rev/18-d8ebe3020bd4ac9c 22ms\n */\n router.delete(\n '/:package/-rev/:revision',\n can('unpublish'),\n async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {\n const packageName = req.params.package;\n const rev = 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 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 '/:package/-/:filename/-rev/:revision',\n can('unpublish'),\n can('publish'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const packageName = req.params.package;\n const { filename, revision } = req.params;\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 return next({ ok: API_MESSAGE.TARBALL_REMOVED });\n } catch (err) {\n return next(err);\n }\n }\n );\n}\n\nexport function publishPackage(storage: Storage): any {\n return async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const ac = new AbortController();\n const packageName = req.params.package;\n const { revision } = req.params;\n const metadata = req.body;\n const username = req?.remote_user?.name;\n\n debug('publishing package %o for user %o', packageName, username);\n logger.debug(\n { packageName, username },\n 'publishing package @{packageName} for user @{username}'\n );\n\n try {\n const message = await storage.updateManifest(metadata, {\n name: packageName,\n revision,\n signal: ac.signal,\n requestOptions: {\n host: req.hostname,\n protocol: req.protocol,\n headers: req.headers as { [key: string]: string },\n username,\n },\n uplinksLook: false,\n });\n\n res.status(HTTP_STATUS.CREATED);\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":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAEA,IAAAC,KAAA,GAAAF,sBAAA,CAAAC,OAAA;AAGA,IAAAE,KAAA,GAAAF,OAAA;AACA,IAAAG,OAAA,GAAAH,OAAA;AACA,IAAAI,WAAA,GAAAJ,OAAA;AAAiE,SAAAD,uBAAAM,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAKjE;;AAEA,MAAMG,KAAK,GAAG,IAAAC,cAAU,EAAC,uBAAuB,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASC,OAAOA,CAACC,MAAc,EAAEC,IAAU,EAAEC,OAAgB,EAAQ;EAClF,MAAMC,GAAG,GAAG,IAAAC,iBAAK,EAACH,IAAI,EAAE;IACtBI,SAAS,EAAEA,CAACC,CAAC,EAAEC,CAAC,KAAKC,cAAM,CAACC,KAAK,CAACH,CAAC,EAAEC,CAAC,CAAC;IACvCG,QAAQ,EAAEA,CAACJ,CAAC,EAAEC,CAAC,KAAKC,cAAM,CAACC,KAAK,CAACH,CAAC,EAAEC,CAAC;EACvC,CAAC,CAAC;EACFP,MAAM,CAACW,GAAG,CACR,WAAW,EACXR,GAAG,CAAC,SAAS,CAAC,EACd,IAAAS,iBAAK,EAACC,aAAI,CAACC,OAAO,CAAC,MAAM,CAAC,CAAC,EAC3BC,sBAAU,EACVC,cAAc,CAACd,OAAO,CACxB,CAAC;EAEDF,MAAM,CAACW,GAAG,CACR,0BAA0B,EAC1BR,GAAG,CAAC,WAAW,CAAC,EAChB,IAAAS,iBAAK,EAACC,aAAI,CAACC,OAAO,CAAC,MAAM,CAAC,CAAC,EAC3BC,sBAAU,EACVC,cAAc,CAACd,OAAO,CACxB,CAAC;;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEF,MAAM,CAACiB,MAAM,CACX,0BAA0B,EAC1Bd,GAAG,CAAC,WAAW,CAAC,EAChB,gBAAgBe,GAAmB,EAAEC,GAAoB,EAAEC,IAAsB,EAAE;IAAA,IAAAC,gBAAA;IACjF,MAAMC,WAAW,GAAGJ,GAAG,CAACK,MAAM,CAACC,OAAO;IACtC,MAAMC,GAAG,GAAGP,GAAG,CAACK,MAAM,CAACG,QAAQ;IAC/B,MAAMC,QAAQ,GAAGT,GAAG,aAAHA,GAAG,wBAAAG,gBAAA,GAAHH,GAAG,CAAEU,WAAW,cAAAP,gBAAA,uBAAhBA,gBAAA,CAAkBQ,IAAI;IAEvCrB,cAAM,CAACX,KAAK,CAAC;MAAEyB;IAAY,CAAC,EAAG,6BAA4B,CAAC;IAC5D,IAAI;MACF,MAAMpB,OAAO,CAAC4B,aAAa,CAACR,WAAW,EAAEG,GAAG,EAAEE,QAAQ,CAAC;MACvD9B,KAAK,CAAC,wBAAwB,EAAEyB,WAAW,CAAC;MAC5CH,GAAG,CAACY,MAAM,CAACC,iBAAW,CAACC,OAAO,CAAC;MAC/B,OAAOb,IAAI,CAAC;QAAEc,EAAE,EAAEC,iBAAW,CAACC;MAAY,CAAC,CAAC;IAC9C,CAAC,CAAC,OAAOC,GAAG,EAAE;MACZ,OAAOjB,IAAI,CAACiB,GAAG,CAAC;IAClB;EACF,CACF,CAAC;;EAED;AACF;AACA;AACA;EACErC,MAAM,CAACiB,MAAM,CACX,sCAAsC,EACtCd,GAAG,CAAC,WAAW,CAAC,EAChBA,GAAG,CAAC,SAAS,CAAC,EACd,gBACEe,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EACP;IAAA,IAAAkB,iBAAA;IACf,MAAMhB,WAAW,GAAGJ,GAAG,CAACK,MAAM,CAACC,OAAO;IACtC,MAAM;MAAEe,QAAQ;MAAEb;IAAS,CAAC,GAAGR,GAAG,CAACK,MAAM;IACzC,MAAMI,QAAQ,GAAGT,GAAG,aAAHA,GAAG,wBAAAoB,iBAAA,GAAHpB,GAAG,CAAEU,WAAW,cAAAU,iBAAA,uBAAhBA,iBAAA,CAAkBT,IAAI;IAEvCrB,cAAM,CAACX,KAAK,CACV;MAAEyB,WAAW;MAAEiB,QAAQ;MAAEb;IAAS,CAAC,EAClC,kEACH,CAAC;IACD,IAAI;MACF,MAAMxB,OAAO,CAACsC,aAAa,CAAClB,WAAW,EAAEiB,QAAQ,EAAEb,QAAQ,EAAEC,QAAQ,CAAC;MACtER,GAAG,CAACY,MAAM,CAACC,iBAAW,CAACC,OAAO,CAAC;MAE/BzB,cAAM,CAACX,KAAK,CACV;QAAEyB,WAAW;QAAEiB,QAAQ;QAAEb;MAAS,CAAC,EAClC,sEACH,CAAC;MACD,OAAON,IAAI,CAAC;QAAEc,EAAE,EAAEC,iBAAW,CAACM;MAAgB,CAAC,CAAC;IAClD,CAAC,CAAC,OAAOJ,GAAG,EAAE;MACZ,OAAOjB,IAAI,CAACiB,GAAG,CAAC;IAClB;EACF,CACF,CAAC;AACH;AAEO,SAASrB,cAAcA,CAACd,OAAgB,EAAO;EACpD,OAAO,gBACLgB,GAAmB,EACnBC,GAAoB,EACpBC,IAAsB,EACP;IAAA,IAAAsB,iBAAA;IACf,MAAMC,EAAE,GAAG,IAAIC,eAAe,CAAC,CAAC;IAChC,MAAMtB,WAAW,GAAGJ,GAAG,CAACK,MAAM,CAACC,OAAO;IACtC,MAAM;MAAEE;IAAS,CAAC,GAAGR,GAAG,CAACK,MAAM;IAC/B,MAAMsB,QAAQ,GAAG3B,GAAG,CAAC4B,IAAI;IACzB,MAAMnB,QAAQ,GAAGT,GAAG,aAAHA,GAAG,wBAAAwB,iBAAA,GAAHxB,GAAG,CAAEU,WAAW,cAAAc,iBAAA,uBAAhBA,iBAAA,CAAkBb,IAAI;IAEvChC,KAAK,CAAC,mCAAmC,EAAEyB,WAAW,EAAEK,QAAQ,CAAC;IACjEnB,cAAM,CAACX,KAAK,CACV;MAAEyB,WAAW;MAAEK;IAAS,CAAC,EACzB,wDACF,CAAC;IAED,IAAI;MACF,MAAMoB,OAAO,GAAG,MAAM7C,OAAO,CAAC8C,cAAc,CAACH,QAAQ,EAAE;QACrDhB,IAAI,EAAEP,WAAW;QACjBI,QAAQ;QACRuB,MAAM,EAAEN,EAAE,CAACM,MAAM;QACjBC,cAAc,EAAE;UACdC,IAAI,EAAEjC,GAAG,CAACkC,QAAQ;UAClBC,QAAQ,EAAEnC,GAAG,CAACmC,QAAQ;UACtBC,OAAO,EAAEpC,GAAG,CAACoC,OAAoC;UACjD3B;QACF,CAAC;QACD4B,WAAW,EAAE;MACf,CAAC,CAAC;MAEFpC,GAAG,CAACY,MAAM,CAACC,iBAAW,CAACC,OAAO,CAAC;MAE/B,OAAOb,IAAI,CAAC;QACVoC,OAAO,EAAE,IAAI;QACbtB,EAAE,EAAEa;MACN,CAAC,CAAC;IACJ,CAAC,CAAC,OAAOV,GAAQ,EAAE;MACjB;MACAjB,IAAI,CAACiB,GAAG,CAAC;IACX;EACF,CAAC;AACH"}
package/build/user.js CHANGED
@@ -16,10 +16,22 @@ const debug = (0, _debug.default)('verdaccio:api:user');
16
16
  function _default(route, auth, config) {
17
17
  route.get('/-/user/:org_couchdb_user', (0, _middleware.rateLimit)(config === null || config === void 0 ? void 0 : config.userRateLimit), function (req, res, next) {
18
18
  debug('verifying user');
19
+ if (typeof req.remote_user.name !== 'string' || req.remote_user.name === '') {
20
+ debug('user not logged in');
21
+ res.status(_core.HTTP_STATUS.OK);
22
+ return next({
23
+ ok: false
24
+ });
25
+ }
26
+ const username = req.params.org_couchdb_user.split(':')[1];
19
27
  const message = (0, _utils.getAuthenticatedMessage)(req.remote_user.name);
20
28
  debug('user authenticated message %o', message);
21
29
  res.status(_core.HTTP_STATUS.OK);
22
30
  next({
31
+ // 'npm owner' requires user info
32
+ // TODO: we don't have the email
33
+ name: username,
34
+ email: '',
23
35
  ok: message
24
36
  });
25
37
  });
@@ -49,6 +61,9 @@ function _default(route, auth, config) {
49
61
  } = req.body;
50
62
  debug('login or adduser');
51
63
  const remoteName = req === null || req === void 0 ? void 0 : (_req$remote_user = req.remote_user) === null || _req$remote_user === void 0 ? void 0 : _req$remote_user.name;
64
+ if (!_core.validatioUtils.validateUserName(req.params.org_couchdb_user, name)) {
65
+ return next(_core.errorUtils.getBadRequest(_core.API_ERROR.USERNAME_MISMATCH));
66
+ }
52
67
  if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {
53
68
  debug('login: no remote user detected');
54
69
  auth.authenticate(name, password, async function callbackAuthenticate(err, user) {
@@ -76,6 +91,7 @@ function _default(route, auth, config) {
76
91
  });
77
92
  } else {
78
93
  var _config$serverSetting;
94
+ debug('adduser: %o', name);
79
95
  if (_core.validatioUtils.validatePassword(password, config === null || config === void 0 ? void 0 : (_config$serverSetting = config.serverSettings) === null || _config$serverSetting === void 0 ? void 0 : _config$serverSetting.passwordValidationRegex) === false) {
80
96
  debug('adduser: invalid password');
81
97
  // eslint-disable-next-line new-cap
package/build/user.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","names":["_debug","_interopRequireDefault","require","_auth","_config","_core","_logger","_middleware","_utils","obj","__esModule","default","debug","buildDebug","_default","route","auth","config","get","rateLimit","userRateLimit","req","res","next","message","getAuthenticatedMessage","remote_user","name","status","HTTP_STATUS","OK","ok","put","_req$remote_user","password","body","remoteName","authenticate","callbackAuthenticate","err","user","logger","trace","errorUtils","getCode","UNAUTHORIZED","API_ERROR","BAD_USERNAME_PASSWORD","restoredRemoteUser","createRemoteUser","groups","token","getApiToken","getUnauthorized","CREATED","set","HEADERS","CACHE_CONTROL","_config$serverSetting","validatioUtils","validatePassword","serverSettings","passwordValidationRegex","BAD_REQUEST","PASSWORD_SHORT","add_user","INTERNAL_ERROR","getConflict","undefined","mask","delete","API_MESSAGE","LOGGED_OUT"],"sources":["../src/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Response, Router } from 'express';\n\nimport { getApiToken } from '@verdaccio/auth';\nimport { Auth } from '@verdaccio/auth';\nimport { createRemoteUser } from '@verdaccio/config';\nimport {\n API_ERROR,\n API_MESSAGE,\n HEADERS,\n HTTP_STATUS,\n errorUtils,\n validatioUtils,\n} from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { rateLimit } from '@verdaccio/middleware';\nimport { Config, RemoteUser } from '@verdaccio/types';\nimport { getAuthenticatedMessage, mask } from '@verdaccio/utils';\n\nimport { $NextFunctionVer, $RequestExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:user');\n\nexport default function (route: Router, auth: Auth, config: Config): void {\n route.get(\n '/-/user/:org_couchdb_user',\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n debug('verifying user');\n const message = getAuthenticatedMessage(req.remote_user.name);\n debug('user authenticated message %o', message);\n res.status(HTTP_STATUS.OK);\n next({\n ok: message,\n });\n }\n );\n\n /**\n * \n * body example\n * req.body = {\n _id: \"org.couchdb.user:jjjj\",\n name: \"jjjj\",\n password: \"jjjj\",\n type: \"user\",\n roles: [],\n date: \"2022-07-08T15:51:04.002Z\",\n }\n * \n * @export\n * @param {Router} route\n * @param {Auth} auth\n * @param {Config} config\n */\n route.put(\n '/-/user/:org_couchdb_user/:_rev?/:revision?',\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n const { name, password } = req.body;\n debug('login or adduser');\n const remoteName = req?.remote_user?.name;\n\n if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {\n debug('login: no remote user detected');\n auth.authenticate(\n name,\n password,\n async function callbackAuthenticate(err, user): Promise<void> {\n if (err) {\n logger.trace(\n { name, err },\n 'authenticating for user @{username} failed. Error: @{err.message}'\n );\n return next(\n errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD)\n );\n }\n\n const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);\n const token = await getApiToken(auth, config, restoredRemoteUser, password);\n debug('login: new token');\n if (!token) {\n return next(errorUtils.getUnauthorized());\n }\n\n res.status(HTTP_STATUS.CREATED);\n res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');\n\n const message = getAuthenticatedMessage(req.remote_user.name);\n debug('login: created user message %o', message);\n\n return next({\n ok: message,\n token,\n });\n }\n );\n } else {\n if (\n validatioUtils.validatePassword(\n password,\n config?.serverSettings?.passwordValidationRegex\n ) === false\n ) {\n debug('adduser: invalid password');\n // eslint-disable-next-line new-cap\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.PASSWORD_SHORT));\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 token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', mask(token as string, 4));\n }\n if (!token) {\n return next(errorUtils.getUnauthorized());\n }\n\n req.remote_user = user;\n res.status(HTTP_STATUS.CREATED);\n res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');\n debug('adduser: user has been created');\n return next({\n ok: `user '${req.body.name}' created`,\n token,\n });\n });\n }\n }\n );\n\n route.delete(\n '/-/user/token/*',\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n res.status(HTTP_STATUS.OK);\n next({\n ok: API_MESSAGE.LOGGED_OUT,\n });\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.js","names":["_debug","_interopRequireDefault","require","_auth","_config","_core","_logger","_middleware","_utils","obj","__esModule","default","debug","buildDebug","_default","route","auth","config","get","rateLimit","userRateLimit","req","res","next","remote_user","name","status","HTTP_STATUS","OK","ok","username","params","org_couchdb_user","split","message","getAuthenticatedMessage","email","put","_req$remote_user","password","body","remoteName","validatioUtils","validateUserName","errorUtils","getBadRequest","API_ERROR","USERNAME_MISMATCH","authenticate","callbackAuthenticate","err","user","logger","trace","getCode","UNAUTHORIZED","BAD_USERNAME_PASSWORD","restoredRemoteUser","createRemoteUser","groups","token","getApiToken","getUnauthorized","CREATED","set","HEADERS","CACHE_CONTROL","_config$serverSetting","validatePassword","serverSettings","passwordValidationRegex","BAD_REQUEST","PASSWORD_SHORT","add_user","INTERNAL_ERROR","getConflict","undefined","mask","delete","API_MESSAGE","LOGGED_OUT"],"sources":["../src/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Response, Router } from 'express';\n\nimport { getApiToken } from '@verdaccio/auth';\nimport { Auth } from '@verdaccio/auth';\nimport { createRemoteUser } from '@verdaccio/config';\nimport {\n API_ERROR,\n API_MESSAGE,\n HEADERS,\n HTTP_STATUS,\n errorUtils,\n validatioUtils,\n} from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport { rateLimit } from '@verdaccio/middleware';\nimport { Config, RemoteUser } from '@verdaccio/types';\nimport { getAuthenticatedMessage, mask } from '@verdaccio/utils';\n\nimport { $NextFunctionVer, $RequestExtend } from '../types/custom';\n\nconst debug = buildDebug('verdaccio:api:user');\n\nexport default function (route: Router, auth: Auth, config: Config): void {\n route.get(\n '/-/user/:org_couchdb_user',\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n debug('verifying user');\n\n if (typeof req.remote_user.name !== 'string' || req.remote_user.name === '') {\n debug('user not logged in');\n res.status(HTTP_STATUS.OK);\n return next({ ok: false });\n }\n\n const username = req.params.org_couchdb_user.split(':')[1];\n const message = getAuthenticatedMessage(req.remote_user.name);\n debug('user authenticated message %o', message);\n res.status(HTTP_STATUS.OK);\n next({\n // 'npm owner' requires user info\n // TODO: we don't have the email\n name: username,\n email: '',\n ok: message,\n });\n }\n );\n\n /**\n * \n * body example\n * req.body = {\n _id: \"org.couchdb.user:jjjj\",\n name: \"jjjj\",\n password: \"jjjj\",\n type: \"user\",\n roles: [],\n date: \"2022-07-08T15:51:04.002Z\",\n }\n * \n * @export\n * @param {Router} route\n * @param {Auth} auth\n * @param {Config} config\n */\n route.put(\n '/-/user/:org_couchdb_user/:_rev?/:revision?',\n rateLimit(config?.userRateLimit),\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n const { name, password } = req.body;\n debug('login or adduser');\n const remoteName = req?.remote_user?.name;\n\n if (!validatioUtils.validateUserName(req.params.org_couchdb_user, name)) {\n return next(errorUtils.getBadRequest(API_ERROR.USERNAME_MISMATCH));\n }\n\n if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {\n debug('login: no remote user detected');\n auth.authenticate(\n name,\n password,\n async function callbackAuthenticate(err, user): Promise<void> {\n if (err) {\n logger.trace(\n { name, err },\n 'authenticating for user @{username} failed. Error: @{err.message}'\n );\n return next(\n errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD)\n );\n }\n\n const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);\n const token = await getApiToken(auth, config, restoredRemoteUser, password);\n debug('login: new token');\n if (!token) {\n return next(errorUtils.getUnauthorized());\n }\n\n res.status(HTTP_STATUS.CREATED);\n res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');\n\n const message = getAuthenticatedMessage(req.remote_user.name);\n debug('login: created user message %o', message);\n\n return next({\n ok: message,\n token,\n });\n }\n );\n } else {\n debug('adduser: %o', name);\n if (\n validatioUtils.validatePassword(\n password,\n config?.serverSettings?.passwordValidationRegex\n ) === false\n ) {\n debug('adduser: invalid password');\n // eslint-disable-next-line new-cap\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.PASSWORD_SHORT));\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 token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', mask(token as string, 4));\n }\n if (!token) {\n return next(errorUtils.getUnauthorized());\n }\n\n req.remote_user = user;\n res.status(HTTP_STATUS.CREATED);\n res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');\n debug('adduser: user has been created');\n return next({\n ok: `user '${req.body.name}' created`,\n token,\n });\n });\n }\n }\n );\n\n route.delete(\n '/-/user/token/*',\n function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {\n res.status(HTTP_STATUS.OK);\n next({\n ok: API_MESSAGE.LOGGED_OUT,\n });\n }\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/api",
3
- "version": "7.0.0-next-7.16",
3
+ "version": "7.0.0-next-7.18",
4
4
  "description": "loaders logic",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -29,13 +29,13 @@
29
29
  },
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
- "@verdaccio/auth": "7.0.0-next-7.16",
33
- "@verdaccio/config": "7.0.0-next-7.16",
34
- "@verdaccio/core": "7.0.0-next-7.16",
35
- "@verdaccio/logger": "7.0.0-next-7.16",
36
- "@verdaccio/middleware": "7.0.0-next-7.16",
37
- "@verdaccio/store": "7.0.0-next-7.16",
38
- "@verdaccio/utils": "7.0.0-next-7.16",
32
+ "@verdaccio/auth": "7.0.0-next-7.18",
33
+ "@verdaccio/config": "7.0.0-next-7.18",
34
+ "@verdaccio/core": "7.0.0-next-7.18",
35
+ "@verdaccio/logger": "7.0.0-next-7.18",
36
+ "@verdaccio/middleware": "7.0.0-next-7.18",
37
+ "@verdaccio/store": "7.0.0-next-7.18",
38
+ "@verdaccio/utils": "7.0.0-next-7.18",
39
39
  "abortcontroller-polyfill": "1.7.5",
40
40
  "body-parser": "1.20.2",
41
41
  "cookies": "0.9.0",
@@ -43,11 +43,11 @@
43
43
  "express": "4.19.2",
44
44
  "lodash": "4.17.21",
45
45
  "mime": "2.6.0",
46
- "semver": "7.6.0"
46
+ "semver": "7.6.2"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@verdaccio/test-helper": "3.0.0-next-7.2",
50
- "@verdaccio/types": "12.0.0-next-7.3",
50
+ "@verdaccio/types": "12.0.0-next-7.5",
51
51
  "mockdate": "3.0.5",
52
52
  "nock": "13.5.1",
53
53
  "supertest": "6.3.4"
package/src/package.ts CHANGED
@@ -28,6 +28,7 @@ export default function (route: Router, auth: Auth, storage: Storage): void {
28
28
  const name = req.params.package;
29
29
  let version = req.params.version;
30
30
  const write = req.query.write === 'true';
31
+ const username = req?.remote_user?.name;
31
32
  const abbreviated =
32
33
  stringUtils.getByQualityPriorityValue(req.get('Accept')) === Storage.ABBREVIATED_HEADER;
33
34
  const requestOptions = {
@@ -37,6 +38,7 @@ export default function (route: Router, auth: Auth, storage: Storage): void {
37
38
  host: req.host,
38
39
  remoteAddress: req.socket.remoteAddress,
39
40
  byPassCache: write,
41
+ username,
40
42
  };
41
43
 
42
44
  try {
package/src/publish.ts CHANGED
@@ -76,11 +76,11 @@ const debug = buildDebug('verdaccio:api:publish');
76
76
  *
77
77
  * 3. Star a package
78
78
  *
79
- * Permissions: start a package depends of the publish and unpublish permissions, there is no
80
- * specific flag for star or un start.
79
+ * Permissions: staring a package depends of the publish and unpublish permissions, there is no
80
+ * specific flag for star or unstar.
81
81
  * The URL for star is similar to the unpublish (change package format)
82
82
  *
83
- * npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference
83
+ * npm has no endpoint for staring a package, rather mutate the metadata and acts as, the difference
84
84
  * is the users property which is part of the payload and the body only includes
85
85
  *
86
86
  * {
@@ -89,7 +89,24 @@ const debug = buildDebug('verdaccio:api:publish');
89
89
  "users": {
90
90
  [username]: boolean value (true, false)
91
91
  }
92
- }
92
+ }
93
+ *
94
+ * 4. Change owners of a package
95
+ *
96
+ * Similar to staring a package, changing owners (maintainers) of a package uses the publish
97
+ * endpoint.
98
+ *
99
+ * The body includes a list of the new owners with the following format
100
+ *
101
+ * {
102
+ "_id": pkgName,
103
+ "_rev": "4-b0cdaefc9bdb77c8",
104
+ "maintainers": [
105
+ { "name": "first owner", "email": "me@verdaccio.org" },
106
+ { "name": "second owner", "email": "you@verdaccio.org" },
107
+ ...
108
+ ]
109
+ }
93
110
  *
94
111
  */
95
112
  export default function publish(router: Router, auth: Auth, storage: Storage): void {
@@ -127,10 +144,11 @@ export default function publish(router: Router, auth: Auth, storage: Storage): v
127
144
  async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
128
145
  const packageName = req.params.package;
129
146
  const rev = req.params.revision;
147
+ const username = req?.remote_user?.name;
130
148
 
131
149
  logger.debug({ packageName }, `unpublishing @{packageName}`);
132
150
  try {
133
- await storage.removePackage(packageName, rev);
151
+ await storage.removePackage(packageName, rev, username);
134
152
  debug('package %s unpublished', packageName);
135
153
  res.status(HTTP_STATUS.CREATED);
136
154
  return next({ ok: API_MESSAGE.PKG_REMOVED });
@@ -155,13 +173,14 @@ export default function publish(router: Router, auth: Auth, storage: Storage): v
155
173
  ): Promise<void> {
156
174
  const packageName = req.params.package;
157
175
  const { filename, revision } = req.params;
176
+ const username = req?.remote_user?.name;
158
177
 
159
178
  logger.debug(
160
179
  { packageName, filename, revision },
161
180
  `removing a tarball for @{packageName}-@{tarballName}-@{revision}`
162
181
  );
163
182
  try {
164
- await storage.removeTarball(packageName, filename, revision);
183
+ await storage.removeTarball(packageName, filename, revision, username);
165
184
  res.status(HTTP_STATUS.CREATED);
166
185
 
167
186
  logger.debug(
@@ -188,6 +207,12 @@ export function publishPackage(storage: Storage): any {
188
207
  const metadata = req.body;
189
208
  const username = req?.remote_user?.name;
190
209
 
210
+ debug('publishing package %o for user %o', packageName, username);
211
+ logger.debug(
212
+ { packageName, username },
213
+ 'publishing package @{packageName} for user @{username}'
214
+ );
215
+
191
216
  try {
192
217
  const message = await storage.updateManifest(metadata, {
193
218
  name: packageName,
package/src/user.ts CHANGED
@@ -27,10 +27,22 @@ export default function (route: Router, auth: Auth, config: Config): void {
27
27
  rateLimit(config?.userRateLimit),
28
28
  function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
29
29
  debug('verifying user');
30
+
31
+ if (typeof req.remote_user.name !== 'string' || req.remote_user.name === '') {
32
+ debug('user not logged in');
33
+ res.status(HTTP_STATUS.OK);
34
+ return next({ ok: false });
35
+ }
36
+
37
+ const username = req.params.org_couchdb_user.split(':')[1];
30
38
  const message = getAuthenticatedMessage(req.remote_user.name);
31
39
  debug('user authenticated message %o', message);
32
40
  res.status(HTTP_STATUS.OK);
33
41
  next({
42
+ // 'npm owner' requires user info
43
+ // TODO: we don't have the email
44
+ name: username,
45
+ email: '',
34
46
  ok: message,
35
47
  });
36
48
  }
@@ -61,6 +73,10 @@ export default function (route: Router, auth: Auth, config: Config): void {
61
73
  debug('login or adduser');
62
74
  const remoteName = req?.remote_user?.name;
63
75
 
76
+ if (!validatioUtils.validateUserName(req.params.org_couchdb_user, name)) {
77
+ return next(errorUtils.getBadRequest(API_ERROR.USERNAME_MISMATCH));
78
+ }
79
+
64
80
  if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {
65
81
  debug('login: no remote user detected');
66
82
  auth.authenticate(
@@ -97,6 +113,7 @@ export default function (route: Router, auth: Auth, config: Config): void {
97
113
  }
98
114
  );
99
115
  } else {
116
+ debug('adduser: %o', name);
100
117
  if (
101
118
  validatioUtils.validatePassword(
102
119
  password,
@@ -11,7 +11,7 @@ import {
11
11
  generatePackageMetadata,
12
12
  initializeServer as initializeServerHelper,
13
13
  } from '@verdaccio/test-helper';
14
- import { GenericBody, PackageUsers } from '@verdaccio/types';
14
+ import { Author, GenericBody, PackageUsers } from '@verdaccio/types';
15
15
  import { buildToken, generateRandomHexString } from '@verdaccio/utils';
16
16
 
17
17
  import apiMiddleware from '../../src';
@@ -142,6 +142,37 @@ export function starPackage(
142
142
  return test;
143
143
  }
144
144
 
145
+ export function changeOwners(
146
+ app,
147
+ options: {
148
+ maintainers: Author[];
149
+ name: string;
150
+ _rev: string;
151
+ _id?: string;
152
+ },
153
+ token?: string
154
+ ): supertest.Test {
155
+ const { _rev, _id, maintainers } = options;
156
+ const ownerManifest = {
157
+ _rev,
158
+ _id,
159
+ maintainers,
160
+ };
161
+
162
+ const test = supertest(app)
163
+ .put(`/${encodeURIComponent(options.name)}`)
164
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
165
+ .send(JSON.stringify(ownerManifest))
166
+ .set('accept', HEADERS.GZIP)
167
+ .set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
168
+
169
+ if (typeof token === 'string') {
170
+ test.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token));
171
+ }
172
+
173
+ return test;
174
+ }
175
+
145
176
  export function getDisTags(app, pkgName) {
146
177
  return supertest(app)
147
178
  .get(`/-/package/${encodeURIComponent(pkgName)}/dist-tags`)
@@ -0,0 +1,24 @@
1
+ storage: ./storage
2
+
3
+ auth:
4
+ htpasswd:
5
+ file: ./htpasswd-owner
6
+
7
+ web:
8
+ enable: true
9
+ title: verdaccio
10
+
11
+ log: { type: stdout, format: pretty, level: info }
12
+
13
+ # TODO: Add test case for $owner access
14
+ packages:
15
+ '@*/*':
16
+ access: $all
17
+ publish: $authenticated
18
+ unpublish: $authenticated
19
+ '**':
20
+ access: $all
21
+ publish: $authenticated
22
+ unpublish: $authenticated
23
+
24
+ _debug: true
@@ -0,0 +1,118 @@
1
+ /* eslint-disable jest/no-commented-out-tests */
2
+ import nock from 'nock';
3
+
4
+ import { HTTP_STATUS } from '@verdaccio/core';
5
+
6
+ import {
7
+ changeOwners,
8
+ createUser,
9
+ getPackage,
10
+ initializeServer,
11
+ publishVersionWithToken,
12
+ } from './_helper';
13
+
14
+ describe('owner', () => {
15
+ test.each([['foo', '@scope%2Ffoo']])('should get owner of package', async (pkgName) => {
16
+ nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
17
+ const app = await initializeServer('owner.yaml');
18
+ const credentials = { name: 'test', password: 'test' };
19
+ const response = await createUser(app, credentials.name, credentials.password);
20
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
21
+ await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
22
+ HTTP_STATUS.CREATED
23
+ );
24
+
25
+ // expect publish to set owner to logged in user
26
+ const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
27
+ const maintainers = manifest.body.maintainers;
28
+ expect(maintainers).toHaveLength(1);
29
+ // TODO: This should eventually include the email of the user
30
+ expect(maintainers).toEqual([{ name: credentials.name, email: '' }]);
31
+ });
32
+
33
+ test.each([['foo', '@scope%2Ffoo']])('should add/remove owner to package', async (pkgName) => {
34
+ nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
35
+ const app = await initializeServer('owner.yaml');
36
+ const credentials = { name: 'test', password: 'test' };
37
+ const firstOwner = { name: 'test', email: '' };
38
+ const response = await createUser(app, credentials.name, credentials.password);
39
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
40
+ await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
41
+ HTTP_STATUS.CREATED
42
+ );
43
+
44
+ // publish sets owner to logged in user
45
+ const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
46
+ const maintainers = manifest.body.maintainers;
47
+ expect(maintainers).toHaveLength(1);
48
+ expect(maintainers).toEqual([firstOwner]);
49
+
50
+ // add another owner
51
+ const secondOwner = { name: 'tester', email: 'test@verdaccio.org' };
52
+ const newOwners = [...maintainers, secondOwner];
53
+ await changeOwners(
54
+ app,
55
+ {
56
+ _rev: manifest.body._rev,
57
+ _id: manifest.body._id,
58
+ name: pkgName,
59
+ maintainers: newOwners,
60
+ },
61
+ response.body.token
62
+ ).expect(HTTP_STATUS.CREATED);
63
+
64
+ const manifest2 = await getPackage(app, '', decodeURIComponent(pkgName));
65
+ const maintainers2 = manifest2.body.maintainers;
66
+ expect(maintainers2).toHaveLength(2);
67
+ expect(maintainers2).toEqual([firstOwner, secondOwner]);
68
+
69
+ // remove original owner
70
+ await changeOwners(
71
+ app,
72
+ {
73
+ _rev: manifest2.body._rev,
74
+ _id: manifest2.body._id,
75
+ name: pkgName,
76
+ maintainers: [secondOwner],
77
+ },
78
+ response.body.token
79
+ ).expect(HTTP_STATUS.CREATED);
80
+
81
+ const manifest3 = await getPackage(app, '', decodeURIComponent(pkgName));
82
+ const maintainers3 = manifest3.body.maintainers;
83
+ expect(maintainers3).toHaveLength(1);
84
+ expect(maintainers3).toEqual([secondOwner]);
85
+ });
86
+
87
+ test.each([['foo', '@scope%2Ffoo']])('should fail if user is not logged in', async (pkgName) => {
88
+ nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
89
+ const app = await initializeServer('owner.yaml');
90
+ const credentials = { name: 'test', password: 'test' };
91
+ const firstOwner = { name: 'test', email: '' };
92
+ const response = await createUser(app, credentials.name, credentials.password);
93
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
94
+ await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
95
+ HTTP_STATUS.CREATED
96
+ );
97
+
98
+ // publish sets owner to logged in user
99
+ const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
100
+ const maintainers = manifest.body.maintainers;
101
+ expect(maintainers).toHaveLength(1);
102
+ expect(maintainers).toEqual([firstOwner]);
103
+
104
+ // try adding another owner
105
+ const secondOwner = { name: 'tester', email: 'test@verdaccio.org' };
106
+ const newOwners = [...maintainers, secondOwner];
107
+ await changeOwners(
108
+ app,
109
+ {
110
+ _rev: manifest.body._rev,
111
+ _id: manifest.body._id,
112
+ name: pkgName,
113
+ maintainers: newOwners,
114
+ },
115
+ '' // no token
116
+ ).expect(HTTP_STATUS.UNAUTHORIZED);
117
+ });
118
+ });
@@ -43,6 +43,12 @@ describe('search', () => {
43
43
  links: {
44
44
  npm: '',
45
45
  },
46
+ maintainers: [
47
+ {
48
+ email: '',
49
+ name: 'test',
50
+ },
51
+ ],
46
52
  name: pkg,
47
53
  publisher: {},
48
54
  scope: '',
@@ -97,6 +103,12 @@ describe('search', () => {
97
103
  links: {
98
104
  npm: '',
99
105
  },
106
+ maintainers: [
107
+ {
108
+ email: '',
109
+ name: 'test',
110
+ },
111
+ ],
100
112
  name: pkg,
101
113
  publisher: {},
102
114
  scope: '@scope',
@@ -148,6 +148,25 @@ describe('token', () => {
148
148
  .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
149
149
  .expect(HTTP_STATUS.OK);
150
150
  expect(response2.body.ok).toBe(`you are authenticated as '${credentials.name}'`);
151
+ expect(response2.body.name).toBe(credentials.name);
152
+ }
153
+ );
154
+
155
+ test.each([['user.yaml'], ['user.jwt.yaml']])(
156
+ 'should return name of requested user',
157
+ async (conf) => {
158
+ const app = await initializeServer(conf);
159
+ const username = 'yeti';
160
+ const credentials = { name: 'jota', password: 'secretPass' };
161
+ const response = await createUser(app, credentials.name, credentials.password);
162
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
163
+ const response3 = await supertest(app)
164
+ .get(`/-/user/org.couchdb.user:${username}`)
165
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
166
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
167
+ .expect(HTTP_STATUS.OK);
168
+ expect(response3.body.ok).toBe(`you are authenticated as '${credentials.name}'`);
169
+ expect(response3.body.name).toBe(username);
151
170
  }
152
171
  );
153
172
 
@@ -165,5 +184,38 @@ describe('token', () => {
165
184
  .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
166
185
  .expect(HTTP_STATUS.OK);
167
186
  });
187
+
188
+ test.each([['user.yaml'], ['user.jwt.yaml']])(
189
+ 'should return "false" if user is not logged in',
190
+ async (conf) => {
191
+ const app = await initializeServer(conf);
192
+ const credentials = { name: 'jota', password: '' };
193
+ const response = await supertest(app)
194
+ .get(`/-/user/org.couchdb.user:${credentials.name}`)
195
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
196
+ .expect(HTTP_STATUS.OK);
197
+ expect(response.body.ok).toBe(false);
198
+ }
199
+ );
200
+
201
+ test.each([['user.yaml'], ['user.jwt.yaml']])(
202
+ 'should fail if URL does not match user in request body',
203
+ async (conf) => {
204
+ const app = await initializeServer(conf);
205
+ const credentials = { name: 'jota', password: 'secretPass' };
206
+ const response = await createUser(app, credentials.name, credentials.password);
207
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
208
+ const response2 = await supertest(app)
209
+ .put('/-/user/org.couchdb.user:yeti') // different user
210
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
211
+ .send({
212
+ name: credentials.name,
213
+ password: credentials.password,
214
+ })
215
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
216
+ .expect(HTTP_STATUS.BAD_REQUEST);
217
+ expect(response2.body.error).toBe(API_ERROR.USERNAME_MISMATCH);
218
+ }
219
+ );
168
220
  });
169
221
  });