@verdaccio/api 6.0.0-6-next.25 → 6.0.0-6-next.28

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 (64) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/build/dist-tags.js +40 -35
  3. package/build/dist-tags.js.map +1 -1
  4. package/build/index.js +1 -7
  5. package/build/index.js.map +1 -1
  6. package/build/package.js +39 -28
  7. package/build/package.js.map +1 -1
  8. package/build/ping.js.map +1 -1
  9. package/build/publish.d.ts +79 -22
  10. package/build/publish.js +89 -313
  11. package/build/publish.js.map +1 -1
  12. package/build/search.js.map +1 -1
  13. package/build/star.js +53 -54
  14. package/build/star.js.map +1 -1
  15. package/build/stars.js +6 -6
  16. package/build/stars.js.map +1 -1
  17. package/build/user.js +19 -1
  18. package/build/user.js.map +1 -1
  19. package/build/v1/profile.js.map +1 -1
  20. package/build/v1/search.js +2 -4
  21. package/build/v1/search.js.map +1 -1
  22. package/build/v1/token.js.map +1 -1
  23. package/build/whoami.js +11 -25
  24. package/build/whoami.js.map +1 -1
  25. package/jest.config.js +8 -1
  26. package/package.json +71 -69
  27. package/src/dist-tags.ts +63 -50
  28. package/src/index.ts +1 -6
  29. package/src/package.ts +37 -44
  30. package/src/publish.ts +115 -336
  31. package/src/star.ts +53 -52
  32. package/src/stars.ts +9 -11
  33. package/src/user.ts +19 -2
  34. package/src/v1/search.ts +3 -3
  35. package/src/whoami.ts +8 -24
  36. package/test/integration/_helper.ts +97 -54
  37. package/test/integration/config/distTag.yaml +25 -0
  38. package/test/integration/config/package.yaml +3 -8
  39. package/test/integration/config/ping.yaml +2 -6
  40. package/test/integration/config/publish.yaml +4 -10
  41. package/test/integration/config/search.yaml +29 -0
  42. package/test/integration/config/token.jwt.yaml +27 -0
  43. package/test/integration/config/token.yaml +19 -0
  44. package/test/integration/config/user.jwt.yaml +37 -0
  45. package/test/integration/config/user.yaml +8 -14
  46. package/test/integration/config/whoami.yaml +4 -11
  47. package/test/integration/distTag.spec.ts +76 -0
  48. package/test/integration/package.spec.ts +53 -75
  49. package/test/integration/ping.spec.ts +4 -3
  50. package/test/integration/publish.spec.ts +60 -25
  51. package/test/integration/search.spec.ts +126 -0
  52. package/test/integration/token.spec.ts +124 -0
  53. package/test/integration/user.jwt.spec.ts +87 -0
  54. package/test/integration/user.spec.ts +1 -0
  55. package/test/integration/whoami.spec.ts +21 -39
  56. package/test/unit/publish.disabled.ts +252 -0
  57. package/tsconfig.json +1 -1
  58. package/__mocks__/@verdaccio/logger/index.js +0 -21
  59. package/build/utils.d.ts +0 -7
  60. package/build/utils.js +0 -36
  61. package/build/utils.js.map +0 -1
  62. package/src/utils.ts +0 -25
  63. package/test/unit/__snapshots__/publish.spec.ts.snap +0 -49
  64. package/test/unit/publish.spec.ts +0 -300
@@ -1,33 +1,9 @@
1
1
  import supertest from 'supertest';
2
2
 
3
- import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
3
+ import { DIST_TAGS, HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
4
+ import { Storage } from '@verdaccio/store';
4
5
 
5
- import { $RequestExtend, $ResponseExtend } from '../../types/custom';
6
- import { initializeServer, publishTaggedVersion, publishVersion } from './_helper';
7
-
8
- const mockApiJWTmiddleware = jest.fn(
9
- () =>
10
- (req: $RequestExtend, res: $ResponseExtend, _next): void => {
11
- req.remote_user = { name: 'foo', groups: [], real_groups: [] };
12
- _next();
13
- }
14
- );
15
-
16
- jest.mock('@verdaccio/auth', () => ({
17
- Auth: class {
18
- apiJWTmiddleware() {
19
- return mockApiJWTmiddleware();
20
- }
21
- allow_access(_d, _f, cb) {
22
- // always allow access
23
- cb(null, true);
24
- }
25
- allow_publish(_d, _f, cb) {
26
- // always allow publish
27
- cb(null, true);
28
- }
29
- },
30
- }));
6
+ import { initializeServer, publishVersion } from './_helper';
31
7
 
32
8
  describe('package', () => {
33
9
  let app;
@@ -35,57 +11,59 @@ describe('package', () => {
35
11
  app = await initializeServer('package.yaml');
36
12
  });
37
13
 
38
- test('should return a package', async () => {
39
- await publishVersion(app, 'package.yaml', 'foo', '1.0.0');
40
- return new Promise((resolve) => {
41
- supertest(app)
42
- .get('/foo')
43
- .set('Accept', HEADERS.JSON)
44
- .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
45
- .expect(HTTP_STATUS.OK)
46
- .then((response) => {
47
- expect(response.body.name).toEqual('foo');
48
- resolve(response);
49
- });
50
- });
14
+ test.each([['foo'], ['@scope/foo']])('should return a foo private package', async (pkg) => {
15
+ await publishVersion(app, pkg, '1.0.0');
16
+ const response = await supertest(app)
17
+ .get(`/${pkg}`)
18
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
19
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
20
+ .expect(HTTP_STATUS.OK);
21
+ expect(response.body.name).toEqual(pkg);
51
22
  });
52
23
 
53
- test('should return a package by version', async () => {
54
- await publishVersion(app, 'package.yaml', 'foo2', '1.0.0');
55
- return new Promise((resolve) => {
56
- supertest(app)
57
- .get('/foo2/1.0.0')
58
- .set('Accept', HEADERS.JSON)
24
+ test.each([['foo'], ['@scope/foo']])(
25
+ 'should return a foo private package by version',
26
+ async (pkg) => {
27
+ await publishVersion(app, pkg, '1.0.0');
28
+ const response = await supertest(app)
29
+ .get(`/${pkg}`)
30
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
59
31
  .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
60
- .expect(HTTP_STATUS.OK)
61
- .then((response) => {
62
- expect(response.body.name).toEqual('foo2');
63
- resolve(response);
64
- });
65
- });
66
- });
32
+ .expect(HTTP_STATUS.OK);
33
+ expect(response.body.name).toEqual(pkg);
34
+ }
35
+ );
67
36
 
68
- // FIXME: investigate the 404
69
- test.skip('should return a package by dist-tag', async (done) => {
70
- // await publishVersion(app, 'package.yaml', 'foo3', '1.0.0');
71
- await publishVersion(app, 'package.yaml', 'foo-tagged', '1.0.0');
72
- await publishTaggedVersion(app, 'package.yaml', 'foo-tagged', '1.0.1', 'test');
73
- return supertest(app)
74
- .get('/foo-tagged/1.0.1')
75
- .set('Accept', HEADERS.JSON)
76
- .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
77
- .expect(HTTP_STATUS.CREATED)
78
- .then((response) => {
79
- expect(response.body.name).toEqual('foo3');
80
- done();
81
- });
82
- });
37
+ test.each([['foo'], ['@scope/foo']])(
38
+ 'should return a foo private package by version',
39
+ async (pkg) => {
40
+ await publishVersion(app, pkg, '1.0.0');
41
+ const response = await supertest(app)
42
+ .get(`/${pkg}`)
43
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
44
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
45
+ .expect(HTTP_STATUS.OK);
46
+ expect(response.body.name).toEqual(pkg);
47
+ }
48
+ );
83
49
 
84
- test('should return 404', async () => {
85
- return supertest(app)
86
- .get('/404-not-found')
87
- .set('Accept', HEADERS.JSON)
88
- .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
89
- .expect(HTTP_STATUS.NOT_FOUND);
90
- });
50
+ test.each([['foo-abbreviated'], ['@scope/foo-abbreviated']])(
51
+ 'should return abbreviated local manifest',
52
+ async (pkg) => {
53
+ await publishVersion(app, pkg, '1.0.0');
54
+ const response = await supertest(app)
55
+ .get(`/${pkg}`)
56
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
57
+ .set(HEADERS.ACCEPT, Storage.ABBREVIATED_HEADER)
58
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
59
+ .expect(HTTP_STATUS.OK);
60
+ expect(response.body.name).toEqual(pkg);
61
+ expect(response.body.time).toBeDefined();
62
+ expect(response.body.modified).toBeDefined();
63
+ expect(response.body[DIST_TAGS]).toEqual({ latest: '1.0.0' });
64
+ expect(response.body.readme).not.toBeDefined();
65
+ expect(response.body._rev).not.toBeDefined();
66
+ expect(response.body.users).not.toBeDefined();
67
+ }
68
+ );
91
69
  });
@@ -6,11 +6,12 @@ import { initializeServer } from './_helper';
6
6
 
7
7
  describe('ping', () => {
8
8
  test('should return the reply the ping', async () => {
9
- return supertest(await initializeServer('ping.yaml'))
9
+ const app = await initializeServer('ping.yaml');
10
+ const response = await supertest(app)
10
11
  .get('/-/ping')
11
12
  .set('Accept', HEADERS.JSON)
12
13
  .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
13
- .expect(HTTP_STATUS.OK)
14
- .then((response) => expect(response.body).toEqual({}));
14
+ .expect(HTTP_STATUS.OK);
15
+ expect(response.body).toEqual({});
15
16
  });
16
17
  });
@@ -60,9 +60,9 @@ describe('publish', () => {
60
60
  describe('handle invalid publish formats', () => {
61
61
  const pkgName = 'test';
62
62
  const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
63
- test.skip('should fail on publish a bad _attachments package', async (done) => {
63
+ test('should fail on publish a bad _attachments package', async () => {
64
64
  const app = await initializeServer('publish.yaml');
65
- return supertest(app)
65
+ const response = await supertest(app)
66
66
  .put(`/${encodeURIComponent(pkgName)}`)
67
67
  .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
68
68
  .send(
@@ -73,12 +73,8 @@ describe('publish', () => {
73
73
  )
74
74
  )
75
75
  .set('accept', HEADERS.GZIP)
76
- .expect(HTTP_STATUS.BAD_REQUEST)
77
- .then((response) => {
78
- console.log('response.body', response.body);
79
- expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
80
- done();
81
- });
76
+ .expect(HTTP_STATUS.BAD_REQUEST);
77
+ expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
82
78
  });
83
79
 
84
80
  test('should fail on publish a bad versions package', async () => {
@@ -97,7 +93,6 @@ describe('publish', () => {
97
93
  .set('accept', HEADERS.GZIP)
98
94
  .expect(HTTP_STATUS.BAD_REQUEST)
99
95
  .then((response) => {
100
- console.log('response.body', response.body);
101
96
  expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
102
97
  resolve(response);
103
98
  });
@@ -109,7 +104,7 @@ describe('publish', () => {
109
104
  test('should publish a package', async () => {
110
105
  const app = await initializeServer('publish.yaml');
111
106
  return new Promise((resolve) => {
112
- publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
107
+ publishVersion(app, 'foo', '1.0.0')
113
108
  .expect(HTTP_STATUS.CREATED)
114
109
  .then((response) => {
115
110
  expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
@@ -126,13 +121,7 @@ describe('publish', () => {
126
121
  supertest(app)
127
122
  .put(`/${encodeURIComponent(pkgName)}`)
128
123
  .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
129
- .send(
130
- JSON.stringify(
131
- Object.assign({}, pkgMetadata, {
132
- _attachments: null,
133
- })
134
- )
135
- )
124
+ .send(JSON.stringify(Object.assign({}, pkgMetadata)))
136
125
  .set('accept', HEADERS.GZIP)
137
126
  .expect(HTTP_STATUS.CREATED)
138
127
  .then((response) => {
@@ -173,12 +162,11 @@ describe('publish', () => {
173
162
 
174
163
  test('should fails on publish a duplicated package', async () => {
175
164
  const app = await initializeServer('publish.yaml');
176
- await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
165
+ await publishVersion(app, 'foo', '1.0.0');
177
166
  return new Promise((resolve) => {
178
- publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
167
+ publishVersion(app, 'foo', '1.0.0')
179
168
  .expect(HTTP_STATUS.CONFLICT)
180
169
  .then((response) => {
181
- console.log('response.body', response.body);
182
170
  expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
183
171
  resolve(response);
184
172
  });
@@ -186,14 +174,61 @@ describe('publish', () => {
186
174
  });
187
175
 
188
176
  describe('unpublish a package', () => {
189
- let app;
177
+ test('should unpublish entirely a package', async () => {
178
+ const app = await initializeServer('publish.yaml');
179
+ await publishVersion(app, 'foo', '1.0.0');
180
+ const response = await supertest(app)
181
+ // FIXME: should be filtered by revision to avoid
182
+ // conflicts
183
+ .delete(`/${encodeURIComponent('foo')}/-rev/xxx`)
184
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
185
+ .expect(HTTP_STATUS.CREATED);
186
+ expect(response.body.ok).toEqual(API_MESSAGE.PKG_REMOVED);
187
+ // package should be completely un published
188
+ await supertest(app)
189
+ .get('/foo')
190
+ .set('Accept', HEADERS.JSON)
191
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
192
+ .expect(HTTP_STATUS.NOT_FOUND);
193
+ });
194
+
195
+ test('should fails unpublish entirely a package', async () => {
196
+ const app = await initializeServer('publish.yaml');
197
+ const response = await supertest(app)
198
+ .delete(`/${encodeURIComponent('foo')}/-rev/1cf3-fe3`)
199
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
200
+ .expect(HTTP_STATUS.NOT_FOUND);
201
+ expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
202
+ });
203
+
204
+ test('should fails remove a tarball of a package does not exist', async () => {
205
+ const app = await initializeServer('publish.yaml');
206
+ const response = await supertest(app)
207
+ .delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
208
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
209
+ .expect(HTTP_STATUS.NOT_FOUND);
210
+ expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
211
+ });
190
212
 
191
- beforeEach(async () => {
192
- app = await initializeServer('publish.yaml');
193
- await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
213
+ test('should fails on try remove a tarball does not exist', async () => {
214
+ const app = await initializeServer('publish.yaml');
215
+ await publishVersion(app, 'foo', '1.0.0');
216
+ const response = await supertest(app)
217
+ .delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
218
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
219
+ .expect(HTTP_STATUS.NOT_FOUND);
220
+ expect(response.body.error).toEqual(API_ERROR.NO_SUCH_FILE);
194
221
  });
195
222
 
196
- test('should unpublish a package', () => {});
223
+ test('should remove a tarball that does exist', async () => {
224
+ const app = await initializeServer('publish.yaml');
225
+ await publishVersion(app, 'foo', '1.0.0');
226
+ const response = await supertest(app)
227
+ .delete(`/foo/-/foo-1.0.0.tgz/-rev/revision`)
228
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
229
+ .expect(HTTP_STATUS.CREATED);
230
+ expect(response.body.ok).toEqual(API_MESSAGE.TARBALL_REMOVED);
231
+ });
197
232
  });
198
233
 
199
234
  describe('star a package', () => {});
@@ -0,0 +1,126 @@
1
+ import MockDate from 'mockdate';
2
+ import supertest from 'supertest';
3
+
4
+ import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
5
+
6
+ import { createUser, initializeServer, publishVersionWithToken } from './_helper';
7
+
8
+ describe('search', () => {
9
+ let app;
10
+ beforeEach(async () => {
11
+ app = await initializeServer('search.yaml');
12
+ });
13
+
14
+ describe('search authenticated', () => {
15
+ test.each([['foo']])('should return a foo private package', async (pkg) => {
16
+ const mockDate = '2018-01-14T11:17:40.712Z';
17
+ MockDate.set(mockDate);
18
+ const res = await createUser(app, 'test', 'test');
19
+ await publishVersionWithToken(app, pkg, '1.0.0', res.body.token);
20
+ // this should not be displayed as part of the search
21
+ await publishVersionWithToken(app, 'private-auth', '1.0.0', res.body.token);
22
+ const response = await supertest(app)
23
+ .get(
24
+ `/-/v1/search?text=${encodeURIComponent(
25
+ pkg
26
+ )}&size=2000&from=0&quality=1&popularity=0.1&maintenance=0.1`
27
+ )
28
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
29
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
30
+ .expect(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
31
+ .expect(HTTP_STATUS.OK);
32
+ expect(response.body).toEqual({
33
+ objects: [
34
+ {
35
+ package: {
36
+ author: {
37
+ email: 'user@domain.com',
38
+ name: 'User NPM',
39
+ },
40
+ date: mockDate,
41
+ description: 'package generated',
42
+ keywords: [],
43
+ links: {
44
+ npm: '',
45
+ },
46
+ name: pkg,
47
+ publisher: {},
48
+ scope: '',
49
+ version: '1.0.0',
50
+ },
51
+ score: {
52
+ detail: {
53
+ maintenance: 0,
54
+ popularity: 1,
55
+ quality: 1,
56
+ },
57
+ final: 1,
58
+ },
59
+ searchScore: 1,
60
+ verdaccioPkgCached: false,
61
+ verdaccioPrivate: true,
62
+ },
63
+ ],
64
+ time: 'Sun, 14 Jan 2018 11:17:40 GMT',
65
+ total: 1,
66
+ });
67
+ });
68
+
69
+ test.each([['@scope/foo']])('should return a scoped foo private package', async (pkg) => {
70
+ const mockDate = '2018-01-14T11:17:40.712Z';
71
+ MockDate.set(mockDate);
72
+ const res = await createUser(app, 'test', 'test');
73
+ await publishVersionWithToken(app, pkg, '1.0.0', res.body.token);
74
+ // this should not be displayed as part of the search
75
+ await publishVersionWithToken(app, '@private/auth', '1.0.0', res.body.token);
76
+ const response = await supertest(app)
77
+ .get(
78
+ `/-/v1/search?text=${encodeURIComponent(
79
+ pkg
80
+ )}&size=2000&from=0&quality=1&popularity=0.1&maintenance=0.1`
81
+ )
82
+ .set(HEADERS.ACCEPT, HEADERS.JSON)
83
+ .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
84
+ .expect(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
85
+ .expect(HTTP_STATUS.OK);
86
+ expect(response.body).toEqual({
87
+ objects: [
88
+ {
89
+ package: {
90
+ author: {
91
+ email: 'user@domain.com',
92
+ name: 'User NPM',
93
+ },
94
+ date: mockDate,
95
+ description: 'package generated',
96
+ keywords: [],
97
+ links: {
98
+ npm: '',
99
+ },
100
+ name: pkg,
101
+ publisher: {},
102
+ scope: '@scope',
103
+ version: '1.0.0',
104
+ },
105
+ score: {
106
+ detail: {
107
+ maintenance: 0,
108
+ popularity: 1,
109
+ quality: 1,
110
+ },
111
+ final: 1,
112
+ },
113
+ searchScore: 1,
114
+ verdaccioPkgCached: false,
115
+ verdaccioPrivate: true,
116
+ },
117
+ ],
118
+ time: 'Sun, 14 Jan 2018 11:17:40 GMT',
119
+ total: 1,
120
+ });
121
+ });
122
+ });
123
+ describe('error handling', () => {
124
+ test.todo('should able to abort the request');
125
+ });
126
+ });
@@ -0,0 +1,124 @@
1
+ import _ from 'lodash';
2
+ import supertest from 'supertest';
3
+
4
+ import {
5
+ API_ERROR,
6
+ HEADERS,
7
+ HEADER_TYPE,
8
+ HTTP_STATUS,
9
+ SUPPORT_ERRORS,
10
+ TOKEN_BEARER,
11
+ } from '@verdaccio/core';
12
+ import { buildToken } from '@verdaccio/utils';
13
+
14
+ import { deleteTokenCLI, generateTokenCLI, getNewToken, initializeServer } from './_helper';
15
+
16
+ describe('token', () => {
17
+ describe('basics', () => {
18
+ test.each([['token.yaml'], ['token.jwt.yaml']])('should list empty tokens', async (conf) => {
19
+ const app = await initializeServer(conf);
20
+ const token = await getNewToken(app, { name: 'jota_token', password: 'secretPass' });
21
+ const response = await supertest(app)
22
+ .get('/-/npm/v1/tokens')
23
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
24
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
25
+ .expect(HTTP_STATUS.OK);
26
+ expect(response.body.objects).toHaveLength(0);
27
+ });
28
+
29
+ test.each([['token.yaml'], ['token.jwt.yaml']])('should generate one token', async (conf) => {
30
+ const app = await initializeServer(conf);
31
+ const credentials = { name: 'jota_token', password: 'secretPass' };
32
+ const token = await getNewToken(app, credentials);
33
+ await generateTokenCLI(app, token, {
34
+ password: credentials.password,
35
+ readonly: false,
36
+ cidr_whitelist: [],
37
+ });
38
+ const response = await supertest(app)
39
+ .get('/-/npm/v1/tokens')
40
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
41
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
42
+ .expect(HTTP_STATUS.OK);
43
+ const { objects, urls } = response.body;
44
+
45
+ expect(objects).toHaveLength(1);
46
+ const [tokenGenerated] = objects;
47
+ expect(tokenGenerated.user).toEqual(credentials.name);
48
+ expect(tokenGenerated.readonly).toBeFalsy();
49
+ expect(tokenGenerated.token).toMatch(/.../);
50
+ expect(_.isString(tokenGenerated.created)).toBeTruthy();
51
+
52
+ // we don't support pagination yet
53
+ expect(urls.next).toEqual('');
54
+ });
55
+
56
+ test.each([['token.yaml'], ['token.jwt.yaml']])('should delete a token', async (conf) => {
57
+ const app = await initializeServer(conf);
58
+ const credentials = { name: 'jota_token', password: 'secretPass' };
59
+ const token = await getNewToken(app, credentials);
60
+ const response = await generateTokenCLI(app, token, {
61
+ password: credentials.password,
62
+ readonly: false,
63
+ cidr_whitelist: [],
64
+ });
65
+
66
+ const key = response.body.key;
67
+ await deleteTokenCLI(app, token, key);
68
+ const response2 = await supertest(app)
69
+ .get('/-/npm/v1/tokens')
70
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
71
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
72
+ .expect(HTTP_STATUS.OK);
73
+ const { objects } = response2.body;
74
+ expect(objects).toHaveLength(0);
75
+ });
76
+ });
77
+
78
+ describe('handle errors', () => {
79
+ test.each([['token.yaml'], ['token.jwt.yaml']])('should delete a token', async (conf) => {
80
+ const app = await initializeServer(conf);
81
+ const credentials = { name: 'jota_token', password: 'secretPass' };
82
+ const token = await getNewToken(app, credentials);
83
+ const resp = await generateTokenCLI(app, token, {
84
+ password: 'wrongPassword',
85
+ readonly: false,
86
+ cidr_whitelist: [],
87
+ });
88
+ expect(resp.body.error).toEqual(API_ERROR.BAD_USERNAME_PASSWORD);
89
+ });
90
+
91
+ test.each([['token.yaml'], ['token.jwt.yaml']])(
92
+ 'should fail if readonly is missing',
93
+ async (conf) => {
94
+ const app = await initializeServer(conf);
95
+ const credentials = { name: 'jota_token', password: 'secretPass' };
96
+ const token = await getNewToken(app, credentials);
97
+ const resp = await generateTokenCLI(app, token, {
98
+ password: credentials.password,
99
+ cidr_whitelist: [],
100
+ });
101
+ expect(resp.body.error).toEqual(SUPPORT_ERRORS.PARAMETERS_NOT_VALID);
102
+ }
103
+ );
104
+ });
105
+
106
+ test.each([['token.yaml'], ['token.jwt.yaml']])(
107
+ 'should fail if cidr_whitelist is missing',
108
+ async (conf) => {
109
+ const app = await initializeServer(conf);
110
+ const credentials = { name: 'jota_token', password: 'secretPass' };
111
+ const token = await getNewToken(app, credentials);
112
+ const resp = await generateTokenCLI(app, token, {
113
+ password: credentials.password,
114
+ readonly: false,
115
+ });
116
+ expect(resp.body.error).toEqual(SUPPORT_ERRORS.PARAMETERS_NOT_VALID);
117
+ }
118
+ );
119
+
120
+ test.todo('handle failure if delete token');
121
+ test.todo('handle failure if getApiToken fails');
122
+ test.todo('handle failure if token creating fails');
123
+ test.todo('handle failure if token list fails');
124
+ });
@@ -0,0 +1,87 @@
1
+ import nock from 'nock';
2
+ import supertest from 'supertest';
3
+
4
+ import { API_ERROR, HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
5
+ import { generateRemotePackageMetadata } from '@verdaccio/test-helper';
6
+ import { buildToken } from '@verdaccio/utils';
7
+
8
+ import { createUser, getPackage, initializeServer } from './_helper';
9
+
10
+ const FORBIDDEN_VUE = 'authorization required to access package vue';
11
+
12
+ describe('token', () => {
13
+ describe('basics', () => {
14
+ const FAKE_TOKEN: string = buildToken(TOKEN_BEARER, 'fake');
15
+ test.each([['user.yaml'], ['user.jwt.yaml']])('should test add a new user', async (conf) => {
16
+ const upstreamManifest = generateRemotePackageMetadata(
17
+ 'vue',
18
+ '1.0.0',
19
+ 'https://registry.verdaccio.org'
20
+ );
21
+ nock('https://registry.verdaccio.org').get(`/vue`).reply(201, upstreamManifest);
22
+
23
+ const app = await initializeServer(conf);
24
+ const credentials = { name: 'JotaJWT', password: 'secretPass' };
25
+ const response = await createUser(app, credentials.name, credentials.password);
26
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
27
+
28
+ const vueResponse = await getPackage(app, response.body.token, 'vue');
29
+ expect(vueResponse.body).toBeDefined();
30
+ expect(vueResponse.body.name).toMatch('vue');
31
+
32
+ const vueFailResp = await getPackage(app, FAKE_TOKEN, 'vue', HTTP_STATUS.UNAUTHORIZED);
33
+ expect(vueFailResp.body.error).toMatch(FORBIDDEN_VUE);
34
+ });
35
+
36
+ test.each([['user.yaml'], ['user.jwt.yaml']])('should test add a new user', async (conf) => {
37
+ const app = await initializeServer(conf);
38
+ const credentials = { name: 'JotaJWT', password: 'secretPass' };
39
+ const response = await createUser(app, credentials.name, credentials.password);
40
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
41
+ const response2 = await supertest(app)
42
+ .put(`/-/user/org.couchdb.user:${credentials.name}`)
43
+ .send({
44
+ name: credentials.name,
45
+ password: credentials.password,
46
+ })
47
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
48
+ .expect(HTTP_STATUS.CONFLICT);
49
+ expect(response2.body.error).toBe(API_ERROR.USERNAME_ALREADY_REGISTERED);
50
+ });
51
+
52
+ test.each([['user.yaml'], ['user.jwt.yaml']])(
53
+ 'should fails on login if user credentials are invalid even if jwt',
54
+ async (conf) => {
55
+ const app = await initializeServer(conf);
56
+ const credentials = { name: 'newFailsUser', password: 'secretPass' };
57
+ const response = await createUser(app, credentials.name, credentials.password);
58
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
59
+ const response2 = await supertest(app)
60
+ .put(`/-/user/org.couchdb.user:${credentials.name}`)
61
+ .send({
62
+ name: credentials.name,
63
+ password: 'BAD_PASSWORD',
64
+ })
65
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
66
+ .expect(HTTP_STATUS.UNAUTHORIZED);
67
+ expect(response2.body.error).toBe(API_ERROR.UNAUTHORIZED_ACCESS);
68
+ }
69
+ );
70
+
71
+ test.each([['user.yaml'], ['user.jwt.yaml']])(
72
+ 'should verify if user is logged',
73
+ async (conf) => {
74
+ const app = await initializeServer(conf);
75
+ const credentials = { name: 'jota', password: 'secretPass' };
76
+ const response = await createUser(app, credentials.name, credentials.password);
77
+ expect(response.body.ok).toMatch(`user '${credentials.name}' created`);
78
+ const response2 = await supertest(app)
79
+ .get(`/-/user/org.couchdb.user:${credentials.name}`)
80
+ .set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, response.body.token))
81
+ .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
82
+ .expect(HTTP_STATUS.OK);
83
+ expect(response2.body.ok).toBe(`you are authenticated as '${credentials.name}'`);
84
+ }
85
+ );
86
+ });
87
+ });
@@ -47,6 +47,7 @@ jest.mock('@verdaccio/auth', () => ({
47
47
  },
48
48
  }));
49
49
 
50
+ // FIXME: This might be covered with user.jwt.spec
50
51
  describe('user', () => {
51
52
  const credentials = { name: 'test', password: 'test' };
52
53