@flink-app/streaming-plugin 2.0.0-alpha.113 → 2.0.0-alpha.115
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/CHANGELOG.md +14 -0
- package/dist/StreamingPlugin.d.ts.map +1 -1
- package/dist/StreamingPlugin.js +49 -10
- package/dist/StreamingPlugin.js.map +1 -1
- package/package.json +3 -3
- package/spec/StreamingPlugin.spec.ts +94 -2
- package/src/StreamingPlugin.ts +42 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @flink-app/streaming-plugin
|
|
2
2
|
|
|
3
|
+
## 2.0.0-alpha.115
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @flink-app/flink@2.0.0-alpha.115
|
|
8
|
+
|
|
9
|
+
## 2.0.0-alpha.114
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 383c91b: Fix auth rejections being treated as authenticated. `genericRequestPlugin` routes, `streamingPlugin` routes and the s3-plugin legacy upload route checked the auth result with `if (!authenticated)`. A `FlinkResponse` — the shape an auth plugin returns to replace the default 401, e.g. from jwt-auth-plugin's `onAuthFailure` — is a truthy object, so those routes let the rejected request through to the handler. They now use `isAuthSuccess()` and send the plugin's response verbatim (its `headers` applied to the response and stripped from the JSON body, status defaulting to 401), falling back to each route's existing default response when the plugin simply returned `false`. Headers from a successful authentication are applied too — on streaming routes before the stream headers are flushed.
|
|
14
|
+
- Updated dependencies [5f11719]
|
|
15
|
+
- @flink-app/flink@2.0.0-alpha.114
|
|
16
|
+
|
|
3
17
|
## 2.0.0-alpha.113
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamingPlugin.d.ts","sourceRoot":"","sources":["../src/StreamingPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"StreamingPlugin.d.ts","sourceRoot":"","sources":["../src/StreamingPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,QAAQ,EACR,YAAY,EACZ,WAAW,EAKd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAgB,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAgB,MAAM,SAAS,CAAC;AAqEtI;;;GAGG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACxC,EAAE,SAAe;IACxB,OAAO,CAAC,GAAG,CAAC,CAAgB;IAC5B,OAAO,CAAC,OAAO,CAAmC;gBAEtC,OAAO,GAAE,sBAA2B;IAO1C,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC7C;;;;;;;;;;OAUG;IACI,qBAAqB,CAAC,GAAG,SAAS,YAAY,EAAE,CAAC,GAAG,GAAG,EAAE,aAAa,EAAE,mBAAmB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI;IAEjH;;;;;;;;;;;;;OAaG;IACI,qBAAqB,CAAC,GAAG,SAAS,YAAY,EAAE,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,mBAAmB,GAAG,IAAI;CA8JzI;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAEjF"}
|
package/dist/StreamingPlugin.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
14
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
15
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -35,6 +46,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
46
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
47
|
}
|
|
37
48
|
};
|
|
49
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
50
|
+
var t = {};
|
|
51
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
52
|
+
t[p] = s[p];
|
|
53
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
54
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
55
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
56
|
+
t[p[i]] = s[p[i]];
|
|
57
|
+
}
|
|
58
|
+
return t;
|
|
59
|
+
};
|
|
38
60
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
61
|
exports.StreamingPlugin = void 0;
|
|
40
62
|
exports.streamingPlugin = streamingPlugin;
|
|
@@ -170,7 +192,7 @@ var StreamingPlugin = /** @class */ (function () {
|
|
|
170
192
|
var methodAndRoute = "".concat(method.toUpperCase(), " ").concat(props.path);
|
|
171
193
|
// Register Express route
|
|
172
194
|
this.app.expressApp[method](props.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
173
|
-
var
|
|
195
|
+
var authResult, headers, body, status, authHeaders, err_1, streamWriter, err_2;
|
|
174
196
|
return __generator(this, function (_a) {
|
|
175
197
|
switch (_a.label) {
|
|
176
198
|
case 0:
|
|
@@ -191,17 +213,34 @@ var StreamingPlugin = /** @class */ (function () {
|
|
|
191
213
|
_a.trys.push([1, 3, , 4]);
|
|
192
214
|
return [4 /*yield*/, this.app.auth.authenticateRequest(req, props.permissions, this.app.ctx)];
|
|
193
215
|
case 2:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
216
|
+
authResult = _a.sent();
|
|
217
|
+
// Only `true` and a FlinkAuthSuccess mean authenticated. A FlinkResponse is a
|
|
218
|
+
// rejection the plugin shaped itself and is sent verbatim (its `headers` are
|
|
219
|
+
// applied to the response and stripped from the JSON body, status defaults to 401).
|
|
220
|
+
if (!(0, flink_1.isAuthSuccess)(authResult)) {
|
|
221
|
+
if (authResult && typeof authResult === "object") {
|
|
222
|
+
headers = authResult.headers, body = __rest(authResult, ["headers"]);
|
|
223
|
+
status = authResult.status || 401;
|
|
224
|
+
if (headers) {
|
|
225
|
+
res.set(headers);
|
|
226
|
+
}
|
|
227
|
+
res.status(status).json(__assign(__assign({}, body), { status: status }));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
res.status(401).json({
|
|
231
|
+
status: 401,
|
|
232
|
+
error: {
|
|
233
|
+
title: "Unauthorized",
|
|
234
|
+
detail: "Authentication required",
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
203
238
|
return [2 /*return*/];
|
|
204
239
|
}
|
|
240
|
+
authHeaders = (0, flink_1.getAuthSuccessHeaders)(authResult);
|
|
241
|
+
if (authHeaders) {
|
|
242
|
+
res.set(authHeaders);
|
|
243
|
+
}
|
|
205
244
|
return [3 /*break*/, 4];
|
|
206
245
|
case 3:
|
|
207
246
|
err_1 = _a.sent();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamingPlugin.js","sourceRoot":"","sources":["../src/StreamingPlugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StreamingPlugin.js","sourceRoot":"","sources":["../src/StreamingPlugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/streaming-plugin",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.115",
|
|
4
4
|
"description": "Streaming response support for Flink Framework (SSE and NDJSON)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
},
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@flink-app/flink": "2.0.0-alpha.
|
|
22
|
+
"@flink-app/flink": "2.0.0-alpha.115"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/jasmine": "^3.7.1",
|
|
26
26
|
"@types/node": "^20.10.0",
|
|
27
27
|
"ts-node": "^10.0.0",
|
|
28
|
-
"@flink-app/flink": "2.0.0-alpha.
|
|
28
|
+
"@flink-app/flink": "2.0.0-alpha.115"
|
|
29
29
|
},
|
|
30
30
|
"gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
|
|
31
31
|
"scripts": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlinkApp, FlinkContext, FlinkAuthPlugin, FlinkRequest } from "@flink-app/flink";
|
|
1
|
+
import { FlinkApp, FlinkContext, FlinkAuthPlugin, FlinkAuthResult, FlinkRequest } from "@flink-app/flink";
|
|
2
2
|
import { EventEmitter } from "events";
|
|
3
3
|
import { streamingPlugin, StreamHandler, StreamingRouteProps } from "../src";
|
|
4
4
|
|
|
@@ -11,7 +11,14 @@ interface TestContext extends FlinkContext {
|
|
|
11
11
|
class MockAuthPlugin implements FlinkAuthPlugin {
|
|
12
12
|
public shouldAuthenticate: boolean = true;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** When set, returned verbatim - used to exercise the full auth result union */
|
|
15
|
+
public resultOverride?: FlinkAuthResult;
|
|
16
|
+
|
|
17
|
+
async authenticateRequest(req: FlinkRequest, permissions: string | string[]): Promise<FlinkAuthResult> {
|
|
18
|
+
if (this.resultOverride !== undefined) {
|
|
19
|
+
return this.resultOverride;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
// Simulate authentication check
|
|
16
23
|
const token = req.headers?.authorization;
|
|
17
24
|
|
|
@@ -46,6 +53,11 @@ class MockResponse extends EventEmitter {
|
|
|
46
53
|
this.headers[key] = value;
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
set(headers: { [key: string]: string }) {
|
|
57
|
+
Object.assign(this.headers, headers);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
status(code: number) {
|
|
50
62
|
this.statusCode = code;
|
|
51
63
|
return this;
|
|
@@ -411,6 +423,86 @@ describe("StreamingPlugin", () => {
|
|
|
411
423
|
}, 100);
|
|
412
424
|
});
|
|
413
425
|
|
|
426
|
+
it("should reject with the FlinkResponse the auth plugin returned", (done) => {
|
|
427
|
+
mockAuth.resultOverride = {
|
|
428
|
+
status: 403,
|
|
429
|
+
error: { id: "1", code: "PERMISSION_DENIED", title: "Permission denied" },
|
|
430
|
+
headers: { "set-cookie": "jwt=deleted; path=/;" },
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const route: StreamingRouteProps = {
|
|
434
|
+
path: "/test-auth-shaped-rejection",
|
|
435
|
+
format: "sse",
|
|
436
|
+
skipAutoRegister: true,
|
|
437
|
+
permissions: ["admin"],
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const handler: StreamHandler<TestContext> = async ({ stream }) => {
|
|
441
|
+
stream.write({ message: "Should not see this" });
|
|
442
|
+
stream.end();
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
pluginWithAuth.registerStreamHandler(handler, route);
|
|
446
|
+
|
|
447
|
+
const req = new MockRequest();
|
|
448
|
+
req.headers.authorization = "Bearer valid-token";
|
|
449
|
+
const res = new MockResponse();
|
|
450
|
+
|
|
451
|
+
appWithAuth.expressApp!._router.stack.forEach((layer: any) => {
|
|
452
|
+
if (layer.route?.path === "/test-auth-shaped-rejection") {
|
|
453
|
+
layer.route.stack[0].handle(req, res, () => {});
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
setTimeout(() => {
|
|
458
|
+
// A FlinkResponse is a rejection, not an authenticated result
|
|
459
|
+
expect(res.statusCode).toBe(403);
|
|
460
|
+
expect(res.jsonData[0].error.code).toBe("PERMISSION_DENIED");
|
|
461
|
+
expect(res.headers["set-cookie"]).toBe("jwt=deleted; path=/;");
|
|
462
|
+
// headers are transport level and must not leak into the JSON body
|
|
463
|
+
expect(res.jsonData[0].headers).toBeUndefined();
|
|
464
|
+
expect(res.data.length).toBe(0);
|
|
465
|
+
done();
|
|
466
|
+
}, 100);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("should stream and apply headers when the auth plugin returns an authenticated result", (done) => {
|
|
470
|
+
mockAuth.resultOverride = { authenticated: true, headers: { "set-cookie": "jwt=renewed; path=/;" } };
|
|
471
|
+
|
|
472
|
+
const route: StreamingRouteProps = {
|
|
473
|
+
path: "/test-auth-success-headers",
|
|
474
|
+
format: "sse",
|
|
475
|
+
skipAutoRegister: true,
|
|
476
|
+
permissions: ["admin"],
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const handler: StreamHandler<TestContext> = async ({ stream }) => {
|
|
480
|
+
stream.write({ message: "Hello" });
|
|
481
|
+
stream.end();
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
pluginWithAuth.registerStreamHandler(handler, route);
|
|
485
|
+
|
|
486
|
+
const req = new MockRequest();
|
|
487
|
+
req.headers.authorization = "Bearer valid-token";
|
|
488
|
+
const res = new MockResponse();
|
|
489
|
+
|
|
490
|
+
appWithAuth.expressApp!._router.stack.forEach((layer: any) => {
|
|
491
|
+
if (layer.route?.path === "/test-auth-success-headers") {
|
|
492
|
+
layer.route.stack[0].handle(req, res, () => {});
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
setTimeout(() => {
|
|
497
|
+
expect(res.statusCode).not.toBe(401);
|
|
498
|
+
// set before the stream headers are flushed
|
|
499
|
+
expect(res.headers["set-cookie"]).toBe("jwt=renewed; path=/;");
|
|
500
|
+
expect(res.data.length).toBe(1);
|
|
501
|
+
expect(res.ended).toBe(true);
|
|
502
|
+
done();
|
|
503
|
+
}, 100);
|
|
504
|
+
});
|
|
505
|
+
|
|
414
506
|
it("should allow streaming without permissions when no auth is required", (done) => {
|
|
415
507
|
const route: StreamingRouteProps = {
|
|
416
508
|
path: "/test-no-auth-required",
|
package/src/StreamingPlugin.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ExpressResponse,
|
|
3
|
+
FlinkApp,
|
|
4
|
+
FlinkContext,
|
|
5
|
+
FlinkPlugin,
|
|
6
|
+
getAuthSuccessHeaders,
|
|
7
|
+
HttpMethod,
|
|
8
|
+
isAuthSuccess,
|
|
9
|
+
log,
|
|
10
|
+
} from "@flink-app/flink";
|
|
2
11
|
import { StreamFormat, StreamHandler, StreamHandlerModule, StreamingPluginOptions, StreamingRouteProps, StreamWriter } from "./types";
|
|
3
12
|
|
|
4
13
|
/**
|
|
@@ -205,17 +214,40 @@ Streaming handlers will auto-register like regular HTTP handlers.
|
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
try {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
+
const authResult = await this.app!.auth.authenticateRequest(req as any, props.permissions, this.app!.ctx);
|
|
218
|
+
|
|
219
|
+
// Only `true` and a FlinkAuthSuccess mean authenticated. A FlinkResponse is a
|
|
220
|
+
// rejection the plugin shaped itself and is sent verbatim (its `headers` are
|
|
221
|
+
// applied to the response and stripped from the JSON body, status defaults to 401).
|
|
222
|
+
if (!isAuthSuccess(authResult)) {
|
|
223
|
+
if (authResult && typeof authResult === "object") {
|
|
224
|
+
const { headers, ...body } = authResult;
|
|
225
|
+
const status = authResult.status || 401;
|
|
226
|
+
|
|
227
|
+
if (headers) {
|
|
228
|
+
res.set(headers);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
res.status(status).json({ ...body, status });
|
|
232
|
+
} else {
|
|
233
|
+
res.status(401).json({
|
|
234
|
+
status: 401,
|
|
235
|
+
error: {
|
|
236
|
+
title: "Unauthorized",
|
|
237
|
+
detail: "Authentication required",
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
217
241
|
return;
|
|
218
242
|
}
|
|
243
|
+
|
|
244
|
+
// Headers contributed by a successful authentication (e.g. a renewed session
|
|
245
|
+
// cookie) must be set before the stream headers are flushed.
|
|
246
|
+
const authHeaders = getAuthSuccessHeaders(authResult);
|
|
247
|
+
|
|
248
|
+
if (authHeaders) {
|
|
249
|
+
res.set(authHeaders);
|
|
250
|
+
}
|
|
219
251
|
} catch (err) {
|
|
220
252
|
log.error(`[StreamingPlugin] ${methodAndRoute} authentication error:`, err);
|
|
221
253
|
res.status(401).json({
|