@verdaccio/api 8.1.0-next-8.12 → 8.1.0-next-8.13

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/README.md +17 -10
  2. package/package.json +16 -12
  3. package/.babelrc +0 -3
  4. package/.eslintrc +0 -5
  5. package/CHANGELOG.md +0 -2152
  6. package/src/dist-tags.ts +0 -106
  7. package/src/index.ts +0 -65
  8. package/src/package.ts +0 -117
  9. package/src/ping.ts +0 -14
  10. package/src/publish.ts +0 -262
  11. package/src/search.ts +0 -12
  12. package/src/stars.ts +0 -37
  13. package/src/user.ts +0 -179
  14. package/src/v1/profile.ts +0 -113
  15. package/src/v1/search.ts +0 -85
  16. package/src/v1/token.ts +0 -157
  17. package/src/whoami.ts +0 -26
  18. package/test/.eslintrc +0 -8
  19. package/test/integration/_helper.ts +0 -198
  20. package/test/integration/config/distTag.yaml +0 -25
  21. package/test/integration/config/owner.yaml +0 -24
  22. package/test/integration/config/package.yaml +0 -25
  23. package/test/integration/config/ping.yaml +0 -26
  24. package/test/integration/config/profile.yaml +0 -27
  25. package/test/integration/config/publish-proxy.yaml +0 -26
  26. package/test/integration/config/publish.yaml +0 -24
  27. package/test/integration/config/search.yaml +0 -29
  28. package/test/integration/config/star.yaml +0 -26
  29. package/test/integration/config/token.jwt.yaml +0 -27
  30. package/test/integration/config/token.yaml +0 -19
  31. package/test/integration/config/user.jwt.yaml +0 -37
  32. package/test/integration/config/user.yaml +0 -30
  33. package/test/integration/config/whoami.yaml +0 -29
  34. package/test/integration/distTag.spec.ts +0 -80
  35. package/test/integration/owner.spec.ts +0 -119
  36. package/test/integration/package.spec.ts +0 -107
  37. package/test/integration/ping.spec.ts +0 -18
  38. package/test/integration/profile.spec.ts +0 -112
  39. package/test/integration/publish.spec.ts +0 -232
  40. package/test/integration/search.spec.ts +0 -139
  41. package/test/integration/star.spec.ts +0 -74
  42. package/test/integration/token.spec.ts +0 -125
  43. package/test/integration/user.spec.ts +0 -222
  44. package/test/integration/whoami.spec.ts +0 -36
  45. package/test/unit/publish.disabled.ts +0 -252
  46. package/test/unit/validate.api.params.middleware.spec.ts +0 -44
  47. package/tsconfig.build.json +0 -9
  48. package/tsconfig.json +0 -41
  49. package/types/custom.d.ts +0 -16
package/src/user.ts DELETED
@@ -1,179 +0,0 @@
1
- import buildDebug from 'debug';
2
- import { Response, Router } from 'express';
3
-
4
- import { getApiToken } from '@verdaccio/auth';
5
- import { Auth } from '@verdaccio/auth';
6
- import { createRemoteUser } from '@verdaccio/config';
7
- import {
8
- API_ERROR,
9
- API_MESSAGE,
10
- HEADERS,
11
- HTTP_STATUS,
12
- errorUtils,
13
- validatioUtils,
14
- } from '@verdaccio/core';
15
- import { USER_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
16
- import { Logger } from '@verdaccio/types';
17
- import { Config, RemoteUser } from '@verdaccio/types';
18
- import { getAuthenticatedMessage, mask } from '@verdaccio/utils';
19
-
20
- import { $NextFunctionVer, $RequestExtend } from '../types/custom';
21
-
22
- const debug = buildDebug('verdaccio:api:user');
23
-
24
- export default function (route: Router, auth: Auth, config: Config, logger: Logger): void {
25
- route.get(
26
- USER_API_ENDPOINTS.get_user,
27
- rateLimit(config?.userRateLimit),
28
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
29
- debug('verifying user');
30
-
31
- if (
32
- !req.remote_user ||
33
- typeof req.remote_user.name !== 'string' ||
34
- req.remote_user.name === ''
35
- ) {
36
- debug('user not logged in');
37
- res.status(HTTP_STATUS.OK);
38
- return next({ ok: false });
39
- }
40
-
41
- const username = req.params.org_couchdb_user.split(':')[1];
42
- const message = getAuthenticatedMessage(req.remote_user.name);
43
- debug('user authenticated message %o', message);
44
- res.status(HTTP_STATUS.OK);
45
- next({
46
- // 'npm owner' requires user info
47
- // TODO: we don't have the email
48
- name: username,
49
- email: '',
50
- ok: message,
51
- });
52
- }
53
- );
54
-
55
- /**
56
- *
57
- * body example
58
- * req.body = {
59
- _id: "org.couchdb.user:jjjj",
60
- name: "jjjj",
61
- password: "jjjj",
62
- type: "user",
63
- roles: [],
64
- date: "2022-07-08T15:51:04.002Z",
65
- }
66
- *
67
- * @export
68
- * @param {Router} route
69
- * @param {Auth} auth
70
- * @param {Config} config
71
- */
72
- route.put(
73
- USER_API_ENDPOINTS.add_user,
74
- rateLimit(config?.userRateLimit),
75
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
76
- const { name, password } = req.body;
77
- debug('login or adduser');
78
- const remoteName = req?.remote_user?.name;
79
-
80
- if (!validatioUtils.validateUserName(req.params.org_couchdb_user, name)) {
81
- return next(errorUtils.getBadRequest(API_ERROR.USERNAME_MISMATCH));
82
- }
83
-
84
- if (typeof remoteName !== 'undefined' && typeof name === 'string' && remoteName === name) {
85
- debug('login: no remote user detected');
86
- auth.authenticate(
87
- name,
88
- password,
89
- async function callbackAuthenticate(err, user): Promise<void> {
90
- if (err) {
91
- logger.trace(
92
- { name, err },
93
- 'authenticating for user @{username} failed. Error: @{err.message}'
94
- );
95
- return next(
96
- errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD)
97
- );
98
- }
99
-
100
- const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);
101
- const token = await getApiToken(auth, config, restoredRemoteUser, password);
102
- debug('login: new token');
103
- if (!token) {
104
- return next(errorUtils.getUnauthorized());
105
- }
106
-
107
- res.status(HTTP_STATUS.CREATED);
108
- res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
109
-
110
- const message = getAuthenticatedMessage(req.remote_user.name);
111
- debug('login: created user message %o', message);
112
-
113
- return next({
114
- ok: message,
115
- token,
116
- });
117
- }
118
- );
119
- } else {
120
- debug('adduser: %o', name);
121
- if (
122
- validatioUtils.validatePassword(
123
- password,
124
- config?.serverSettings?.passwordValidationRegex
125
- ) === false
126
- ) {
127
- debug('adduser: invalid password');
128
- // eslint-disable-next-line new-cap
129
- return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.PASSWORD_SHORT));
130
- }
131
-
132
- auth.add_user(name, password, async function (err, user): Promise<void> {
133
- if (err) {
134
- if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {
135
- debug('adduser: error on create user');
136
- // With npm registering is the same as logging in,
137
- // and npm accepts only an 409 error.
138
- // So, changing status code here.
139
- return next(
140
- errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)
141
- );
142
- }
143
- return next(err);
144
- }
145
-
146
- const token =
147
- name && password
148
- ? await getApiToken(auth, config, user as RemoteUser, password)
149
- : undefined;
150
- if (token) {
151
- debug('adduser: new token %o', mask(token as string, 4));
152
- }
153
- if (!token) {
154
- return next(errorUtils.getUnauthorized());
155
- }
156
-
157
- req.remote_user = user;
158
- res.status(HTTP_STATUS.CREATED);
159
- res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
160
- debug('adduser: user has been created');
161
- return next({
162
- ok: `user '${req.body.name}' created`,
163
- token,
164
- });
165
- });
166
- }
167
- }
168
- );
169
-
170
- route.delete(
171
- USER_API_ENDPOINTS.user_token,
172
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
173
- res.status(HTTP_STATUS.OK);
174
- next({
175
- ok: API_MESSAGE.LOGGED_OUT,
176
- });
177
- }
178
- );
179
- }
package/src/v1/profile.ts DELETED
@@ -1,113 +0,0 @@
1
- import { Response, Router } from 'express';
2
- import _ from 'lodash';
3
-
4
- import { Auth } from '@verdaccio/auth';
5
- import {
6
- API_ERROR,
7
- APP_ERROR,
8
- HTTP_STATUS,
9
- SUPPORT_ERRORS,
10
- errorUtils,
11
- validatioUtils,
12
- } from '@verdaccio/core';
13
- import { PROFILE_API_ENDPOINTS } from '@verdaccio/middleware';
14
- import { rateLimit } from '@verdaccio/middleware';
15
- import { Config } from '@verdaccio/types';
16
-
17
- import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
18
-
19
- export interface Profile {
20
- tfa: boolean;
21
- name: string;
22
- email: string;
23
- email_verified: boolean;
24
- created: string;
25
- updated: string;
26
- cidr_whitelist: string[] | null;
27
- fullname: string;
28
- }
29
-
30
- export default function (route: Router, auth: Auth, config: Config): void {
31
- function buildProfile(name: string): Profile {
32
- return {
33
- tfa: false,
34
- name,
35
- email: '',
36
- email_verified: false,
37
- created: '',
38
- updated: '',
39
- cidr_whitelist: null,
40
- fullname: '',
41
- };
42
- }
43
-
44
- route.get(
45
- PROFILE_API_ENDPOINTS.get_profile,
46
- rateLimit(config?.userRateLimit),
47
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
48
- if (_.isNil(req.remote_user.name) === false) {
49
- return next(buildProfile(req.remote_user.name));
50
- }
51
-
52
- res.status(HTTP_STATUS.UNAUTHORIZED);
53
- return next({
54
- message: API_ERROR.MUST_BE_LOGGED,
55
- });
56
- }
57
- );
58
-
59
- route.post(
60
- PROFILE_API_ENDPOINTS.get_profile,
61
- rateLimit(config?.userRateLimit),
62
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
63
- if (_.isNil(req.remote_user.name)) {
64
- res.status(HTTP_STATUS.UNAUTHORIZED);
65
- return next({
66
- message: API_ERROR.MUST_BE_LOGGED,
67
- });
68
- }
69
-
70
- const { password, tfa } = req.body;
71
- const { name } = req.remote_user;
72
-
73
- if (_.isNil(password) === false) {
74
- if (
75
- validatioUtils.validatePassword(
76
- password.new,
77
- config?.serverSettings?.passwordValidationRegex
78
- ) === false
79
- ) {
80
- /* eslint new-cap:off */
81
- return next(errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.PASSWORD_SHORT));
82
- /* eslint new-cap:off */
83
- }
84
-
85
- if (_.isEmpty(password.old)) {
86
- return next(errorUtils.getBadRequest('old password is required'));
87
- }
88
-
89
- auth.changePassword(
90
- name,
91
- password.old,
92
- password.new,
93
- (err, isUpdated): $NextFunctionVer => {
94
- if (_.isNull(err) === false) {
95
- return next(errorUtils.getForbidden(err.message));
96
- }
97
-
98
- if (isUpdated) {
99
- return next(buildProfile(req.remote_user.name));
100
- }
101
- return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));
102
- }
103
- );
104
- } else if (_.isNil(tfa) === false) {
105
- return next(
106
- errorUtils.getCode(HTTP_STATUS.SERVICE_UNAVAILABLE, SUPPORT_ERRORS.TFA_DISABLED)
107
- );
108
- } else {
109
- return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, APP_ERROR.PROFILE_ERROR));
110
- }
111
- }
112
- );
113
- }
package/src/v1/search.ts DELETED
@@ -1,85 +0,0 @@
1
- import buildDebug from 'debug';
2
- import _ from 'lodash';
3
-
4
- import { Auth } from '@verdaccio/auth';
5
- import { HTTP_STATUS, searchUtils } from '@verdaccio/core';
6
- import { SEARCH_API_ENDPOINTS } from '@verdaccio/middleware';
7
- import { Storage } from '@verdaccio/store';
8
- import { Manifest } from '@verdaccio/types';
9
- import { Logger } from '@verdaccio/types';
10
-
11
- const debug = buildDebug('verdaccio:api:search');
12
-
13
- /**
14
- * Endpoint for npm search v1
15
- * Empty value
16
- * - {"objects":[],"total":0,"time":"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)"}
17
- * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
18
- */
19
- export default function (route, auth: Auth, storage: Storage, logger: Logger): void {
20
- function checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {
21
- return new Promise((resolve, reject) => {
22
- auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
23
- if (err) {
24
- if (err.status && String(err.status).match(/^4\d\d$/)) {
25
- // auth plugin returns 4xx user error,
26
- // that's equivalent of !allowed basically
27
- allowed = false;
28
- return resolve(null);
29
- } else {
30
- reject(err);
31
- }
32
- } else {
33
- return resolve(allowed ? pkg : null);
34
- }
35
- });
36
- });
37
- }
38
-
39
- route.get(SEARCH_API_ENDPOINTS.search, async (req, res, next) => {
40
- const { query, url } = req;
41
- let [size, from] = ['size', 'from'].map((k) => query[k]);
42
- let data;
43
- const abort = new AbortController();
44
-
45
- req.socket.on('close', function () {
46
- debug('search web aborted');
47
- abort.abort();
48
- });
49
-
50
- size = parseInt(size, 10) || 20;
51
- from = parseInt(from, 10) || 0;
52
-
53
- try {
54
- debug('storage search initiated');
55
- data = await storage.search({
56
- query,
57
- url,
58
- abort,
59
- });
60
- debug('storage items tota: %o', data.length);
61
- const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(
62
- data.map((pkgItem) => {
63
- return checkAccess(pkgItem, auth, req.remote_user);
64
- })
65
- );
66
-
67
- const final: searchUtils.SearchItemPkg[] = checkAccessPromises
68
- .filter((i) => !_.isNull(i))
69
- .slice(from, size);
70
- logger.debug(`search results ${final?.length}`);
71
-
72
- const response: searchUtils.SearchResults = {
73
- objects: final,
74
- total: final.length,
75
- time: new Date().toUTCString(),
76
- };
77
-
78
- res.status(HTTP_STATUS.OK).json(response);
79
- } catch (error) {
80
- logger.error({ error }, 'search endpoint has failed @{error.message}');
81
- next(next);
82
- return;
83
- }
84
- });
85
- }
package/src/v1/token.ts DELETED
@@ -1,157 +0,0 @@
1
- import { Response, Router } from 'express';
2
- import _ from 'lodash';
3
-
4
- import { getApiToken } from '@verdaccio/auth';
5
- import { Auth } from '@verdaccio/auth';
6
- import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, errorUtils } from '@verdaccio/core';
7
- import { rateLimit } from '@verdaccio/middleware';
8
- import { TOKEN_API_ENDPOINTS } from '@verdaccio/middleware';
9
- import { Storage } from '@verdaccio/store';
10
- import { Config, RemoteUser, Token } from '@verdaccio/types';
11
- import { Logger } from '@verdaccio/types';
12
- import { mask, stringToMD5 } from '@verdaccio/utils';
13
-
14
- import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
15
-
16
- export type NormalizeToken = Token & {
17
- created: string;
18
- };
19
-
20
- function normalizeToken(token: Token): NormalizeToken {
21
- return {
22
- ...token,
23
- created: new Date(token.created).toISOString(),
24
- };
25
- }
26
-
27
- // https://github.com/npm/npm-profile/blob/latest/lib/index.js
28
- export default function (
29
- route: Router,
30
- auth: Auth,
31
- storage: Storage,
32
- config: Config,
33
- logger: Logger
34
- ): void {
35
- route.get(
36
- TOKEN_API_ENDPOINTS.get_tokens,
37
- rateLimit(config?.userRateLimit),
38
- async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
39
- const { name } = req.remote_user;
40
-
41
- if (_.isNil(name) === false) {
42
- try {
43
- const tokens = await storage.readTokens({ user: name });
44
- const totalTokens = tokens.length;
45
- logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');
46
-
47
- res.status(HTTP_STATUS.OK);
48
- return next({
49
- objects: tokens.map(normalizeToken),
50
- urls: {
51
- next: '', // TODO: pagination?
52
- },
53
- });
54
- } catch (error: any) {
55
- logger.error({ error: error.msg }, 'token list has failed: @{error}');
56
- return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
57
- }
58
- }
59
- return next(errorUtils.getUnauthorized());
60
- }
61
- );
62
-
63
- route.post(
64
- TOKEN_API_ENDPOINTS.get_tokens,
65
- rateLimit(config?.userRateLimit),
66
- function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
67
- const { password, readonly, cidr_whitelist } = req.body;
68
- const { name } = req.remote_user;
69
-
70
- if (!_.isBoolean(readonly) || !_.isArray(cidr_whitelist)) {
71
- return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));
72
- }
73
-
74
- auth.authenticate(name, password, async (err, user?: RemoteUser) => {
75
- if (err) {
76
- const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;
77
- return next(errorUtils.getCode(errorCode, err.message));
78
- }
79
-
80
- req.remote_user = user;
81
-
82
- if (!_.isFunction(storage.saveToken)) {
83
- return next(
84
- errorUtils.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)
85
- );
86
- }
87
-
88
- try {
89
- const token = await getApiToken(auth, config, user as RemoteUser, password);
90
- if (!token) {
91
- throw errorUtils.getInternalError();
92
- }
93
-
94
- const key = stringToMD5(token);
95
- // TODO: use a utility here
96
- const maskedToken = mask(token, 5);
97
- const created = new Date().getTime();
98
-
99
- /**
100
- * cidr_whitelist: is not being used, we pass it through
101
- * token: we do not store the real token (it is generated once and retrieved
102
- * to the user), just a mask of it.
103
- */
104
- const saveToken: Token = {
105
- user: name,
106
- token: maskedToken,
107
- key,
108
- cidr: cidr_whitelist,
109
- readonly,
110
- created,
111
- };
112
-
113
- await storage.saveToken(saveToken);
114
- logger.debug({ key, name }, 'token @{key} was created for user @{name}');
115
- res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
116
- return next(
117
- normalizeToken({
118
- token,
119
- user: name,
120
- key: saveToken.key,
121
- cidr: cidr_whitelist,
122
- readonly,
123
- created: saveToken.created,
124
- })
125
- );
126
- } catch (error: any) {
127
- logger.error({ error: error.msg }, 'token creation has failed: @{error}');
128
- return next(errorUtils.getInternalError(error.message));
129
- }
130
- });
131
- }
132
- );
133
-
134
- route.delete(
135
- TOKEN_API_ENDPOINTS.delete_token,
136
- rateLimit(config?.userRateLimit),
137
- async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {
138
- const {
139
- params: { tokenKey },
140
- } = req;
141
- const { name } = req.remote_user;
142
-
143
- if (_.isNil(name) === false) {
144
- logger.debug({ name }, '@{name} has requested remove a token');
145
- try {
146
- await storage.deleteToken(name, tokenKey);
147
- logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');
148
- return next({});
149
- } catch (error: any) {
150
- logger.error({ error: error.msg }, 'token creation has failed: @{error}');
151
- return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
152
- }
153
- }
154
- return next(errorUtils.getUnauthorized());
155
- }
156
- );
157
- }
package/src/whoami.ts DELETED
@@ -1,26 +0,0 @@
1
- import buildDebug from 'debug';
2
- import { Response, Router } from 'express';
3
-
4
- import { errorUtils } from '@verdaccio/core';
5
- import { USER_API_ENDPOINTS } from '@verdaccio/middleware';
6
-
7
- import { $NextFunctionVer, $RequestExtend } from '../types/custom';
8
-
9
- const debug = buildDebug('verdaccio:api:user');
10
-
11
- export default function (route: Router): void {
12
- route.get(
13
- USER_API_ENDPOINTS.whoami,
14
- (req: $RequestExtend, _res: Response, next: $NextFunctionVer): any => {
15
- // remote_user is set by the auth middleware
16
- const username = req?.remote_user?.name;
17
- if (!username) {
18
- debug('whoami: user not found');
19
- return next(errorUtils.getUnauthorized('Unauthorized'));
20
- }
21
-
22
- debug('whoami: response %o', username);
23
- return next({ username: username });
24
- }
25
- );
26
- }
package/test/.eslintrc DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "rules": {
3
- "@typescript-eslint/explicit-function-return-type": 0,
4
- "@typescript-eslint/explicit-member-accessibility": 0,
5
- "@typescript-eslint/no-unused-vars": 2,
6
- "no-console": 0
7
- }
8
- }