@verdaccio/api 7.0.0-next-7.15 → 7.0.0-next-7.17
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 +29 -0
- package/build/package.js +4 -1
- package/build/package.js.map +1 -1
- package/build/publish.d.ts +21 -4
- package/build/publish.js +34 -8
- package/build/publish.js.map +1 -1
- package/build/user.js +16 -0
- package/build/user.js.map +1 -1
- package/package.json +11 -11
- package/src/package.ts +2 -0
- package/src/publish.ts +31 -6
- package/src/user.ts +17 -0
- package/test/integration/_helper.ts +32 -1
- package/test/integration/config/owner.yaml +24 -0
- package/test/integration/owner.spec.ts +118 -0
- package/test/integration/search.spec.ts +12 -0
- package/test/integration/user.spec.ts +52 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @verdaccio/api
|
|
2
2
|
|
|
3
|
+
## 7.0.0-next-7.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6e764e3: feat: add support for npm owner
|
|
8
|
+
- Updated dependencies [6e764e3]
|
|
9
|
+
- Updated dependencies [de6ff5c]
|
|
10
|
+
- @verdaccio/config@7.0.0-next-7.17
|
|
11
|
+
- @verdaccio/core@7.0.0-next-7.17
|
|
12
|
+
- @verdaccio/store@7.0.0-next-7.17
|
|
13
|
+
- @verdaccio/auth@7.0.0-next-7.17
|
|
14
|
+
- @verdaccio/logger@7.0.0-next-7.17
|
|
15
|
+
- @verdaccio/middleware@7.0.0-next-7.17
|
|
16
|
+
- @verdaccio/utils@7.0.0-next-7.17
|
|
17
|
+
|
|
18
|
+
## 7.0.0-next-7.16
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [e5624e1]
|
|
23
|
+
- Updated dependencies [5bfab62]
|
|
24
|
+
- @verdaccio/store@7.0.0-next-7.16
|
|
25
|
+
- @verdaccio/logger@7.0.0-next-7.16
|
|
26
|
+
- @verdaccio/middleware@7.0.0-next-7.16
|
|
27
|
+
- @verdaccio/auth@7.0.0-next-7.16
|
|
28
|
+
- @verdaccio/core@7.0.0-next-7.16
|
|
29
|
+
- @verdaccio/config@7.0.0-next-7.16
|
|
30
|
+
- @verdaccio/utils@7.0.0-next-7.16
|
|
31
|
+
|
|
3
32
|
## 7.0.0-next-7.15
|
|
4
33
|
|
|
5
34
|
### 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({
|
package/build/package.js.map
CHANGED
|
@@ -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;
|
|
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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"}
|
package/build/publish.d.ts
CHANGED
|
@@ -63,11 +63,11 @@ import { Storage } from '@verdaccio/store';
|
|
|
63
63
|
*
|
|
64
64
|
* 3. Star a package
|
|
65
65
|
*
|
|
66
|
-
* Permissions:
|
|
67
|
-
* specific flag for star or
|
|
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
|
|
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:
|
|
81
|
-
* specific flag for star or
|
|
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
|
|
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$
|
|
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$
|
|
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,
|
package/build/publish.js.map
CHANGED
|
@@ -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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"}
|
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.
|
|
3
|
+
"version": "7.0.0-next-7.17",
|
|
4
4
|
"description": "loaders logic",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -29,25 +29,25 @@
|
|
|
29
29
|
},
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@verdaccio/auth": "7.0.0-next-7.
|
|
33
|
-
"@verdaccio/config": "7.0.0-next-7.
|
|
34
|
-
"@verdaccio/core": "7.0.0-next-7.
|
|
35
|
-
"@verdaccio/logger": "7.0.0-next-7.
|
|
36
|
-
"@verdaccio/middleware": "7.0.0-next-7.
|
|
37
|
-
"@verdaccio/store": "7.0.0-next-7.
|
|
38
|
-
"@verdaccio/utils": "7.0.0-next-7.
|
|
32
|
+
"@verdaccio/auth": "7.0.0-next-7.17",
|
|
33
|
+
"@verdaccio/config": "7.0.0-next-7.17",
|
|
34
|
+
"@verdaccio/core": "7.0.0-next-7.17",
|
|
35
|
+
"@verdaccio/logger": "7.0.0-next-7.17",
|
|
36
|
+
"@verdaccio/middleware": "7.0.0-next-7.17",
|
|
37
|
+
"@verdaccio/store": "7.0.0-next-7.17",
|
|
38
|
+
"@verdaccio/utils": "7.0.0-next-7.17",
|
|
39
39
|
"abortcontroller-polyfill": "1.7.5",
|
|
40
40
|
"body-parser": "1.20.2",
|
|
41
41
|
"cookies": "0.9.0",
|
|
42
42
|
"debug": "4.3.4",
|
|
43
|
-
"express": "4.
|
|
43
|
+
"express": "4.19.2",
|
|
44
44
|
"lodash": "4.17.21",
|
|
45
45
|
"mime": "2.6.0",
|
|
46
|
-
"semver": "7.6.
|
|
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.
|
|
50
|
+
"@verdaccio/types": "12.0.0-next-7.4",
|
|
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:
|
|
80
|
-
* specific flag for star or
|
|
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
|
|
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
|
});
|