@hapi/nes 14.0.0 → 14.0.2

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/LICENSE.md CHANGED
File without changes
package/README.md CHANGED
File without changes
package/lib/client.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import type { ClientRequestArgs } from "http";
2
+ import type { ClientOptions } from "ws";
3
+
1
4
  // Same as exported type in @hapi/hapi v20
2
5
  type HTTP_METHODS = 'ACL' | 'BIND' | 'CHECKOUT' | 'CONNECT' | 'COPY' | 'DELETE' | 'GET' | 'HEAD' | 'LINK' | 'LOCK' |
3
6
  'M-SEARCH' | 'MERGE' | 'MKACTIVITY' | 'MKCALENDAR' | 'MKCOL' | 'MOVE' | 'NOTIFY' | 'OPTIONS' | 'PATCH' | 'POST' |
@@ -173,7 +176,7 @@ export class Client {
173
176
  constructor(
174
177
  url: `ws://${string}` | `wss://${string}`,
175
178
  options?: {
176
- ws?: string | string[];
179
+ ws?: ClientOptions | ClientRequestArgs;
177
180
  timeout?: number | boolean;
178
181
  });
179
182
 
@@ -369,4 +372,4 @@ export class Client {
369
372
  * `client.connect()`
370
373
  */
371
374
  reauthenticate(auth: ClientConnectOptions['auth']): Promise<unknown>;
372
- }
375
+ }
package/lib/client.js CHANGED
File without changes
package/lib/index.js CHANGED
File without changes
package/lib/listener.js CHANGED
@@ -441,26 +441,37 @@ internals.Sockets.prototype.add = function (socket) {
441
441
  };
442
442
 
443
443
 
444
- internals.Sockets.prototype.auth = function (socket) {
444
+ internals.Sockets.prototype.auth = function (socket, previous, credentials) {
445
+
446
+ // Called before the socket credentials are replaced
447
+
448
+ if (!this._listener._settings.auth.index ||
449
+ !this._items[socket.id]) { // Closed while authenticating
445
450
 
446
- if (!this._listener._settings.auth.index) {
447
451
  return;
448
452
  }
449
453
 
450
- if (!socket.auth.credentials.user) {
451
- return;
454
+ const user = credentials.user;
455
+ if (user === previous) {
456
+ return; // Same user as before, index unchanged
452
457
  }
453
458
 
454
- const user = socket.auth.credentials.user;
455
- if (this._listener._settings.auth.maxConnectionsPerUser &&
459
+ if (user &&
460
+ this._listener._settings.auth.maxConnectionsPerUser &&
456
461
  this._byUser[user] &&
457
462
  this._byUser[user].length >= this._listener._settings.auth.maxConnectionsPerUser) {
458
463
 
459
464
  throw Boom.serverUnavailable('Too many connections for the authenticated user');
460
465
  }
461
466
 
462
- this._byUser[user] = this._byUser[user] ?? [];
463
- this._byUser[user].push(socket);
467
+ if (previous) {
468
+ this._deindex(socket, previous);
469
+ }
470
+
471
+ if (user) {
472
+ this._byUser[user] = this._byUser[user] ?? [];
473
+ this._byUser[user].push(socket);
474
+ }
464
475
  };
465
476
 
466
477
 
@@ -471,12 +482,17 @@ internals.Sockets.prototype.remove = function (socket) {
471
482
  if (socket.auth.credentials &&
472
483
  socket.auth.credentials.user) {
473
484
 
474
- const user = socket.auth.credentials.user;
475
- if (this._byUser[user]) {
476
- this._byUser[user] = this._byUser[user].filter((item) => item !== socket);
477
- if (!this._byUser[user].length) {
478
- delete this._byUser[user];
479
- }
485
+ this._deindex(socket, socket.auth.credentials.user);
486
+ }
487
+ };
488
+
489
+
490
+ internals.Sockets.prototype._deindex = function (socket, user) {
491
+
492
+ if (this._byUser[user]) {
493
+ this._byUser[user] = this._byUser[user].filter((item) => item !== socket);
494
+ if (!this._byUser[user].length) {
495
+ delete this._byUser[user];
480
496
  }
481
497
  }
482
498
  };
@@ -582,15 +598,52 @@ internals.Subscribers.prototype._remove = async function (socket, path, params)
582
598
  };
583
599
 
584
600
 
601
+ internals.Subscribers.prototype.reauth = function (socket, previous) {
602
+
603
+ // Called after the socket credentials are replaced
604
+
605
+ if (!this._settings.auth ||
606
+ !this._settings.auth.index) {
607
+
608
+ return;
609
+ }
610
+
611
+ const user = socket.auth.credentials.user;
612
+ if (user === previous) {
613
+ return;
614
+ }
615
+
616
+ const item = this._items[socket.id];
617
+ if (!item) {
618
+ return; // Removed while the socket was closing
619
+ }
620
+
621
+ if (previous) {
622
+ this._deindex(item, previous);
623
+ }
624
+
625
+ if (user) {
626
+ this._byUser[user] = this._byUser[user] ?? [];
627
+ this._byUser[user].push(item);
628
+ }
629
+ };
630
+
631
+
585
632
  internals.Subscribers.prototype._cleanup = function (socket, item) {
586
633
 
587
634
  delete this._items[socket.id];
588
635
 
589
636
  if (socket.auth.credentials &&
590
- socket.auth.credentials.user &&
591
- this._byUser[socket.auth.credentials.user]) {
637
+ socket.auth.credentials.user) {
638
+
639
+ this._deindex(item, socket.auth.credentials.user);
640
+ }
641
+ };
642
+
643
+
644
+ internals.Subscribers.prototype._deindex = function (item, user) {
592
645
 
593
- const user = socket.auth.credentials.user;
646
+ if (this._byUser[user]) {
594
647
  this._byUser[user] = this._byUser[user].filter((record) => record !== item);
595
648
  if (!this._byUser[user].length) {
596
649
  delete this._byUser[user];
package/lib/socket.js CHANGED
@@ -237,7 +237,7 @@ internals.Socket.prototype._onMessage = async function (message) {
237
237
  try {
238
238
  var request = JSON.parse(message);
239
239
  }
240
- catch (err) {
240
+ catch {
241
241
  return this._error(Boom.badRequest('Cannot parse message'));
242
242
  }
243
243
 
@@ -563,7 +563,7 @@ internals.Socket.prototype._authenticate = async function (request) {
563
563
  const auth = await Iron.unseal(request.auth, config.password, config.iron ?? Iron.defaults);
564
564
  this._setCredentials(auth);
565
565
  }
566
- catch (err) {
566
+ catch {
567
567
  throw Boom.unauthorized('Invalid token');
568
568
  }
569
569
  }
@@ -585,7 +585,7 @@ internals.Socket.prototype._authByCookie = async function () {
585
585
  try {
586
586
  var { states } = await this.server.states.parse(cookies);
587
587
  }
588
- catch (err) {
588
+ catch {
589
589
  throw Boom.unauthorized('Invalid nes authentication cookie');
590
590
  }
591
591
 
@@ -598,6 +598,9 @@ internals.Socket.prototype._authByCookie = async function () {
598
598
 
599
599
  internals.Socket.prototype._setCredentials = function (auth) {
600
600
 
601
+ const previous = this.auth.credentials ? this.auth.credentials.user : undefined;
602
+ this._listener._sockets.auth(this, previous, auth.credentials); // Throws before any state changes when the user limit is reached
603
+
601
604
  this.auth.isAuthenticated = true;
602
605
  this.auth.credentials = auth.credentials;
603
606
  this.auth.artifacts = auth.artifacts;
@@ -605,7 +608,9 @@ internals.Socket.prototype._setCredentials = function (auth) {
605
608
 
606
609
  this.auth._verified = Date.now();
607
610
 
608
- this._listener._sockets.auth(this);
611
+ for (const subscribers of new Set(Object.values(this._subscriptions))) { // Paths on the same route share one subscribers object
612
+ subscribers.reauth(this, previous);
613
+ }
609
614
  };
610
615
 
611
616
 
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@hapi/nes",
3
3
  "description": "WebSocket adapter plugin for hapi routes",
4
- "version": "14.0.0",
5
- "repository": "git://github.com/hapijs/nes",
4
+ "version": "14.0.2",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git://github.com/hapijs/nes"
8
+ },
6
9
  "main": "lib/index.js",
7
10
  "types": "lib/index.d.ts",
8
11
  "engines": {
@@ -16,34 +19,30 @@
16
19
  "plugin",
17
20
  "websocket"
18
21
  ],
19
- "eslintConfig": {
20
- "extends": [
21
- "plugin:@hapi/module"
22
- ]
23
- },
24
22
  "dependencies": {
25
23
  "@hapi/boom": "^10.0.1",
26
- "@hapi/bounce": "^3.0.1",
24
+ "@hapi/bounce": "^3.0.2",
27
25
  "@hapi/call": "^9.0.1",
28
26
  "@hapi/cryptiles": "^6.0.1",
29
- "@hapi/hoek": "^11.0.2",
27
+ "@hapi/hoek": "^11.0.7",
30
28
  "@hapi/iron": "^7.0.1",
31
29
  "@hapi/teamwork": "^6.0.0",
32
30
  "@hapi/validate": "^2.0.1",
33
- "ws": "^8.17.1"
31
+ "@types/ws": "^8.5.13",
32
+ "ws": "^8.18.0"
34
33
  },
35
34
  "devDependencies": {
36
35
  "@hapi/code": "^9.0.3",
37
- "@hapi/eslint-plugin": "*",
38
- "@hapi/hapi": "^21.3.9",
39
- "@hapi/lab": "^25.2.0",
40
- "@types/node": "^20.14.2",
36
+ "@hapi/eslint-plugin": "^7.0.0",
37
+ "@hapi/hapi": "^21.3.12",
38
+ "@hapi/lab": "^26.0.0",
39
+ "@types/node": "^20.17.12",
41
40
  "joi": "^17.13.3",
42
41
  "typescript": "^5.4.5"
43
42
  },
43
+ "license": "BSD-3-Clause",
44
44
  "scripts": {
45
45
  "test": "lab -a @hapi/code -t 100 -L -m 10000 -Y",
46
46
  "test-cov-html": "lab -a @hapi/code -r html -o coverage.html -m 10000"
47
- },
48
- "license": "BSD-3-Clause"
49
- }
47
+ }
48
+ }