refactor(streams): decide binary stream auth on isDummy - #2940
Conversation
|
Note on scope: this targets Only |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR consolidates security interfaces in ChangesSecurity strategy and WebSocket authentication
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant WebSocketClient
participant StreamApplication
participant SecurityStrategy
WebSocketClient->>StreamApplication: Open WebSocket connection
StreamApplication->>SecurityStrategy: authorizeWS(connection)
SecurityStrategy-->>StreamApplication: Authorization result
StreamApplication-->>WebSocketClient: Allow connection or return HTTP 401
WebSocketClient->>StreamApplication: Submit login request
StreamApplication->>SecurityStrategy: Check supportsLogin and call login
SecurityStrategy-->>StreamApplication: LoginResponse or HTTP 501
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/api/streams/index.ts`:
- Around line 123-126: Update the debug call in the
app.securityStrategy.isDummy() branch to avoid eagerly interpolating streamId
when the debug namespace is disabled. Guard the dynamic message construction
with the debug-enabled check, and use a static message plus the stream
identifier when debug logging is not enabled.
In `@src/interfaces/ws.ts`:
- Around line 811-819: Update the login dispatcher to route every
parsedMsg.login request to processLoginRequest, regardless of supportsLogin().
In processLoginRequest, check supportsLogin() before rate limiting or invoking
login, and preserve the existing completed 501 response for unsupported login
requests.
In `@src/security.ts`:
- Around line 289-294: Update the shouldAllowWrite declaration to replace the
any delta parameter with the concrete WsMessage type used by the WebSocket path
and TokenSecurity context/updates access. Remove the JSDoc text explaining the
untyped binary-stream request string, while preserving the existing request
parameter contract.
In `@src/tokensecurity.ts`:
- Around line 549-550: Replace the broad unknown-to-SKRequest cast in
hasAdminAccess and shouldAllowWrite with a local intersection type combining
WSConnection and Pick<SKRequest, 'skPrincipal' | 'skIsAuthenticated'>. Apply
this in src/tokensecurity.ts at lines 549-550 and 1263-1267, preserving access
to only the authentication fields both functions read.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 632e6524-3b43-4e19-bd3a-6c55c9a3da2a
📒 Files selected for processing (4)
src/api/streams/index.tssrc/interfaces/ws.tssrc/security.tssrc/tokensecurity.ts
e698d9d to
b2f9a2d
Compare
|
@coderabbitai Two applied, one partially, one rejected. Guard the dynamic debug message — applied, and to the pre-existing one next to it as well. Both now use the lazy Use Type the
Narrowing Dispatch unsupported login to the 501 — still no, same as on #2937. Verified: 0 type errors, build and |
|
Tip For best results, initiate chat on the files or code changes.
The lazy debug formatting and I agree that the I withdraw the unsupported-login concern. Signal K WebSocket connections intentionally open before authentication. The existing silent behavior is therefore part of the intended flow. 🐇 ✏️ Learnings added
You are interacting with an AI system. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
ready for human review |
b2f9a2d to
d0e25c6
Compare
The websocket, binary stream and token security modules each declared their own view of SecurityStrategy, so the interface a caller saw depended on which file it was in and none of them agreed on what a strategy provides. The websocket copy also typed the principal without permissions, which made admin checks impossible to express there. Declare the websocket authentication methods on SecurityStrategy itself and export SkPrincipal, WSConnection and LoginResponse, so the local copies become aliases or disappear. hasAdminAccess stays optional: it is unavailable under dummy security, so callers must still gate on isDummy() rather than a false return.
The upgrade handler treated "shouldAllowWrite is a function" as the test for whether security is enabled, and "authorizeWS exists" as the test for whether it can authenticate. Both are true for every strategy in the tree, so neither fallback ran: the branch that allowed unauthenticated connections was unreachable, and so was the one passing the string 'streams' to shouldAllowWrite, which expects a delta. Ask isDummy() instead, which is what the first branch meant, and call authorizeWS directly. Behaviour is unchanged except under dummy security, where a connection is now labelled 'unauthenticated' rather than 'unknown' - the identifier is recorded but never used to authorize.
d0e25c6 to
7320b58
Compare
Follow-up to #2937, from a CodeRabbit finding there about
shouldAllowWrite(request, 'streams').What problem does this solve?
The binary stream upgrade handler used the presence of methods as a proxy for two different questions, and got both wrong:
shouldAllowWriteandauthorizeWSare both defined by every strategy in the tree —tokensecurityassigns them, anddummysecurityhas a no-opauthorizeWSand ashouldAllowWritereturningtrue. So both fallbacks are unreachable:elselabelled "Security is disabled, allow connection without authentication" never runs, becauseshouldAllowWriteis a function under dummy security too.elsepassing the string'streams'never runs either. That one would have been a crash if it did:shouldAllowWriteimmediately readsdelta.contextanddelta.updates.find().Traced against the built output rather than by reading:
So today a security-disabled server takes the authentication path, calls a no-op, finds no principal, and falls back to
'unknown'— arriving at roughly the right outcome through entirely the wrong route.What this changes
Ask
isDummy(), which is what the first branch was trying to ask, and callauthorizeWSdirectly. Both dead branches go.Behaviour is unchanged except under dummy security, where the connection is now labelled
'unauthenticated'instead of'unknown'.StreamPrincipal.identifieris recorded on the client and never consulted for an authorization decision, and'unauthenticated'is the more accurate of the two.Depends on #2937
authorizeWSis optional on master's localWebSocketSecurityStrategy, so calling it unguarded does not compile there. #2937 removes that local interface and makesauthorizeWSnon-optional on the shared contract. This PR is based on that branch and should merge after it.Tested
test/binary-stream-auth.ts— 5 passing, covering the paths that matter here: unauthenticated rejected with 401, invalid token rejected with 401, and valid tokens accepted via cookie, query parameter andAuthorizationheader.Full run:
npm run test-only1171 passing, 0 failing. 0 type errors, build andci-lintclean.Summary
SecurityStrategycontract.SkPrincipal,WSConnection, andLoginResponseinterfaces.isDummy()and directauthorizeWScalls.unauthenticated.