@verdaccio/auth 7.0.0-next.4 → 7.0.0-next.6

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 (38) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +1 -1
  3. package/build/auth.d.ts +7 -26
  4. package/build/auth.js +38 -23
  5. package/build/auth.js.map +1 -1
  6. package/build/index.d.ts +1 -0
  7. package/build/index.js +12 -0
  8. package/build/index.js.map +1 -1
  9. package/build/signature-legacy.d.ts +5 -0
  10. package/build/signature-legacy.js +60 -0
  11. package/build/signature-legacy.js.map +1 -0
  12. package/build/signature.d.ts +4 -0
  13. package/build/signature.js +60 -0
  14. package/build/signature.js.map +1 -0
  15. package/build/types.d.ts +34 -0
  16. package/build/types.js +13 -0
  17. package/build/types.js.map +1 -0
  18. package/build/utils.d.ts +5 -15
  19. package/build/utils.js +18 -7
  20. package/build/utils.js.map +1 -1
  21. package/package.json +12 -10
  22. package/src/auth.ts +62 -50
  23. package/src/index.ts +1 -0
  24. package/src/signature-legacy.ts +66 -0
  25. package/src/signature.ts +66 -0
  26. package/src/types.ts +46 -0
  27. package/src/utils.ts +37 -31
  28. package/test/auth.spec.ts +561 -40
  29. package/test/helper/plugin.ts +8 -0
  30. package/test/partials/plugin/verdaccio-access-ok/access.js +9 -0
  31. package/test/partials/plugin/verdaccio-access-ok/package.json +5 -0
  32. package/test/partials/plugin/verdaccio-adduser/adduser.js +25 -0
  33. package/test/partials/plugin/verdaccio-adduser/package.json +5 -0
  34. package/test/partials/plugin/verdaccio-adduser-legacy/adduser.js +25 -0
  35. package/test/partials/plugin/verdaccio-adduser-legacy/package.json +5 -0
  36. package/test/partials/plugin/verdaccio-change-password/change.js +32 -0
  37. package/test/partials/plugin/verdaccio-change-password/package.json +5 -0
  38. package/test/partials/plugin/verdaccio-fail/fail.js +3 -0
@@ -0,0 +1,66 @@
1
+ import buildDebug from 'debug';
2
+ import _ from 'lodash';
3
+
4
+ import { TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/core';
5
+ import { aesDecrypt, parseBasicPayload } from '@verdaccio/signature';
6
+ import { Security } from '@verdaccio/types';
7
+
8
+ import { AuthMiddlewarePayload } from './types';
9
+ import {
10
+ convertPayloadToBase64,
11
+ isAESLegacy,
12
+ parseAuthTokenHeader,
13
+ verifyJWTPayload,
14
+ } from './utils';
15
+
16
+ const debug = buildDebug('verdaccio:auth:utils');
17
+
18
+ export function parseAESCredentials(authorizationHeader: string, secret: string) {
19
+ debug('parseAESCredentials');
20
+ const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
21
+
22
+ // basic is deprecated and should not be enforced
23
+ // basic is currently being used for functional test
24
+ if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {
25
+ debug('legacy header basic');
26
+ const credentials = convertPayloadToBase64(token).toString();
27
+
28
+ return credentials;
29
+ } else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
30
+ debug('legacy header bearer');
31
+ const credentials = aesDecrypt(token, secret);
32
+
33
+ return credentials;
34
+ }
35
+ }
36
+
37
+ export function getMiddlewareCredentials(
38
+ security: Security,
39
+ secretKey: string,
40
+ authorizationHeader: string
41
+ ): AuthMiddlewarePayload {
42
+ debug('getMiddlewareCredentials');
43
+ // comment out for debugging purposes
44
+ if (isAESLegacy(security)) {
45
+ debug('is legacy');
46
+ const credentials = parseAESCredentials(authorizationHeader, secretKey);
47
+ if (!credentials) {
48
+ debug('parse legacy credentials failed');
49
+ return;
50
+ }
51
+
52
+ const parsedCredentials = parseBasicPayload(credentials);
53
+ if (!parsedCredentials) {
54
+ debug('parse legacy basic payload credentials failed');
55
+ return;
56
+ }
57
+
58
+ return parsedCredentials;
59
+ }
60
+ const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
61
+
62
+ debug('is jwt');
63
+ if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
64
+ return verifyJWTPayload(token, secretKey);
65
+ }
66
+ }
package/src/types.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+
3
+ import { VerdaccioError } from '@verdaccio/core';
4
+ import { AuthPackageAllow, JWTSignOptions, Logger, RemoteUser } from '@verdaccio/types';
5
+
6
+ export interface AESPayload {
7
+ user: string;
8
+ password: string;
9
+ }
10
+
11
+ export type BasicPayload = AESPayload | void;
12
+ export type AuthMiddlewarePayload = RemoteUser | BasicPayload;
13
+
14
+ export interface AuthTokenHeader {
15
+ scheme: string;
16
+ token: string;
17
+ }
18
+ export type AllowActionCallbackResponse = boolean | undefined;
19
+ export type AllowActionCallback = (
20
+ error: VerdaccioError | null,
21
+ allowed?: AllowActionCallbackResponse
22
+ ) => void;
23
+
24
+ export type AllowAction = (
25
+ user: RemoteUser,
26
+ pkg: AuthPackageAllow,
27
+ callback: AllowActionCallback
28
+ ) => void;
29
+
30
+ export interface TokenEncryption {
31
+ jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string>;
32
+ aesEncrypt(buf: string): string | void;
33
+ }
34
+
35
+ export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
36
+
37
+ // remove
38
+ export interface IAuthMiddleware {
39
+ apiJWTmiddleware(): $NextFunctionVer;
40
+ webUIJWTmiddleware(): $NextFunctionVer;
41
+ }
42
+
43
+ export type $RequestExtend = Request & { remote_user?: any; log: Logger };
44
+ export type $ResponseExtend = Response & { cookies?: any };
45
+ export type $NextFunctionVer = NextFunction & any;
46
+ export { NextFunction };
package/src/utils.ts CHANGED
@@ -7,36 +7,28 @@ import {
7
7
  HTTP_STATUS,
8
8
  TOKEN_BASIC,
9
9
  TOKEN_BEARER,
10
- VerdaccioError,
11
10
  errorUtils,
12
11
  pluginUtils,
13
12
  } from '@verdaccio/core';
14
- import { aesDecrypt, parseBasicPayload, verifyPayload } from '@verdaccio/signature';
13
+ import {
14
+ aesDecrypt,
15
+ aesDecryptDeprecated,
16
+ parseBasicPayload,
17
+ verifyPayload,
18
+ } from '@verdaccio/signature';
15
19
  import { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';
16
20
 
17
- import { AESPayload, TokenEncryption } from './auth';
21
+ import {
22
+ ActionsAllowed,
23
+ AllowAction,
24
+ AllowActionCallback,
25
+ AuthMiddlewarePayload,
26
+ AuthTokenHeader,
27
+ TokenEncryption,
28
+ } from './types';
18
29
 
19
30
  const debug = buildDebug('verdaccio:auth:utils');
20
31
 
21
- export type BasicPayload = AESPayload | void;
22
- export type AuthMiddlewarePayload = RemoteUser | BasicPayload;
23
-
24
- export interface AuthTokenHeader {
25
- scheme: string;
26
- token: string;
27
- }
28
- export type AllowActionCallbackResponse = boolean | undefined;
29
- export type AllowActionCallback = (
30
- error: VerdaccioError | null,
31
- allowed?: AllowActionCallbackResponse
32
- ) => void;
33
-
34
- export type AllowAction = (
35
- user: RemoteUser,
36
- pkg: AuthPackageAllow,
37
- callback: AllowActionCallback
38
- ) => void;
39
-
40
32
  /**
41
33
  * Split authentication header eg: Bearer [secret_token]
42
34
  * @param authorizationHeader auth token
@@ -48,7 +40,11 @@ export function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHead
48
40
  return { scheme, token };
49
41
  }
50
42
 
51
- export function parseAESCredentials(authorizationHeader: string, secret: string) {
43
+ export function parseAESCredentials(
44
+ authorizationHeader: string,
45
+ secret: string,
46
+ enhanced: boolean
47
+ ) {
52
48
  debug('parseAESCredentials');
53
49
  const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
54
50
 
@@ -61,7 +57,11 @@ export function parseAESCredentials(authorizationHeader: string, secret: string)
61
57
  return credentials;
62
58
  } else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
63
59
  debug('legacy header bearer');
64
- const credentials = aesDecrypt(token, secret);
60
+ debug('legacy header enhanced?', enhanced);
61
+ const credentials = enhanced
62
+ ? aesDecrypt(token.toString(), secret)
63
+ : // FUTURE: once deprecated legacy is removed this logic won't be longer need it
64
+ aesDecryptDeprecated(convertPayloadToBase64(token), secret).toString('utf-8');
65
65
 
66
66
  return credentials;
67
67
  }
@@ -70,13 +70,14 @@ export function parseAESCredentials(authorizationHeader: string, secret: string)
70
70
  export function getMiddlewareCredentials(
71
71
  security: Security,
72
72
  secretKey: string,
73
- authorizationHeader: string
73
+ authorizationHeader: string,
74
+ enhanced: boolean = true
74
75
  ): AuthMiddlewarePayload {
75
76
  debug('getMiddlewareCredentials');
76
77
  // comment out for debugging purposes
77
78
  if (isAESLegacy(security)) {
78
79
  debug('is legacy');
79
- const credentials = parseAESCredentials(authorizationHeader, secretKey);
80
+ const credentials = parseAESCredentials(authorizationHeader, secretKey, enhanced);
80
81
  if (!credentials) {
81
82
  debug('parse legacy credentials failed');
82
83
  return;
@@ -161,14 +162,15 @@ export function isAuthHeaderValid(authorization: string): boolean {
161
162
  export function getDefaultPlugins(logger: Logger): pluginUtils.Auth<Config> {
162
163
  return {
163
164
  authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {
165
+ debug('triggered default authenticate method');
164
166
  cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
165
167
  },
166
168
 
167
169
  adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {
170
+ debug('triggered default adduser method');
168
171
  return cb(errorUtils.getConflict(API_ERROR.BAD_USERNAME_PASSWORD));
169
172
  },
170
173
 
171
- // FIXME: allow_action and allow_publish should be in the @verdaccio/types
172
174
  // @ts-ignore
173
175
  allow_access: allow_action('access', logger),
174
176
  // @ts-ignore
@@ -177,8 +179,6 @@ export function getDefaultPlugins(logger: Logger): pluginUtils.Auth<Config> {
177
179
  };
178
180
  }
179
181
 
180
- export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
181
-
182
182
  export function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {
183
183
  return function allowActionCallback(
184
184
  user: RemoteUser,
@@ -187,8 +187,13 @@ export function allow_action(action: ActionsAllowed, logger: Logger): AllowActio
187
187
  ): void {
188
188
  logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{remote}`);
189
189
  const { name, groups } = user;
190
+ debug('allow_action "%s": groups %s', action, groups);
190
191
  const groupAccess = pkg[action] as string[];
191
- const hasPermission = groupAccess.some((group) => name === group || groups.includes(group));
192
+ debug('allow_action "%s": groupAccess %s', action, groupAccess);
193
+ const hasPermission = groupAccess.some((group) => {
194
+ return name === group || groups.includes(group);
195
+ });
196
+ debug('package "%s" has permission "%s"', name, hasPermission);
192
197
  logger.trace(
193
198
  { pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },
194
199
  `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{remote}, package: @{pkgName}`
@@ -218,7 +223,8 @@ export function handleSpecialUnpublish(logger: Logger): any {
218
223
  return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
219
224
  const action = 'unpublish';
220
225
  // verify whether the unpublish prop has been defined
221
- const isUnpublishMissing: boolean = _.isNil(pkg[action]);
226
+ const isUnpublishMissing: boolean = !pkg[action];
227
+ debug('is unpublish method missing ? %s', isUnpublishMissing);
222
228
  const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;
223
229
  logger.trace(
224
230
  { user: user.name, name: pkg.name, hasGroups },