@abraca/dabra 2.0.4 → 2.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/dabra",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "abracadabra provider",
5
5
  "keywords": [
6
6
  "abracadabra",
@@ -142,6 +142,16 @@ export class AbracadabraBaseProvider extends EventEmitter {
142
142
 
143
143
  isAuthenticated = false;
144
144
 
145
+ /**
146
+ * True once this provider has received at least one Authenticated frame
147
+ * on the current socket lifetime. Used by `permissionDeniedHandler` to
148
+ * tell apart "the entry doc is wrong / unreachable" (first frame is a
149
+ * denial → config error, give up) from "a mid-session write was denied"
150
+ * (server keeps the read subscription alive → don't kill the socket).
151
+ * Reset on close.
152
+ */
153
+ private _hasEverAuthenticated = false;
154
+
145
155
  /** Current WebSocket connection status. */
146
156
  get connectionStatus(): WebSocketStatus {
147
157
  return this.configuration.websocketProvider.status;
@@ -504,6 +514,10 @@ export class AbracadabraBaseProvider extends EventEmitter {
504
514
  onClose() {
505
515
  this.isAuthenticated = false;
506
516
  this.synced = false;
517
+ // Reset per-socket-lifetime auth flag so the next connection's
518
+ // permissionDeniedHandler can correctly identify a fresh-connection
519
+ // denial as a config error.
520
+ this._hasEverAuthenticated = false;
507
521
 
508
522
  // update awareness (all users except local left)
509
523
  if (this.awareness) {
@@ -616,15 +630,26 @@ export class AbracadabraBaseProvider extends EventEmitter {
616
630
  // closes after every server-side rejection, AbracadabraWS schedules a
617
631
  // retry, the new connection sends the same Subscribe → server denies
618
632
  // again, ad infinitum (CPU/log/network spam, page looks "broken" to
619
- // the user). The dominant cases:
633
+ // the user). The dominant cases here:
620
634
  // * "document not found" — entry-doc-id pinned to something that
621
635
  // doesn't exist on this server
622
- // * "permission denied" — operator never granted the user access
623
- // Both are config issues; retrying won't fix them. We only stop the
624
- // socket when this provider OWNS it (`manageSocket`); shared sockets
625
- // (e.g. multiplexed child providers) leave the parent's lifecycle
626
- // alone and just give up on this doc.
627
- if (this.manageSocket) {
636
+ // * initial "permission denied" — operator never granted access
637
+ // Both are config issues; retrying won't fix them.
638
+ //
639
+ // BUT once we've successfully authenticated at least once on this
640
+ // socket, a permission_denied frame means a *mid-session* operation
641
+ // was rejected (e.g. a write attempt on a doc the user only has read
642
+ // on, or a Forbidden surfaced from defense-in-depth). The server
643
+ // keeps the read subscription alive in that case (see ws.rs handling
644
+ // of Error::Forbidden), so killing the socket would be a regression:
645
+ // it tears down every other doc's sync just because one write was
646
+ // denied. Skip the disconnect here and let the affected operation
647
+ // surface its own error.
648
+ //
649
+ // We only stop the socket when this provider OWNS it (`manageSocket`);
650
+ // shared sockets (e.g. multiplexed child providers) leave the parent's
651
+ // lifecycle alone and just give up on this doc.
652
+ if (this.manageSocket && !this._hasEverAuthenticated) {
628
653
  try {
629
654
  this.configuration.websocketProvider.disconnect();
630
655
  }
@@ -634,6 +659,7 @@ export class AbracadabraBaseProvider extends EventEmitter {
634
659
 
635
660
  authenticatedHandler(scope: string) {
636
661
  this.isAuthenticated = true;
662
+ this._hasEverAuthenticated = true;
637
663
  this.authorizedScope = scope as AuthorizedScope;
638
664
 
639
665
  this.emit("authenticated", { scope });