@verdaccio/api 7.0.0-next.5 → 7.0.1-next-8.0
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 +282 -0
- package/LICENSE +1 -1
- package/build/dist-tags.js +1 -1
- package/build/dist-tags.js.map +1 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/package.js +6 -3
- package/build/package.js.map +1 -1
- package/build/ping.js.map +1 -1
- package/build/publish.d.ts +21 -4
- package/build/publish.js +35 -9
- package/build/publish.js.map +1 -1
- package/build/search.js.map +1 -1
- package/build/stars.js +1 -1
- package/build/stars.js.map +1 -1
- package/build/user.js +19 -3
- package/build/user.js.map +1 -1
- package/build/v1/profile.js +6 -3
- package/build/v1/profile.js.map +1 -1
- package/build/v1/search.js +4 -3
- package/build/v1/search.js.map +1 -1
- package/build/v1/token.js +1 -1
- package/build/v1/token.js.map +1 -1
- package/build/whoami.js +2 -2
- package/build/whoami.js.map +1 -1
- package/package.json +17 -17
- package/src/index.ts +3 -2
- package/src/package.ts +3 -1
- package/src/publish.ts +31 -6
- package/src/user.ts +17 -0
- package/src/v1/profile.ts +5 -3
- package/src/v1/search.ts +2 -1
- package/test/integration/_helper.ts +33 -1
- package/test/integration/config/owner.yaml +24 -0
- package/test/integration/config/profile.yaml +27 -0
- package/test/integration/config/user.jwt.yaml +1 -1
- package/test/integration/config/user.yaml +1 -1
- package/test/integration/distTag.spec.ts +1 -0
- package/test/integration/owner.spec.ts +119 -0
- package/test/integration/package.spec.ts +3 -2
- package/test/integration/ping.spec.ts +1 -0
- package/test/integration/profile.spec.ts +112 -0
- package/test/integration/publish.spec.ts +1 -0
- package/test/integration/search.spec.ts +13 -0
- package/test/integration/star.spec.ts +1 -0
- package/test/integration/token.spec.ts +1 -0
- package/test/integration/user.spec.ts +54 -1
- package/test/integration/whoami.spec.ts +1 -0
- package/test/unit/validate.api.params.middleware.spec.ts +1 -0
- package/jest.config.js +0 -10
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,
|
package/src/v1/profile.ts
CHANGED
|
@@ -81,15 +81,17 @@ export default function (route: Router, auth: Auth, config: Config): void {
|
|
|
81
81
|
/* eslint new-cap:off */
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
if (_.isEmpty(password.old)) {
|
|
85
|
+
return next(errorUtils.getBadRequest('old password is required'));
|
|
86
|
+
}
|
|
87
|
+
|
|
84
88
|
auth.changePassword(
|
|
85
89
|
name,
|
|
86
90
|
password.old,
|
|
87
91
|
password.new,
|
|
88
92
|
(err, isUpdated): $NextFunctionVer => {
|
|
89
93
|
if (_.isNull(err) === false) {
|
|
90
|
-
return next(
|
|
91
|
-
errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)
|
|
92
|
-
);
|
|
94
|
+
return next(errorUtils.getForbidden(err.message));
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
if (isUpdated) {
|
package/src/v1/search.ts
CHANGED
|
@@ -50,12 +50,13 @@ export default function (route, auth: Auth, storage: Storage): void {
|
|
|
50
50
|
from = parseInt(from, 10) || 0;
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
+
debug('storage search initiated');
|
|
53
54
|
data = await storage.search({
|
|
54
55
|
query,
|
|
55
56
|
url,
|
|
56
57
|
abort,
|
|
57
58
|
});
|
|
58
|
-
debug('
|
|
59
|
+
debug('storage items tota: %o', data.length);
|
|
59
60
|
const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(
|
|
60
61
|
data.map((pkgItem) => {
|
|
61
62
|
return checkAccess(pkgItem, auth, req.remote_user);
|
|
@@ -2,6 +2,7 @@ import { Application } from 'express';
|
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import supertest from 'supertest';
|
|
5
|
+
import { expect } from 'vitest';
|
|
5
6
|
|
|
6
7
|
import { parseConfigFile } from '@verdaccio/config';
|
|
7
8
|
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
generatePackageMetadata,
|
|
12
13
|
initializeServer as initializeServerHelper,
|
|
13
14
|
} from '@verdaccio/test-helper';
|
|
14
|
-
import { GenericBody, PackageUsers } from '@verdaccio/types';
|
|
15
|
+
import { Author, GenericBody, PackageUsers } from '@verdaccio/types';
|
|
15
16
|
import { buildToken, generateRandomHexString } from '@verdaccio/utils';
|
|
16
17
|
|
|
17
18
|
import apiMiddleware from '../../src';
|
|
@@ -142,6 +143,37 @@ export function starPackage(
|
|
|
142
143
|
return test;
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
export function changeOwners(
|
|
147
|
+
app,
|
|
148
|
+
options: {
|
|
149
|
+
maintainers: Author[];
|
|
150
|
+
name: string;
|
|
151
|
+
_rev: string;
|
|
152
|
+
_id?: string;
|
|
153
|
+
},
|
|
154
|
+
token?: string
|
|
155
|
+
): supertest.Test {
|
|
156
|
+
const { _rev, _id, maintainers } = options;
|
|
157
|
+
const ownerManifest = {
|
|
158
|
+
_rev,
|
|
159
|
+
_id,
|
|
160
|
+
maintainers,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const test = supertest(app)
|
|
164
|
+
.put(`/${encodeURIComponent(options.name)}`)
|
|
165
|
+
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
|
166
|
+
.send(JSON.stringify(ownerManifest))
|
|
167
|
+
.set('accept', HEADERS.GZIP)
|
|
168
|
+
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
|
|
169
|
+
|
|
170
|
+
if (typeof token === 'string') {
|
|
171
|
+
test.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return test;
|
|
175
|
+
}
|
|
176
|
+
|
|
145
177
|
export function getDisTags(app, pkgName) {
|
|
146
178
|
return supertest(app)
|
|
147
179
|
.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,27 @@
|
|
|
1
|
+
auth:
|
|
2
|
+
htpasswd:
|
|
3
|
+
file: ./htpasswd-profile
|
|
4
|
+
web:
|
|
5
|
+
enable: true
|
|
6
|
+
title: verdaccio
|
|
7
|
+
|
|
8
|
+
uplinks:
|
|
9
|
+
|
|
10
|
+
log: { type: stdout, format: pretty, level: trace }
|
|
11
|
+
|
|
12
|
+
packages:
|
|
13
|
+
'@*/*':
|
|
14
|
+
access: $all
|
|
15
|
+
publish: $all
|
|
16
|
+
unpublish: $all
|
|
17
|
+
proxy: npmjs
|
|
18
|
+
'verdaccio':
|
|
19
|
+
access: $all
|
|
20
|
+
publish: $all
|
|
21
|
+
'**':
|
|
22
|
+
access: $all
|
|
23
|
+
publish: $all
|
|
24
|
+
unpublish: $all
|
|
25
|
+
proxy: npmjs
|
|
26
|
+
|
|
27
|
+
_debug: true
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* eslint-disable jest/no-commented-out-tests */
|
|
2
|
+
import nock from 'nock';
|
|
3
|
+
import { describe, expect, test } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { HTTP_STATUS } from '@verdaccio/core';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
changeOwners,
|
|
9
|
+
createUser,
|
|
10
|
+
getPackage,
|
|
11
|
+
initializeServer,
|
|
12
|
+
publishVersionWithToken,
|
|
13
|
+
} from './_helper';
|
|
14
|
+
|
|
15
|
+
describe('owner', () => {
|
|
16
|
+
test.each([['foo', '@scope%2Ffoo']])('should get owner of package', async (pkgName) => {
|
|
17
|
+
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
|
|
18
|
+
const app = await initializeServer('owner.yaml');
|
|
19
|
+
const credentials = { name: 'test', password: 'test' };
|
|
20
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
21
|
+
expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
|
|
22
|
+
await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
|
|
23
|
+
HTTP_STATUS.CREATED
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// expect publish to set owner to logged in user
|
|
27
|
+
const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
|
|
28
|
+
const maintainers = manifest.body.maintainers;
|
|
29
|
+
expect(maintainers).toHaveLength(1);
|
|
30
|
+
// TODO: This should eventually include the email of the user
|
|
31
|
+
expect(maintainers).toEqual([{ name: credentials.name, email: '' }]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test.each([['foo', '@scope%2Ffoo']])('should add/remove owner to package', async (pkgName) => {
|
|
35
|
+
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
|
|
36
|
+
const app = await initializeServer('owner.yaml');
|
|
37
|
+
const credentials = { name: 'test', password: 'test' };
|
|
38
|
+
const firstOwner = { name: 'test', email: '' };
|
|
39
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
40
|
+
expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
|
|
41
|
+
await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
|
|
42
|
+
HTTP_STATUS.CREATED
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// publish sets owner to logged in user
|
|
46
|
+
const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
|
|
47
|
+
const maintainers = manifest.body.maintainers;
|
|
48
|
+
expect(maintainers).toHaveLength(1);
|
|
49
|
+
expect(maintainers).toEqual([firstOwner]);
|
|
50
|
+
|
|
51
|
+
// add another owner
|
|
52
|
+
const secondOwner = { name: 'tester', email: 'test@verdaccio.org' };
|
|
53
|
+
const newOwners = [...maintainers, secondOwner];
|
|
54
|
+
await changeOwners(
|
|
55
|
+
app,
|
|
56
|
+
{
|
|
57
|
+
_rev: manifest.body._rev,
|
|
58
|
+
_id: manifest.body._id,
|
|
59
|
+
name: pkgName,
|
|
60
|
+
maintainers: newOwners,
|
|
61
|
+
},
|
|
62
|
+
response.body.token
|
|
63
|
+
).expect(HTTP_STATUS.CREATED);
|
|
64
|
+
|
|
65
|
+
const manifest2 = await getPackage(app, '', decodeURIComponent(pkgName));
|
|
66
|
+
const maintainers2 = manifest2.body.maintainers;
|
|
67
|
+
expect(maintainers2).toHaveLength(2);
|
|
68
|
+
expect(maintainers2).toEqual([firstOwner, secondOwner]);
|
|
69
|
+
|
|
70
|
+
// remove original owner
|
|
71
|
+
await changeOwners(
|
|
72
|
+
app,
|
|
73
|
+
{
|
|
74
|
+
_rev: manifest2.body._rev,
|
|
75
|
+
_id: manifest2.body._id,
|
|
76
|
+
name: pkgName,
|
|
77
|
+
maintainers: [secondOwner],
|
|
78
|
+
},
|
|
79
|
+
response.body.token
|
|
80
|
+
).expect(HTTP_STATUS.CREATED);
|
|
81
|
+
|
|
82
|
+
const manifest3 = await getPackage(app, '', decodeURIComponent(pkgName));
|
|
83
|
+
const maintainers3 = manifest3.body.maintainers;
|
|
84
|
+
expect(maintainers3).toHaveLength(1);
|
|
85
|
+
expect(maintainers3).toEqual([secondOwner]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test.each([['foo', '@scope%2Ffoo']])('should fail if user is not logged in', async (pkgName) => {
|
|
89
|
+
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
|
|
90
|
+
const app = await initializeServer('owner.yaml');
|
|
91
|
+
const credentials = { name: 'test', password: 'test' };
|
|
92
|
+
const firstOwner = { name: 'test', email: '' };
|
|
93
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
94
|
+
expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
|
|
95
|
+
await publishVersionWithToken(app, pkgName, '1.0.0', response.body.token).expect(
|
|
96
|
+
HTTP_STATUS.CREATED
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// publish sets owner to logged in user
|
|
100
|
+
const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
|
|
101
|
+
const maintainers = manifest.body.maintainers;
|
|
102
|
+
expect(maintainers).toHaveLength(1);
|
|
103
|
+
expect(maintainers).toEqual([firstOwner]);
|
|
104
|
+
|
|
105
|
+
// try adding another owner
|
|
106
|
+
const secondOwner = { name: 'tester', email: 'test@verdaccio.org' };
|
|
107
|
+
const newOwners = [...maintainers, secondOwner];
|
|
108
|
+
await changeOwners(
|
|
109
|
+
app,
|
|
110
|
+
{
|
|
111
|
+
_rev: manifest.body._rev,
|
|
112
|
+
_id: manifest.body._id,
|
|
113
|
+
name: pkgName,
|
|
114
|
+
maintainers: newOwners,
|
|
115
|
+
},
|
|
116
|
+
'' // no token
|
|
117
|
+
).expect(HTTP_STATUS.UNAUTHORIZED);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import supertest from 'supertest';
|
|
2
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
2
3
|
|
|
3
4
|
import { DIST_TAGS, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
|
|
4
5
|
import { Storage } from '@verdaccio/store';
|
|
@@ -26,8 +27,8 @@ describe('package', () => {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
test.each([
|
|
29
|
-
['
|
|
30
|
-
['@scope/
|
|
30
|
+
['foo2', 'foo2-1.0.0.tgz'],
|
|
31
|
+
['@scope/foo2', 'foo2-1.0.0.tgz'],
|
|
31
32
|
])('should fails if tarball does not exist', async (pkg, fileName) => {
|
|
32
33
|
await publishVersion(app, pkg, '1.0.1');
|
|
33
34
|
return await supertest(app)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import supertest from 'supertest';
|
|
2
|
+
import { describe, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
|
5
|
+
import { buildToken } from '@verdaccio/utils';
|
|
6
|
+
|
|
7
|
+
import { createUser, initializeServer } from './_helper';
|
|
8
|
+
|
|
9
|
+
describe('profile ', () => {
|
|
10
|
+
describe('get profile ', () => {
|
|
11
|
+
test('should return Unauthorized if header token is missing', async () => {
|
|
12
|
+
const app = await initializeServer('profile.yaml');
|
|
13
|
+
return supertest(app)
|
|
14
|
+
.get('/-/npm/v1/user')
|
|
15
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
16
|
+
.expect(HTTP_STATUS.UNAUTHORIZED);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should return user details', async () => {
|
|
20
|
+
const app = await initializeServer('profile.yaml');
|
|
21
|
+
const credentials = { name: 'test', password: 'test' };
|
|
22
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
23
|
+
return supertest(app)
|
|
24
|
+
.get('/-/npm/v1/user')
|
|
25
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
26
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
27
|
+
.expect(HTTP_STATUS.OK);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('post profile ', () => {
|
|
31
|
+
test('should return Unauthorized if header token is missing', async () => {
|
|
32
|
+
const app = await initializeServer('profile.yaml');
|
|
33
|
+
return supertest(app)
|
|
34
|
+
.post('/-/npm/v1/user')
|
|
35
|
+
.send({})
|
|
36
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
37
|
+
.expect(HTTP_STATUS.UNAUTHORIZED);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should return handle to short new password', async () => {
|
|
41
|
+
const app = await initializeServer('profile.yaml');
|
|
42
|
+
const credentials = { name: 'test', password: 'test' };
|
|
43
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
44
|
+
return supertest(app)
|
|
45
|
+
.post('/-/npm/v1/user')
|
|
46
|
+
.send({ password: { new: '_' } })
|
|
47
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
48
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
49
|
+
.expect(HTTP_STATUS.UNAUTHORIZED);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should return handle to missing old password', async () => {
|
|
53
|
+
const app = await initializeServer('profile.yaml');
|
|
54
|
+
const credentials = { name: 'test', password: 'test' };
|
|
55
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
56
|
+
return supertest(app)
|
|
57
|
+
.post('/-/npm/v1/user')
|
|
58
|
+
.send({ password: { new: 'fooooo', old: undefined } })
|
|
59
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
60
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
61
|
+
.expect(HTTP_STATUS.BAD_REQUEST);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should return handle to missing password', async () => {
|
|
65
|
+
const app = await initializeServer('profile.yaml');
|
|
66
|
+
const credentials = { name: 'test', password: 'test' };
|
|
67
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
68
|
+
return supertest(app)
|
|
69
|
+
.post('/-/npm/v1/user')
|
|
70
|
+
.send({ another: '_' })
|
|
71
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
72
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
73
|
+
.expect(HTTP_STATUS.INTERNAL_ERROR);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should return handle change password', async () => {
|
|
77
|
+
const app = await initializeServer('profile.yaml');
|
|
78
|
+
const credentials = { name: 'test', password: 'test' };
|
|
79
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
80
|
+
return supertest(app)
|
|
81
|
+
.post('/-/npm/v1/user')
|
|
82
|
+
.send({ password: { new: 'good password_.%#@$@#$@#', old: 'test' } })
|
|
83
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
84
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
85
|
+
.expect(HTTP_STATUS.OK);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should return handle change password failure', async () => {
|
|
89
|
+
const app = await initializeServer('profile.yaml');
|
|
90
|
+
const credentials = { name: 'test', password: 'test' };
|
|
91
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
92
|
+
return supertest(app)
|
|
93
|
+
.post('/-/npm/v1/user')
|
|
94
|
+
.send({ password: { new: 'good password_.%#@$@#$@#', old: 'test_do_not_match' } })
|
|
95
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
96
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
97
|
+
.expect(HTTP_STATUS.FORBIDDEN);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should handle tfa ( two factor auth) disabled', async () => {
|
|
101
|
+
const app = await initializeServer('profile.yaml');
|
|
102
|
+
const credentials = { name: 'test', password: 'test' };
|
|
103
|
+
const response = await createUser(app, credentials.name, credentials.password);
|
|
104
|
+
return supertest(app)
|
|
105
|
+
.post('/-/npm/v1/user')
|
|
106
|
+
.send({ tfa: '_' })
|
|
107
|
+
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
|
|
108
|
+
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
|
109
|
+
.expect(HTTP_STATUS.SERVICE_UNAVAILABLE);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import nock from 'nock';
|
|
2
2
|
import { basename } from 'path';
|
|
3
3
|
import supertest from 'supertest';
|
|
4
|
+
import { describe, expect, test } from 'vitest';
|
|
4
5
|
|
|
5
6
|
import { HTTP_STATUS } from '@verdaccio/core';
|
|
6
7
|
import { API_ERROR, API_MESSAGE, HEADERS, HEADER_TYPE } from '@verdaccio/core';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import MockDate from 'mockdate';
|
|
2
2
|
import supertest from 'supertest';
|
|
3
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
3
4
|
|
|
4
5
|
import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
|
|
5
6
|
|
|
@@ -43,6 +44,12 @@ describe('search', () => {
|
|
|
43
44
|
links: {
|
|
44
45
|
npm: '',
|
|
45
46
|
},
|
|
47
|
+
maintainers: [
|
|
48
|
+
{
|
|
49
|
+
email: '',
|
|
50
|
+
name: 'test',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
46
53
|
name: pkg,
|
|
47
54
|
publisher: {},
|
|
48
55
|
scope: '',
|
|
@@ -97,6 +104,12 @@ describe('search', () => {
|
|
|
97
104
|
links: {
|
|
98
105
|
npm: '',
|
|
99
106
|
},
|
|
107
|
+
maintainers: [
|
|
108
|
+
{
|
|
109
|
+
email: '',
|
|
110
|
+
name: 'test',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
100
113
|
name: pkg,
|
|
101
114
|
publisher: {},
|
|
102
115
|
scope: '@scope',
|