sadpandajoe commented on code in PR #45404:
URL: https://github.com/apache/superset/pull/45404#discussion_r4410071145


##########
superset-frontend/src/middleware/realtime.ts:
##########
@@ -267,16 +301,132 @@ const teardownSocket = (): void => {
   }
 };
 
+interface Ticket {
+  token: string;
+  expiresInMs: number;
+}
+
+/** Fetch a fresh connection ticket for the current session. */
+const fetchTicket = async (): Promise<Ticket> => {
+  const { json } = await SupersetClient.get({ endpoint: TICKET_ENDPOINT });

Review Comment:
   For an anonymous visitor on a public dashboard (`PUBLIC_ROLE_LIKE` set with 
`WEBSOCKET_ENABLE` on), the bootstrap still enables realtime, so this ticket 
request returns 401. Since it doesn't pass `ignoreUnauthorized: true`, 
`SupersetClient` runs its unauthorized handler and navigates to `/login` before 
`isUnauthorizedError` here ever sees the rejection, so opening the dashboard 
bounces the visitor to the login page instead of just leaving realtime off. 
Should this request pass `ignoreUnauthorized: true`, or skip the ticket fetch 
when there is no signed-in user?



##########
superset-websocket/src/index.ts:
##########
@@ -715,72 +738,243 @@ export const readSocketIdentity = (
   };
 };
 
+/** An inbound `auth` frame: `{type: 'auth', token, tab_id?}`. */
+interface AuthFrame {
+  token: string;
+  tabId?: string;
+}
+
 /**
- * WebSocket `connection` event handler. `identity` is the one already verified
- * during the HTTP upgrade (see `httpUpgrade`), so each connection's JWT is
- * verified exactly once.
+ * Parse a raw inbound frame into an `auth` frame, or `null` when it is 
anything
+ * else (malformed JSON, another message type, or a missing token). An invalid 
or
+ * absent `tab_id` is dropped rather than failing the frame, so the socket 
falls
+ * back to principal-grain delivery.
  */
-export const wsConnection = (
-  ws: WebSocket,
-  identity: SocketIdentity,
-  tabId?: string,
-) => {
-  const { channel } = identity;
-
-  // Refuse the connection if a configured connection limit has been reached,
-  // before tracking it against the internal registries. The cap is keyed on 
the
-  // principal channel (which counts all of a principal's tabs); the per-tab
-  // channel is a delivery refinement and is not separately limit-checked.
-  const limitReason = connectionLimitReason(channel);
-  if (limitReason) {
-    statsd.increment('ws_connection_rejected');
-    logger.warn(`Refusing connection on channel ${channel}: ${limitReason}`);
-    ws.close(CONNECTION_LIMIT_CLOSE_CODE, limitReason);
-    return;
+const parseAuthFrame = (raw: WebSocket.RawData): AuthFrame | null => {
+  let parsed: unknown;
+  try {
+    parsed = JSON.parse(raw.toString());
+  } catch {
+    return null;
   }
+  if (!parsed || typeof parsed !== 'object') return null;
+  const {
+    type,
+    token,
+    tab_id: tabId,
+  } = parsed as {
+    type?: unknown;
+    token?: unknown;
+    tab_id?: unknown;
+  };
+  if (type !== 'auth' || typeof token !== 'string' || token.length === 0) {
+    return null;
+  }
+  return {
+    token,
+    tabId: typeof tabId === 'string' && isValidTabId(tabId) ? tabId : 
undefined,
+  };
+};
 
-  // When the browser advertised a tab id at connect, also register the socket
-  // under a per-tab channel derived from its authorized principal channel
-  // (`<channel>:<tabId>`). The tab id is client-supplied but always prefixed 
by
-  // the authorized principal channel, so it can never address another
-  // principal's sockets. Task-status can then target one tab, while the
-  // principal channel still reaches all of a principal's tabs.
-  const tabChannel = tabId ? `${channel}:${tabId}` : undefined;
-
-  const socketInstance: SocketInstance = {
-    ws,
-    channel,
-    tabChannel,
-    identity,
-    pongTs: Date.now(),
+const sendAuthOk = (ws: WebSocket, identity: SocketIdentity): void => {
+  const message: OutboundMessage = {
+    topic: AUTH_REPLY_TOPIC,
+    payload: {
+      status: 'ok',
+      expires_in: Math.max(
+        0,
+        Math.floor((identity.tokenExpiresAtMs - Date.now()) / 1000),
+      ),
+    },
   };
+  ws.send(JSON.stringify(message));

Review Comment:
   This `ws.send` skips the `maxSocketBufferBytes` check that `sendToSocket` 
applies. An authenticated client can replay its still-valid `auth` frame over 
and over without reading replies, and every one queues another ack in the 
socket's outbound buffer with no cap or termination when no Pub/Sub traffic is 
flowing. Should the ack go through the bounded send path (or close the socket 
when `bufferedAmount` is over the limit) like other outbound messages?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to