@verdaccio/api 7.0.0-next.6 → 7.0.1-next-8.1

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/build/dist-tags.js +1 -1
  3. package/build/dist-tags.js.map +1 -1
  4. package/build/index.js +3 -3
  5. package/build/index.js.map +1 -1
  6. package/build/package.js +6 -3
  7. package/build/package.js.map +1 -1
  8. package/build/ping.js.map +1 -1
  9. package/build/publish.d.ts +21 -4
  10. package/build/publish.js +35 -9
  11. package/build/publish.js.map +1 -1
  12. package/build/search.js.map +1 -1
  13. package/build/stars.js +1 -1
  14. package/build/stars.js.map +1 -1
  15. package/build/user.js +19 -3
  16. package/build/user.js.map +1 -1
  17. package/build/v1/profile.js +6 -3
  18. package/build/v1/profile.js.map +1 -1
  19. package/build/v1/search.js +4 -3
  20. package/build/v1/search.js.map +1 -1
  21. package/build/v1/token.js +1 -1
  22. package/build/v1/token.js.map +1 -1
  23. package/build/whoami.js +2 -2
  24. package/build/whoami.js.map +1 -1
  25. package/package.json +17 -17
  26. package/src/index.ts +3 -2
  27. package/src/package.ts +3 -1
  28. package/src/publish.ts +31 -6
  29. package/src/user.ts +17 -0
  30. package/src/v1/profile.ts +5 -3
  31. package/src/v1/search.ts +2 -1
  32. package/test/integration/_helper.ts +33 -1
  33. package/test/integration/config/owner.yaml +24 -0
  34. package/test/integration/config/profile.yaml +27 -0
  35. package/test/integration/config/user.jwt.yaml +1 -1
  36. package/test/integration/config/user.yaml +1 -1
  37. package/test/integration/distTag.spec.ts +1 -0
  38. package/test/integration/owner.spec.ts +119 -0
  39. package/test/integration/package.spec.ts +3 -2
  40. package/test/integration/ping.spec.ts +1 -0
  41. package/test/integration/profile.spec.ts +112 -0
  42. package/test/integration/publish.spec.ts +1 -0
  43. package/test/integration/search.spec.ts +13 -0
  44. package/test/integration/star.spec.ts +1 -0
  45. package/test/integration/token.spec.ts +1 -0
  46. package/test/integration/user.spec.ts +54 -1
  47. package/test/integration/whoami.spec.ts +1 -0
  48. package/test/unit/validate.api.params.middleware.spec.ts +1 -0
  49. 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: start a package depends of the publish and unpublish permissions, there is no
80
- * specific flag for star or un start.
79
+ * Permissions: staring a package depends of the publish and unpublish permissions, there is no
80
+ * specific flag for star or unstar.
81
81
  * The URL for star is similar to the unpublish (change package format)
82
82
  *
83
- * npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference
83
+ * npm has no endpoint for staring a package, rather mutate the metadata and acts as, the difference
84
84
  * is the users property which is part of the payload and the body only includes
85
85
  *
86
86
  * {
@@ -89,7 +89,24 @@ const debug = buildDebug('verdaccio:api:publish');
89
89
  "users": {
90
90
  [username]: boolean value (true, false)
91
91
  }
92
- }
92
+ }
93
+ *
94
+ * 4. Change owners of a package
95
+ *
96
+ * Similar to staring a package, changing owners (maintainers) of a package uses the publish
97
+ * endpoint.
98
+ *
99
+ * The body includes a list of the new owners with the following format
100
+ *
101
+ * {
102
+ "_id": pkgName,
103
+ "_rev": "4-b0cdaefc9bdb77c8",
104
+ "maintainers": [
105
+ { "name": "first owner", "email": "me@verdaccio.org" },
106
+ { "name": "second owner", "email": "you@verdaccio.org" },
107
+ ...
108
+ ]
109
+ }
93
110
  *
94
111
  */
95
112
  export default function publish(router: Router, auth: Auth, storage: Storage): void {
@@ -127,10 +144,11 @@ export default function publish(router: Router, auth: Auth, storage: Storage): v
127
144
  async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
128
145
  const packageName = req.params.package;
129
146
  const rev = req.params.revision;
147
+ const username = req?.remote_user?.name;
130
148
 
131
149
  logger.debug({ packageName }, `unpublishing @{packageName}`);
132
150
  try {
133
- await storage.removePackage(packageName, rev);
151
+ await storage.removePackage(packageName, rev, username);
134
152
  debug('package %s unpublished', packageName);
135
153
  res.status(HTTP_STATUS.CREATED);
136
154
  return next({ ok: API_MESSAGE.PKG_REMOVED });
@@ -155,13 +173,14 @@ export default function publish(router: Router, auth: Auth, storage: Storage): v
155
173
  ): Promise<void> {
156
174
  const packageName = req.params.package;
157
175
  const { filename, revision } = req.params;
176
+ const username = req?.remote_user?.name;
158
177
 
159
178
  logger.debug(
160
179
  { packageName, filename, revision },
161
180
  `removing a tarball for @{packageName}-@{tarballName}-@{revision}`
162
181
  );
163
182
  try {
164
- await storage.removeTarball(packageName, filename, revision);
183
+ await storage.removeTarball(packageName, filename, revision, username);
165
184
  res.status(HTTP_STATUS.CREATED);
166
185
 
167
186
  logger.debug(
@@ -188,6 +207,12 @@ export function publishPackage(storage: Storage): any {
188
207
  const metadata = req.body;
189
208
  const username = req?.remote_user?.name;
190
209
 
210
+ debug('publishing package %o for user %o', packageName, username);
211
+ logger.debug(
212
+ { packageName, username },
213
+ 'publishing package @{packageName} for user @{username}'
214
+ );
215
+
191
216
  try {
192
217
  const message = await storage.updateManifest(metadata, {
193
218
  name: packageName,
package/src/user.ts CHANGED
@@ -27,10 +27,22 @@ export default function (route: Router, auth: Auth, config: Config): void {
27
27
  rateLimit(config?.userRateLimit),
28
28
  function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
29
29
  debug('verifying user');
30
+
31
+ if (typeof req.remote_user.name !== 'string' || req.remote_user.name === '') {
32
+ debug('user not logged in');
33
+ res.status(HTTP_STATUS.OK);
34
+ return next({ ok: false });
35
+ }
36
+
37
+ const username = req.params.org_couchdb_user.split(':')[1];
30
38
  const message = getAuthenticatedMessage(req.remote_user.name);
31
39
  debug('user authenticated message %o', message);
32
40
  res.status(HTTP_STATUS.OK);
33
41
  next({
42
+ // 'npm owner' requires user info
43
+ // TODO: we don't have the email
44
+ name: username,
45
+ email: '',
34
46
  ok: message,
35
47
  });
36
48
  }
@@ -61,6 +73,10 @@ export default function (route: Router, auth: Auth, config: Config): void {
61
73
  debug('login or adduser');
62
74
  const remoteName = req?.remote_user?.name;
63
75
 
76
+ if (!validatioUtils.validateUserName(req.params.org_couchdb_user, name)) {
77
+ return next(errorUtils.getBadRequest(API_ERROR.USERNAME_MISMATCH));
78
+ }
79
+
64
80
  if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {
65
81
  debug('login: no remote user detected');
66
82
  auth.authenticate(
@@ -97,6 +113,7 @@ export default function (route: Router, auth: Auth, config: Config): void {
97
113
  }
98
114
  );
99
115
  } else {
116
+ debug('adduser: %o', name);
100
117
  if (
101
118
  validatioUtils.validatePassword(
102
119
  password,
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('stream finish');
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
@@ -10,7 +10,7 @@ auth:
10
10
 
11
11
  uplinks:
12
12
  ver:
13
- url: https://registry.verdaccio.org
13
+ url: https://registry.npmjs.org
14
14
 
15
15
  security:
16
16
  api:
@@ -7,7 +7,7 @@ web:
7
7
 
8
8
  uplinks:
9
9
  ver:
10
- url: https://registry.verdaccio.org
10
+ url: https://registry.npmjs.org
11
11
 
12
12
  log: { type: stdout, format: pretty, level: trace }
13
13
 
@@ -1,4 +1,5 @@
1
1
  import supertest from 'supertest';
2
+ import { beforeEach, describe, expect, test } from 'vitest';
2
3
 
3
4
  import { API_MESSAGE, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
4
5
 
@@ -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
- ['foo', 'foo-1.0.0.tgz'],
30
- ['@scope/foo', 'foo-1.0.0.tgz'],
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)
@@ -1,4 +1,5 @@
1
1
  import supertest from 'supertest';
2
+ import { describe, expect, test } from 'vitest';
2
3
 
3
4
  import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
4
5
 
@@ -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',
@@ -1,5 +1,6 @@
1
1
  import nock from 'nock';
2
2
  import supertest from 'supertest';
3
+ import { describe, expect, test } from 'vitest';
3
4
 
4
5
  import { HTTP_STATUS } from '@verdaccio/core';
5
6
  import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
@@ -1,5 +1,6 @@
1
1
  import _ from 'lodash';
2
2
  import supertest from 'supertest';
3
+ import { describe, expect, test } from 'vitest';
3
4
 
4
5
  import {
5
6
  API_ERROR,