Skip to content

Commit 72854b5

Browse files
committed
Auth and redis improvements
1 parent d41cf1e commit 72854b5

File tree

10 files changed

+63
-32
lines changed

10 files changed

+63
-32
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8-
## Version 1.5.2 - 2025-02-xx
8+
## Version 1.5.2 - 2025-01-10
99

1010
### Fixed
1111

12-
- tbd
12+
- Fix fallback to redis configuration `cds.requires.redis`
13+
- Check for authenticated user during websocket connection (no anonymous)
14+
- Improve start log `using websocket` including adapter information
15+
- Improve server shutdown redis client handling
1316

1417
## Version 1.5.1 - 2025-01-09
1518

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ To use the Redis Adapter (basic publish/subscribe), the following steps have to
15581558
{
15591559
"cds": {
15601560
"requires": {
1561-
"redis-websocket": null,
1561+
"redis-websocket": false,
15621562
"redis": {
15631563
"vcap": {
15641564
"tag": "ws-redis"

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
coverageThreshold: {
1313
global: {
1414
branches: 80,
15-
functions: 95,
15+
functions: 90,
1616
lines: 90,
1717
statements: 90,
1818
},

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const SocketServer = require("./socket/base");
66
const redis = require("./redis");
77

88
const LOG = cds.log("/websocket");
9+
const TIMEOUT_SHUTDOWN = 2500;
910

1011
const WebSocketAction = {
1112
Connect: "wsConnect",
@@ -24,7 +25,7 @@ const collectServicesAndMountAdapter = (srv, options) => {
2425
serveWebSocketServer(options);
2526
});
2627
cds.on("shutdown", async () => {
27-
await redis.closeClients();
28+
await shutdownWebSocketServer();
2829
});
2930
}
3031
services[srv.name] = srv;
@@ -85,7 +86,10 @@ function serveWebSocketServer(options) {
8586
serveWebSocketService(socketServer, eventService, options);
8687
}
8788
}
88-
LOG?.info("using websocket", { kind: cds.env.websocket.kind, adapter: socketServer.adapterActive });
89+
LOG?.info("using websocket", {
90+
kind: cds.env.websocket.kind,
91+
adapter: socketServer.adapter ? { impl: socketServer.adapterImpl, active: socketServer.adapterActive } : false,
92+
});
8993
}
9094
});
9195
}
@@ -108,6 +112,26 @@ async function initWebSocketServer(server, path) {
108112
}
109113
}
110114

115+
async function shutdownWebSocketServer() {
116+
return await new Promise((resolve, reject) => {
117+
const timeoutRef = setTimeout(() => {
118+
clearTimeout(timeoutRef);
119+
LOG?.info("Shutdown timeout reached!");
120+
resolve();
121+
}, TIMEOUT_SHUTDOWN);
122+
redis
123+
.closeClients()
124+
.then((result) => {
125+
clearTimeout(timeoutRef);
126+
resolve(result);
127+
})
128+
.catch((err) => {
129+
clearTimeout(timeoutRef);
130+
reject(err);
131+
});
132+
});
133+
}
134+
111135
function normalizeServicePath(servicePath, protocolPath) {
112136
if (servicePath.startsWith(`${protocolPath}/`)) {
113137
return servicePath.substring(`${protocolPath}/`.length);
@@ -447,7 +471,7 @@ function deriveCurrentUser(event, data, headers, req) {
447471
}
448472
}
449473

450-
function deriveDefinedUser(event, data, headers, req) {
474+
function deriveDefinedUser(event, data, headers) {
451475
const include = combineValues(
452476
deriveValues(event, data, headers, {
453477
headerNames: ["wsUsers", "wsUser", "users", "user"],

src/redis/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ const createClientBase = (options = {}) => {
4747
const adapterActiveExplicit = !!options?.active;
4848
const adapterLocal = !!options?.local;
4949
if (!(IS_ON_CF || adapterActiveExplicit || adapterLocal)) {
50-
LOG?.info("Redis not available in local environment");
50+
LOG?.info("Redis is not activated for local environment");
5151
return;
5252
}
53-
const requiresRedis = cds.env.requires?.["redis-websocket"] ?? cds.env.requires?.redis;
53+
const requiresRedis = cds.env.requires?.["redis-websocket"] || cds.env.requires?.redis;
5454
const credentials = requiresRedis?.credentials;
5555
if (!credentials) {
5656
LOG?.info("No Redis credentials found");

src/socket/base.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class SocketServer {
2222
this.path = path;
2323
this.config = config;
2424
this.adapter = null;
25+
this.adapterImpl = null;
2526
this.adapterActive = false;
2627
cds.ws = null;
2728
}
@@ -199,16 +200,6 @@ class SocketServer {
199200
*/
200201
close(socket, code, reason) {}
201202

202-
/**
203-
* Enforce that socket request is authenticated
204-
* @param {Object} socket Server socket
205-
*/
206-
enforceAuth(socket) {
207-
if (socket.request.isAuthenticated && !socket.request.isAuthenticated()) {
208-
throw new Error("403 - Forbidden");
209-
}
210-
}
211-
212203
/**
213204
* Middlewares executed before
214205
* @returns {[function]} Returns a list of middleware functions
@@ -222,7 +213,7 @@ class SocketServer {
222213
* @returns {[function]} Returns a list of middleware functions
223214
*/
224215
afterMiddlewares() {
225-
return [this.removeWrapNext.bind(this)];
216+
return [this.enforceAuth.bind(this), this.removeWrapNext.bind(this)];
226217
}
227218

228219
/**
@@ -372,6 +363,19 @@ class SocketServer {
372363
}
373364
}
374365

366+
/**
367+
* Enforce that socket request is authenticated (no anonymous)
368+
* @param {Object} socket Server socket
369+
*/
370+
enforceAuth(socket, next) {
371+
if (cds.context?.user?._is_anonymous || (socket.request.isAuthenticated && !socket.request.isAuthenticated())) {
372+
const err = new Error("401");
373+
err.code = 4401;
374+
return next(err);
375+
}
376+
next();
377+
}
378+
375379
/**
376380
* Require implementation
377381
* @param {String} impl Implementation name or path

src/socket/socket.io.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class SocketIOServer extends SocketServer {
3030
const format = this.format(service, undefined, "json");
3131
io.on("connection", async (socket) => {
3232
try {
33-
this.enforceAuth(socket);
3433
socket.context = { ...cds.context };
3534
socket.join(room({ tenant: socket.context.tenant }));
3635
if (socket.context.user?.id) {
@@ -282,6 +281,7 @@ class SocketIOServer extends SocketServer {
282281
}
283282
if (this.adapter) {
284283
this.io.adapter(this.adapter);
284+
this.adapterImpl = config.impl;
285285
this.adapterActive = true;
286286
}
287287
}

src/socket/ws.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ class SocketWSServer extends SocketServer {
6767
return;
6868
}
6969
try {
70-
this.enforceAuth(ws);
7170
ws.context = { ...cds.context };
7271
ws.facade = {
7372
service,
@@ -275,6 +274,7 @@ class SocketWSServer extends SocketServer {
275274
if (adapterFactory) {
276275
this.adapter = new adapterFactory(this, config);
277276
await this.adapter?.setup?.();
277+
this.adapterImpl = config.impl;
278278
this.adapterActive = !!this.adapter?.client;
279279
}
280280
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2+
"COOKIE_BACKWARD_COMPATIBILITY": true,
23
"destinations": [
34
{
45
"name": "srv",
56
"url": "http://localhost:4004",
67
"forwardAuthToken": true
78
}
8-
],
9-
"COOKIE_BACKWARD_COMPATIBILITY": true
9+
]
1010
}

0 commit comments

Comments
 (0)