@conduitplatform/module-tools 0.17.0-alpha.8 → 0.17.0-alpha.9
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/dist/index.cjs +598 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +185 -4
- package/dist/index.d.ts +185 -4
- package/dist/index.esm.js +559 -83
- package/dist/index.esm.js.map +1 -1
- package/package.json +4 -4
package/dist/index.esm.js
CHANGED
|
@@ -402,9 +402,9 @@ var GrpcServer = class {
|
|
|
402
402
|
import path2 from "path";
|
|
403
403
|
import { EventEmitter } from "events";
|
|
404
404
|
import { camelCase } from "lodash-es";
|
|
405
|
-
import { status as
|
|
405
|
+
import { status as status5 } from "@grpc/grpc-js";
|
|
406
406
|
import {
|
|
407
|
-
ConduitGrpcSdk as
|
|
407
|
+
ConduitGrpcSdk as ConduitGrpcSdk6,
|
|
408
408
|
HealthCheckResponse_ServingStatus,
|
|
409
409
|
HealthCheckStatus
|
|
410
410
|
} from "@conduitplatform/grpc-sdk";
|
|
@@ -487,6 +487,16 @@ var RouteBuilder = class {
|
|
|
487
487
|
this._options.captureRawHeaders = capture;
|
|
488
488
|
return this;
|
|
489
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Whether module-tools hydrates `context.user` from `context.userId` on routes with
|
|
492
|
+
* `authMiddleware` / `authMiddleware?` (default true).
|
|
493
|
+
* @deprecated The default flips to false in the next major; call
|
|
494
|
+
* `grpcSdk.authentication.getUser(userId)` explicitly instead.
|
|
495
|
+
*/
|
|
496
|
+
hydrateUser(hydrate) {
|
|
497
|
+
this._options.hydrateUser = hydrate;
|
|
498
|
+
return this;
|
|
499
|
+
}
|
|
490
500
|
return(name, fields) {
|
|
491
501
|
this._returns = new ConduitRouteReturnDefinition(name, fields);
|
|
492
502
|
return this;
|
|
@@ -520,45 +530,105 @@ var RouteBuilder = class {
|
|
|
520
530
|
|
|
521
531
|
// src/routing/wrapRouterFunctions.ts
|
|
522
532
|
import {
|
|
523
|
-
ConduitGrpcSdk as
|
|
533
|
+
ConduitGrpcSdk as ConduitGrpcSdk4
|
|
524
534
|
} from "@conduitplatform/grpc-sdk";
|
|
535
|
+
import { status as status3 } from "@grpc/grpc-js";
|
|
536
|
+
|
|
537
|
+
// src/routing/hydrateUser.ts
|
|
538
|
+
import { ConduitGrpcSdk as ConduitGrpcSdk3, GrpcError as GrpcError2 } from "@conduitplatform/grpc-sdk";
|
|
525
539
|
import { status as status2 } from "@grpc/grpc-js";
|
|
540
|
+
function middlewareList(routeOptions) {
|
|
541
|
+
const m = routeOptions?.middlewares;
|
|
542
|
+
if (Array.isArray(m)) return m.filter((x) => typeof x === "string");
|
|
543
|
+
if (typeof m === "string") return [m];
|
|
544
|
+
return [];
|
|
545
|
+
}
|
|
546
|
+
function getAuthMiddlewareKind(routeOptions) {
|
|
547
|
+
const list = middlewareList(routeOptions);
|
|
548
|
+
if (list.includes("authMiddleware")) return "required";
|
|
549
|
+
if (list.includes("authMiddleware?")) return "optional";
|
|
550
|
+
return void 0;
|
|
551
|
+
}
|
|
552
|
+
function shouldHydrateUser(routeOptions, context) {
|
|
553
|
+
if (!routeOptions || !context) return false;
|
|
554
|
+
if (routeOptions.hydrateUser === false) return false;
|
|
555
|
+
if (!getAuthMiddlewareKind(routeOptions)) return false;
|
|
556
|
+
return typeof context.userId === "string" && !!context.userId && !context.user;
|
|
557
|
+
}
|
|
558
|
+
function createUserHydrator(routeOptions, resolve) {
|
|
559
|
+
if (!routeOptions) return void 0;
|
|
560
|
+
const kind = getAuthMiddlewareKind(routeOptions);
|
|
561
|
+
if (!kind || routeOptions.hydrateUser === false) return void 0;
|
|
562
|
+
return {
|
|
563
|
+
optional: kind === "optional",
|
|
564
|
+
getUser: async (userId) => {
|
|
565
|
+
const resolver = resolve();
|
|
566
|
+
if (!resolver) throw new GrpcError2(status2.UNAVAILABLE, "User lookup unavailable");
|
|
567
|
+
return resolver(userId);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function mapHydrationErrorCode(error) {
|
|
572
|
+
const code = error?.code;
|
|
573
|
+
if (typeof code !== "number" || code === status2.OK) return status2.INTERNAL;
|
|
574
|
+
if (code === status2.NOT_FOUND) return status2.UNAUTHENTICATED;
|
|
575
|
+
return code;
|
|
576
|
+
}
|
|
577
|
+
async function hydrateContextUser(routeOptions, context, hydrator) {
|
|
578
|
+
if (!shouldHydrateUser(routeOptions, context)) return;
|
|
579
|
+
try {
|
|
580
|
+
context.user = await hydrator.getUser(context.userId);
|
|
581
|
+
} catch (e) {
|
|
582
|
+
ConduitGrpcSdk3.Logger.warn(
|
|
583
|
+
`User hydration failed: ${e?.message ?? String(e)}`
|
|
584
|
+
);
|
|
585
|
+
if (!hydrator.optional) {
|
|
586
|
+
const code = mapHydrationErrorCode(e);
|
|
587
|
+
throw new GrpcError2(
|
|
588
|
+
code,
|
|
589
|
+
code === status2.UNAUTHENTICATED ? "Unauthorized" : "User lookup failed"
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/routing/wrapRouterFunctions.ts
|
|
526
596
|
var mapGrpcStatusToHttp = (code) => {
|
|
527
597
|
if (!code) return "200 (OK)";
|
|
528
598
|
switch (code) {
|
|
529
|
-
case
|
|
599
|
+
case status3.OK:
|
|
530
600
|
return `200 (OK)`;
|
|
531
|
-
case
|
|
601
|
+
case status3.CANCELLED:
|
|
532
602
|
return `499 (Cancelled)`;
|
|
533
|
-
case
|
|
603
|
+
case status3.INVALID_ARGUMENT:
|
|
534
604
|
return `400 (Invalid Argument)`;
|
|
535
|
-
case
|
|
605
|
+
case status3.DEADLINE_EXCEEDED:
|
|
536
606
|
return `504 (Deadline Exceeded)`;
|
|
537
|
-
case
|
|
607
|
+
case status3.NOT_FOUND:
|
|
538
608
|
return `404 (Not Found)`;
|
|
539
|
-
case
|
|
609
|
+
case status3.ALREADY_EXISTS:
|
|
540
610
|
return `409 (Already Exists)`;
|
|
541
|
-
case
|
|
611
|
+
case status3.PERMISSION_DENIED:
|
|
542
612
|
return `403 (Permission Denied)`;
|
|
543
|
-
case
|
|
613
|
+
case status3.UNAUTHENTICATED:
|
|
544
614
|
return `401 (Unauthenticated)`;
|
|
545
|
-
case
|
|
615
|
+
case status3.OUT_OF_RANGE:
|
|
546
616
|
return `416 (Out of Range)`;
|
|
547
|
-
case
|
|
617
|
+
case status3.FAILED_PRECONDITION:
|
|
548
618
|
return `412 (Failed Precondition)`;
|
|
549
|
-
case
|
|
619
|
+
case status3.ABORTED:
|
|
550
620
|
return `409 (Aborted)`;
|
|
551
|
-
case
|
|
621
|
+
case status3.INTERNAL:
|
|
552
622
|
return `416 (Internal)`;
|
|
553
|
-
case
|
|
623
|
+
case status3.UNAVAILABLE:
|
|
554
624
|
return `503 (Service Unavailable)`;
|
|
555
|
-
case
|
|
625
|
+
case status3.DATA_LOSS:
|
|
556
626
|
return `500 (Data Loss)`;
|
|
557
627
|
default:
|
|
558
628
|
return `500 (Unknown)`;
|
|
559
629
|
}
|
|
560
630
|
};
|
|
561
|
-
function generateLog(routerType, routerRequest, requestReceive, call,
|
|
631
|
+
function generateLog(routerType, routerRequest, requestReceive, call, status10) {
|
|
562
632
|
let log = "";
|
|
563
633
|
const latency = Date.now() - requestReceive;
|
|
564
634
|
if (routerRequest) {
|
|
@@ -568,15 +638,15 @@ function generateLog(routerType, routerRequest, requestReceive, call, status6) {
|
|
|
568
638
|
const socketLabel = socketReq.socketId ?? socketReq.socket;
|
|
569
639
|
log += `(${routerType}) Socket: ${socketReq.event} socket: ${socketLabel}`;
|
|
570
640
|
}
|
|
571
|
-
log += ` ${mapGrpcStatusToHttp(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
641
|
+
log += ` ${mapGrpcStatusToHttp(status10) ?? "200"} ${latency}ms`;
|
|
642
|
+
ConduitGrpcSdk4.Logger.log(log);
|
|
643
|
+
ConduitGrpcSdk4.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
|
|
644
|
+
ConduitGrpcSdk4.Metrics?.set(
|
|
575
645
|
`${routerType}_grpc_request_latency_seconds`,
|
|
576
646
|
latency / 1e3
|
|
577
647
|
);
|
|
578
|
-
const successStatus = !
|
|
579
|
-
|
|
648
|
+
const successStatus = !status10 || status10.toString().startsWith("2");
|
|
649
|
+
ConduitGrpcSdk4.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
|
|
580
650
|
success: successStatus ? "true" : "false"
|
|
581
651
|
});
|
|
582
652
|
}
|
|
@@ -592,7 +662,7 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
|
|
|
592
662
|
responded.did = true;
|
|
593
663
|
if (!r) {
|
|
594
664
|
callback({
|
|
595
|
-
code:
|
|
665
|
+
code: status3.INTERNAL,
|
|
596
666
|
message: "Handler did not return a response"
|
|
597
667
|
});
|
|
598
668
|
}
|
|
@@ -623,11 +693,11 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
|
|
|
623
693
|
}
|
|
624
694
|
generateLog(routerType, routerRequest, requestReceive, call, void 0);
|
|
625
695
|
}
|
|
626
|
-
function wrapRouterGrpcFunction(fun, routerType) {
|
|
696
|
+
function wrapRouterGrpcFunction(fun, routerType, hydration) {
|
|
627
697
|
return (call, callback) => {
|
|
628
698
|
const requestReceive = Date.now();
|
|
629
699
|
let routerRequest = true;
|
|
630
|
-
|
|
700
|
+
ConduitGrpcSdk4.Metrics?.increment(`${routerType}_grpc_requests_total`);
|
|
631
701
|
try {
|
|
632
702
|
call.request.context = parseRequestData(call.request.context);
|
|
633
703
|
call.request.params = parseRequestData(call.request.params);
|
|
@@ -646,16 +716,16 @@ function wrapRouterGrpcFunction(fun, routerType) {
|
|
|
646
716
|
}
|
|
647
717
|
}
|
|
648
718
|
} catch (e) {
|
|
649
|
-
|
|
650
|
-
generateLog(routerType, routerRequest, requestReceive, call,
|
|
651
|
-
|
|
719
|
+
ConduitGrpcSdk4.Metrics?.increment(`${routerType}_grpc_errors_total`);
|
|
720
|
+
generateLog(routerType, routerRequest, requestReceive, call, status3.INTERNAL);
|
|
721
|
+
ConduitGrpcSdk4.Logger.error(e.message ?? "Something went wrong");
|
|
652
722
|
return callback({
|
|
653
|
-
code:
|
|
723
|
+
code: status3.INTERNAL,
|
|
654
724
|
message: e.message ?? "Something went wrong"
|
|
655
725
|
});
|
|
656
726
|
}
|
|
657
727
|
const responded = { did: false };
|
|
658
|
-
fun(call, (r) => {
|
|
728
|
+
const invoke = () => fun(call, (r) => {
|
|
659
729
|
parseResponseData(
|
|
660
730
|
r,
|
|
661
731
|
routerRequest,
|
|
@@ -665,7 +735,13 @@ function wrapRouterGrpcFunction(fun, routerType) {
|
|
|
665
735
|
responded,
|
|
666
736
|
routerType
|
|
667
737
|
);
|
|
668
|
-
})
|
|
738
|
+
});
|
|
739
|
+
const needsHydration = hydration && shouldHydrateUser(hydration.options, call.request.context);
|
|
740
|
+
(needsHydration ? hydrateContextUser(
|
|
741
|
+
hydration.options,
|
|
742
|
+
call.request.context,
|
|
743
|
+
hydration.hydrator
|
|
744
|
+
).then(invoke) : invoke()).then(
|
|
669
745
|
(r) => parseResponseData(
|
|
670
746
|
r,
|
|
671
747
|
routerRequest,
|
|
@@ -676,19 +752,19 @@ function wrapRouterGrpcFunction(fun, routerType) {
|
|
|
676
752
|
routerType
|
|
677
753
|
)
|
|
678
754
|
).catch((error) => {
|
|
679
|
-
|
|
755
|
+
ConduitGrpcSdk4.Metrics?.increment(`${routerType}_grpc_errors_total`);
|
|
680
756
|
generateLog(
|
|
681
757
|
routerType,
|
|
682
758
|
routerRequest,
|
|
683
759
|
requestReceive,
|
|
684
760
|
call,
|
|
685
|
-
error.code ??
|
|
761
|
+
error.code ?? status3.INTERNAL
|
|
686
762
|
);
|
|
687
|
-
|
|
763
|
+
ConduitGrpcSdk4.Logger.error(
|
|
688
764
|
error.debugLogInfo ?? error.message ?? "Something went wrong"
|
|
689
765
|
);
|
|
690
766
|
callback({
|
|
691
|
-
code: error.code ??
|
|
767
|
+
code: error.code ?? status3.INTERNAL,
|
|
692
768
|
message: error.details ?? error.message ?? "Something went wrong"
|
|
693
769
|
});
|
|
694
770
|
});
|
|
@@ -696,10 +772,14 @@ function wrapRouterGrpcFunction(fun, routerType) {
|
|
|
696
772
|
}
|
|
697
773
|
|
|
698
774
|
// src/routing/RoutingUtilities.ts
|
|
699
|
-
function wrapFunctionsAsync(functions, routerType) {
|
|
775
|
+
function wrapFunctionsAsync(functions, routerType, hydrations = {}) {
|
|
700
776
|
const modifiedFunctions = {};
|
|
701
777
|
Object.keys(functions).forEach((key) => {
|
|
702
|
-
modifiedFunctions[key] = wrapRouterGrpcFunction(
|
|
778
|
+
modifiedFunctions[key] = wrapRouterGrpcFunction(
|
|
779
|
+
functions[key],
|
|
780
|
+
routerType,
|
|
781
|
+
hydrations[key]
|
|
782
|
+
);
|
|
703
783
|
});
|
|
704
784
|
return modifiedFunctions;
|
|
705
785
|
}
|
|
@@ -707,8 +787,11 @@ function wrapFunctionsAsync(functions, routerType) {
|
|
|
707
787
|
// src/routing/RoutingManager.ts
|
|
708
788
|
import {
|
|
709
789
|
Admin,
|
|
710
|
-
ConduitRouteActions as ConduitRouteActions2
|
|
790
|
+
ConduitRouteActions as ConduitRouteActions2,
|
|
791
|
+
ConduitGrpcSdk as ConduitGrpcSdk5,
|
|
792
|
+
GrpcError as GrpcError3
|
|
711
793
|
} from "@conduitplatform/grpc-sdk";
|
|
794
|
+
import { status as status4 } from "@grpc/grpc-js";
|
|
712
795
|
var RoutingManager = class _RoutingManager {
|
|
713
796
|
constructor(_router, _server) {
|
|
714
797
|
this._router = _router;
|
|
@@ -722,6 +805,30 @@ var RoutingManager = class _RoutingManager {
|
|
|
722
805
|
_moduleRoutes = {};
|
|
723
806
|
static ClientController;
|
|
724
807
|
static AdminController;
|
|
808
|
+
/** Set by ManagedModule; used to hydrate context.user on client routes. */
|
|
809
|
+
static grpcSdk;
|
|
810
|
+
static userResolver;
|
|
811
|
+
static warnedNoResolver = false;
|
|
812
|
+
/**
|
|
813
|
+
* Overrides how users are fetched for hydration (default: grpcSdk.authentication.getUser).
|
|
814
|
+
* Intended for the module that owns the users: the authentication module has no gRPC
|
|
815
|
+
* client to itself and will register a local resolver here.
|
|
816
|
+
*/
|
|
817
|
+
static setUserResolver(resolver) {
|
|
818
|
+
_RoutingManager.userResolver = resolver;
|
|
819
|
+
}
|
|
820
|
+
static resolveUser() {
|
|
821
|
+
if (_RoutingManager.userResolver) return _RoutingManager.userResolver;
|
|
822
|
+
const sdk = _RoutingManager.grpcSdk;
|
|
823
|
+
if (!sdk) return void 0;
|
|
824
|
+
return async (userId) => {
|
|
825
|
+
const auth = sdk.authentication;
|
|
826
|
+
if (!auth) {
|
|
827
|
+
throw new GrpcError3(status4.UNAVAILABLE, "authentication module unavailable");
|
|
828
|
+
}
|
|
829
|
+
return auth.getUser(userId);
|
|
830
|
+
};
|
|
831
|
+
}
|
|
725
832
|
_routeHandlers = {};
|
|
726
833
|
isAdmin = false;
|
|
727
834
|
get(path5) {
|
|
@@ -788,7 +895,27 @@ var RoutingManager = class _RoutingManager {
|
|
|
788
895
|
}
|
|
789
896
|
async registerRoutes() {
|
|
790
897
|
if (Object.keys(this._routeHandlers).length === 0) return;
|
|
791
|
-
const
|
|
898
|
+
const hydrations = {};
|
|
899
|
+
if (!this.isAdmin) {
|
|
900
|
+
for (const [name, route] of Object.entries(this._moduleRoutes)) {
|
|
901
|
+
const options = route.options;
|
|
902
|
+
const hydrator = createUserHydrator(options, _RoutingManager.resolveUser);
|
|
903
|
+
if (!hydrator) continue;
|
|
904
|
+
const names = "events" in route ? Object.keys(JSON.parse(route.events || "{}")) : [name];
|
|
905
|
+
for (const n of names) hydrations[n] = { options, hydrator };
|
|
906
|
+
if (!_RoutingManager.grpcSdk && !_RoutingManager.userResolver && !_RoutingManager.warnedNoResolver) {
|
|
907
|
+
_RoutingManager.warnedNoResolver = true;
|
|
908
|
+
ConduitGrpcSdk5.Logger.warn(
|
|
909
|
+
"User hydration is needed but RoutingManager.grpcSdk is not set; hydration will fail"
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const modifiedFunctions = wrapFunctionsAsync(
|
|
915
|
+
this._routeHandlers,
|
|
916
|
+
this.isAdmin ? "admin" : "client",
|
|
917
|
+
hydrations
|
|
918
|
+
);
|
|
792
919
|
if (this.isAdmin) {
|
|
793
920
|
_RoutingManager.AdminController.setRoutes(modifiedFunctions);
|
|
794
921
|
} else {
|
|
@@ -867,7 +994,7 @@ var ConduitServiceModule = class {
|
|
|
867
994
|
);
|
|
868
995
|
}
|
|
869
996
|
if (!init) {
|
|
870
|
-
|
|
997
|
+
ConduitGrpcSdk6.Metrics?.set(
|
|
871
998
|
"module_health_state",
|
|
872
999
|
state === HealthCheckStatus.SERVING ? 1 : 0
|
|
873
1000
|
);
|
|
@@ -910,9 +1037,9 @@ var ConduitServiceModule = class {
|
|
|
910
1037
|
} else {
|
|
911
1038
|
this.events.on(
|
|
912
1039
|
`grpc-health-change:${this._serviceName}`,
|
|
913
|
-
(
|
|
1040
|
+
(status10) => {
|
|
914
1041
|
call.write({
|
|
915
|
-
status:
|
|
1042
|
+
status: status10
|
|
916
1043
|
});
|
|
917
1044
|
}
|
|
918
1045
|
);
|
|
@@ -935,7 +1062,7 @@ var ConduitServiceModule = class {
|
|
|
935
1062
|
const resources = this.getExportableResources();
|
|
936
1063
|
callback(null, { resources: JSON.stringify(resources) });
|
|
937
1064
|
} catch (e) {
|
|
938
|
-
callback({ code:
|
|
1065
|
+
callback({ code: status5.INTERNAL, message: e.message });
|
|
939
1066
|
}
|
|
940
1067
|
}
|
|
941
1068
|
async exportResourcesHandler(call, callback) {
|
|
@@ -944,7 +1071,7 @@ var ConduitServiceModule = class {
|
|
|
944
1071
|
const data = await this.exportResources(resourceTypes);
|
|
945
1072
|
callback(null, { data: JSON.stringify(data) });
|
|
946
1073
|
} catch (e) {
|
|
947
|
-
callback({ code:
|
|
1074
|
+
callback({ code: status5.INTERNAL, message: e.message });
|
|
948
1075
|
}
|
|
949
1076
|
}
|
|
950
1077
|
async importResourcesHandler(call, callback) {
|
|
@@ -956,11 +1083,11 @@ var ConduitServiceModule = class {
|
|
|
956
1083
|
const err = e;
|
|
957
1084
|
if (err instanceof SyntaxError) {
|
|
958
1085
|
callback({
|
|
959
|
-
code:
|
|
1086
|
+
code: status5.INVALID_ARGUMENT,
|
|
960
1087
|
message: "Invalid JSON in import data"
|
|
961
1088
|
});
|
|
962
1089
|
} else {
|
|
963
|
-
callback({ code:
|
|
1090
|
+
callback({ code: status5.INTERNAL, message: err.message });
|
|
964
1091
|
}
|
|
965
1092
|
}
|
|
966
1093
|
}
|
|
@@ -1033,7 +1160,7 @@ var ConfigController = class _ConfigController {
|
|
|
1033
1160
|
|
|
1034
1161
|
// src/ManagedModule.ts
|
|
1035
1162
|
import { kebabCase } from "lodash-es";
|
|
1036
|
-
import { status as
|
|
1163
|
+
import { status as status9 } from "@grpc/grpc-js";
|
|
1037
1164
|
import convict from "convict";
|
|
1038
1165
|
|
|
1039
1166
|
// src/interfaces/ModuleLifecycleStage.ts
|
|
@@ -1048,18 +1175,18 @@ var ModuleLifecycleStage = /* @__PURE__ */ ((ModuleLifecycleStage2) => {
|
|
|
1048
1175
|
|
|
1049
1176
|
// src/ManagedModule.ts
|
|
1050
1177
|
import {
|
|
1051
|
-
ConduitGrpcSdk as
|
|
1178
|
+
ConduitGrpcSdk as ConduitGrpcSdk11
|
|
1052
1179
|
} from "@conduitplatform/grpc-sdk";
|
|
1053
1180
|
|
|
1054
1181
|
// src/utilities/initializeSdk.ts
|
|
1055
1182
|
import {
|
|
1056
|
-
ConduitGrpcSdk as
|
|
1183
|
+
ConduitGrpcSdk as ConduitGrpcSdk9,
|
|
1057
1184
|
HealthCheckStatus as HealthCheckStatus2
|
|
1058
1185
|
} from "@conduitplatform/grpc-sdk";
|
|
1059
1186
|
|
|
1060
1187
|
// src/logging/index.ts
|
|
1061
1188
|
import {
|
|
1062
|
-
ConduitGrpcSdk as
|
|
1189
|
+
ConduitGrpcSdk as ConduitGrpcSdk7
|
|
1063
1190
|
} from "@conduitplatform/grpc-sdk";
|
|
1064
1191
|
import winston, { format } from "winston";
|
|
1065
1192
|
import { isEmpty } from "lodash-es";
|
|
@@ -1217,7 +1344,7 @@ async function setupLoki(module, instance) {
|
|
|
1217
1344
|
if (lokiUrl.endsWith("/")) lokiUrl = lokiUrl.slice(0, -1);
|
|
1218
1345
|
const onTry = async () => {
|
|
1219
1346
|
return await lokiReadyCheck(lokiUrl).then(() => {
|
|
1220
|
-
|
|
1347
|
+
ConduitGrpcSdk7.Logger.addTransport(
|
|
1221
1348
|
new LokiTransport({
|
|
1222
1349
|
level: "debug",
|
|
1223
1350
|
host: lokiUrl,
|
|
@@ -1236,7 +1363,7 @@ async function setupLoki(module, instance) {
|
|
|
1236
1363
|
}).catch(() => true);
|
|
1237
1364
|
};
|
|
1238
1365
|
const onFailure = () => {
|
|
1239
|
-
|
|
1366
|
+
ConduitGrpcSdk7.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
|
|
1240
1367
|
};
|
|
1241
1368
|
await linearBackoffTimeoutAsync(onTry, 250, 15, onFailure, true);
|
|
1242
1369
|
}
|
|
@@ -1334,7 +1461,7 @@ import {
|
|
|
1334
1461
|
// src/metrics/MetricsServer.ts
|
|
1335
1462
|
import { createServer as createServer2 } from "http";
|
|
1336
1463
|
import { isNaN } from "lodash-es";
|
|
1337
|
-
import { ConduitGrpcSdk as
|
|
1464
|
+
import { ConduitGrpcSdk as ConduitGrpcSdk8 } from "@conduitplatform/grpc-sdk";
|
|
1338
1465
|
var MetricsServer = class {
|
|
1339
1466
|
moduleName;
|
|
1340
1467
|
instance;
|
|
@@ -1349,7 +1476,7 @@ var MetricsServer = class {
|
|
|
1349
1476
|
const url = "0.0.0.0:" + port.toString();
|
|
1350
1477
|
const server = createServer2(this.requestHandler.bind(this));
|
|
1351
1478
|
server.listen(port, () => {
|
|
1352
|
-
|
|
1479
|
+
ConduitGrpcSdk8.Logger.info(`Metrics server listening on: ${url}`);
|
|
1353
1480
|
});
|
|
1354
1481
|
return server;
|
|
1355
1482
|
}
|
|
@@ -1371,12 +1498,12 @@ var MetricsServer = class {
|
|
|
1371
1498
|
getHttpPort() {
|
|
1372
1499
|
const value = process.env["METRICS_PORT"];
|
|
1373
1500
|
if (!value) {
|
|
1374
|
-
|
|
1501
|
+
ConduitGrpcSdk8.Logger.error(`Metrics HTTP port not set`);
|
|
1375
1502
|
process.exit(-1);
|
|
1376
1503
|
}
|
|
1377
1504
|
const port = parseInt(value, 10);
|
|
1378
1505
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
1379
|
-
|
|
1506
|
+
ConduitGrpcSdk8.Logger.error(`Invalid HTTP port value: ${port}`);
|
|
1380
1507
|
process.exit(-1);
|
|
1381
1508
|
}
|
|
1382
1509
|
return port;
|
|
@@ -1668,7 +1795,7 @@ function clientMiddleware() {
|
|
|
1668
1795
|
clientStartedMetric.inc(labels);
|
|
1669
1796
|
const stopTimer = clientHandlingSecondsMetric.startTimer(labels);
|
|
1670
1797
|
let settled = false;
|
|
1671
|
-
let
|
|
1798
|
+
let status10 = Status.OK;
|
|
1672
1799
|
try {
|
|
1673
1800
|
let request;
|
|
1674
1801
|
if (!call.requestStream) {
|
|
@@ -1694,21 +1821,21 @@ function clientMiddleware() {
|
|
|
1694
1821
|
} catch (err) {
|
|
1695
1822
|
settled = true;
|
|
1696
1823
|
if (err instanceof ClientError) {
|
|
1697
|
-
|
|
1824
|
+
status10 = err.code;
|
|
1698
1825
|
} else if (isAbortError(err)) {
|
|
1699
|
-
|
|
1826
|
+
status10 = Status.ABORTED;
|
|
1700
1827
|
} else {
|
|
1701
|
-
|
|
1828
|
+
status10 = Status.UNKNOWN;
|
|
1702
1829
|
}
|
|
1703
1830
|
throw err;
|
|
1704
1831
|
} finally {
|
|
1705
1832
|
if (!settled) {
|
|
1706
|
-
|
|
1833
|
+
status10 = Status.CANCELLED;
|
|
1707
1834
|
}
|
|
1708
|
-
stopTimer({ [codeLabel]: Status[
|
|
1835
|
+
stopTimer({ [codeLabel]: Status[status10] });
|
|
1709
1836
|
clientHandledMetric.inc({
|
|
1710
1837
|
...labels,
|
|
1711
|
-
[codeLabel]: Status[
|
|
1838
|
+
[codeLabel]: Status[status10]
|
|
1712
1839
|
});
|
|
1713
1840
|
}
|
|
1714
1841
|
};
|
|
@@ -1723,7 +1850,7 @@ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStat
|
|
|
1723
1850
|
} catch {
|
|
1724
1851
|
logger = new ConduitLogger();
|
|
1725
1852
|
}
|
|
1726
|
-
const grpcSdk = new
|
|
1853
|
+
const grpcSdk = new ConduitGrpcSdk9(
|
|
1727
1854
|
coreUrl,
|
|
1728
1855
|
moduleName,
|
|
1729
1856
|
watchModules,
|
|
@@ -1732,7 +1859,7 @@ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStat
|
|
|
1732
1859
|
);
|
|
1733
1860
|
setupLoki(grpcSdk.name, grpcSdk.instance).then();
|
|
1734
1861
|
if (process.env.METRICS_PORT) {
|
|
1735
|
-
|
|
1862
|
+
ConduitGrpcSdk9.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
|
|
1736
1863
|
grpcSdk.addMiddleware(clientMiddleware());
|
|
1737
1864
|
}
|
|
1738
1865
|
return grpcSdk;
|
|
@@ -1930,8 +2057,346 @@ async function reconcileStoredModuleConfig(args) {
|
|
|
1930
2057
|
return { config: persisted, persisted: true };
|
|
1931
2058
|
}
|
|
1932
2059
|
|
|
2060
|
+
// src/utilities/accessTokenVerifier.ts
|
|
2061
|
+
import { ConduitGrpcSdk as ConduitGrpcSdk10, GrpcError as GrpcError5 } from "@conduitplatform/grpc-sdk";
|
|
2062
|
+
import { status as status7 } from "@grpc/grpc-js";
|
|
2063
|
+
import { createPublicKey } from "crypto";
|
|
2064
|
+
|
|
2065
|
+
// src/utilities/accessTokenVerifier.logic.ts
|
|
2066
|
+
import { createDecoder, createVerifier as createVerifier2 } from "fast-jwt";
|
|
2067
|
+
import { status as status6 } from "@grpc/grpc-js";
|
|
2068
|
+
import { GrpcError as GrpcError4 } from "@conduitplatform/grpc-sdk";
|
|
2069
|
+
var DEFAULT_LEEWAY_SECONDS = 5;
|
|
2070
|
+
var verifiers = /* @__PURE__ */ new Map();
|
|
2071
|
+
var decode = createDecoder({ complete: true });
|
|
2072
|
+
function fail(reason) {
|
|
2073
|
+
throw new GrpcError4(status6.UNAUTHENTICATED, reason);
|
|
2074
|
+
}
|
|
2075
|
+
function getVerifier(publicKey) {
|
|
2076
|
+
let verifier = verifiers.get(publicKey);
|
|
2077
|
+
if (!verifier) {
|
|
2078
|
+
verifier = createVerifier2({
|
|
2079
|
+
key: publicKey,
|
|
2080
|
+
algorithms: ["EdDSA"],
|
|
2081
|
+
ignoreExpiration: true,
|
|
2082
|
+
ignoreNotBefore: true,
|
|
2083
|
+
cache: false
|
|
2084
|
+
});
|
|
2085
|
+
verifiers.set(publicKey, verifier);
|
|
2086
|
+
}
|
|
2087
|
+
return verifier;
|
|
2088
|
+
}
|
|
2089
|
+
function selectVerificationKey(kid, signing, now) {
|
|
2090
|
+
if (!kid) return void 0;
|
|
2091
|
+
if (signing.publicKey && kid === signing.kid) return signing.publicKey;
|
|
2092
|
+
if (signing.previousKid && signing.previousPublicKey && kid === signing.previousKid && (signing.previousRetiredAt ?? 0) > now) {
|
|
2093
|
+
return signing.previousPublicKey;
|
|
2094
|
+
}
|
|
2095
|
+
return void 0;
|
|
2096
|
+
}
|
|
2097
|
+
function verifyAccessToken(token, state, options = {}) {
|
|
2098
|
+
const now = options.now ?? Date.now();
|
|
2099
|
+
const leeway = options.leewaySeconds ?? DEFAULT_LEEWAY_SECONDS;
|
|
2100
|
+
if (typeof token !== "string" || token.length === 0) fail("malformed token");
|
|
2101
|
+
let kid;
|
|
2102
|
+
try {
|
|
2103
|
+
const decoded = decode(token);
|
|
2104
|
+
if (decoded.header.alg !== "EdDSA") fail("unsupported algorithm");
|
|
2105
|
+
kid = decoded.header.kid;
|
|
2106
|
+
} catch (e) {
|
|
2107
|
+
if (e instanceof GrpcError4) throw e;
|
|
2108
|
+
fail("malformed token");
|
|
2109
|
+
}
|
|
2110
|
+
const key = selectVerificationKey(kid, state.signing, now);
|
|
2111
|
+
if (!key) fail("unknown kid");
|
|
2112
|
+
let claims;
|
|
2113
|
+
try {
|
|
2114
|
+
claims = getVerifier(key)(token);
|
|
2115
|
+
} catch {
|
|
2116
|
+
fail("bad signature");
|
|
2117
|
+
}
|
|
2118
|
+
if (typeof claims.sub !== "string" || typeof claims.sid !== "string" || typeof claims.exp !== "number") {
|
|
2119
|
+
fail("malformed token");
|
|
2120
|
+
}
|
|
2121
|
+
if (now / 1e3 >= claims.exp + leeway) fail("expired");
|
|
2122
|
+
if (claims.tv !== state.tokenVersion) fail("version mismatch");
|
|
2123
|
+
if (options.clientId !== void 0 && claims.cid !== options.clientId) {
|
|
2124
|
+
fail("client mismatch");
|
|
2125
|
+
}
|
|
2126
|
+
return claims;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
// src/utilities/accessTokenVerifier.ts
|
|
2130
|
+
var AUTHENTICATION_CONFIG_UPDATE_CHANNEL = "authentication:config:update";
|
|
2131
|
+
var INITIAL_RETRY_MS = 1e3;
|
|
2132
|
+
var MAX_RETRY_MS = 3e4;
|
|
2133
|
+
var REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2134
|
+
var UNKNOWN_KID_REFETCH_MIN_MS = 10 * 1e3;
|
|
2135
|
+
var SUBSCRIBER_ID = "access-token-verifier";
|
|
2136
|
+
function isConfigWithAccessTokens(config) {
|
|
2137
|
+
return typeof config === "object" && config !== null && typeof config.accessTokens === "object" && config.accessTokens !== null;
|
|
2138
|
+
}
|
|
2139
|
+
var AccessTokenVerifier = class _AccessTokenVerifier {
|
|
2140
|
+
constructor(grpcSdk) {
|
|
2141
|
+
this.grpcSdk = grpcSdk;
|
|
2142
|
+
}
|
|
2143
|
+
grpcSdk;
|
|
2144
|
+
static instance;
|
|
2145
|
+
_mode = "session";
|
|
2146
|
+
_strictRevocation = false;
|
|
2147
|
+
_tokenVersion = 0;
|
|
2148
|
+
_signing;
|
|
2149
|
+
_loaded = false;
|
|
2150
|
+
_subscribed = false;
|
|
2151
|
+
_generation = 0;
|
|
2152
|
+
_running = false;
|
|
2153
|
+
_retryTimer;
|
|
2154
|
+
_refreshTimer;
|
|
2155
|
+
_lastRefetch = 0;
|
|
2156
|
+
_refetching = false;
|
|
2157
|
+
_warned = /* @__PURE__ */ new Set();
|
|
2158
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
2159
|
+
static getInstance(grpcSdk) {
|
|
2160
|
+
if (!_AccessTokenVerifier.instance) {
|
|
2161
|
+
_AccessTokenVerifier.instance = new _AccessTokenVerifier(grpcSdk);
|
|
2162
|
+
}
|
|
2163
|
+
return _AccessTokenVerifier.instance;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Internal, for tests only: stops the verifier, clears all state and drops the singleton.
|
|
2167
|
+
*/
|
|
2168
|
+
static resetForTests() {
|
|
2169
|
+
_AccessTokenVerifier.instance?.stop();
|
|
2170
|
+
_AccessTokenVerifier.instance?.resetState();
|
|
2171
|
+
_AccessTokenVerifier.instance = void 0;
|
|
2172
|
+
}
|
|
2173
|
+
resetState() {
|
|
2174
|
+
this._mode = "session";
|
|
2175
|
+
this._strictRevocation = false;
|
|
2176
|
+
this._tokenVersion = 0;
|
|
2177
|
+
this._signing = void 0;
|
|
2178
|
+
this._loaded = false;
|
|
2179
|
+
this._lastRefetch = 0;
|
|
2180
|
+
this._warned.clear();
|
|
2181
|
+
this._listeners.clear();
|
|
2182
|
+
}
|
|
2183
|
+
/** Snapshot of the verifier's observable state. */
|
|
2184
|
+
get state() {
|
|
2185
|
+
return {
|
|
2186
|
+
mode: this._mode,
|
|
2187
|
+
strictRevocation: this._strictRevocation,
|
|
2188
|
+
ready: this.ready,
|
|
2189
|
+
tokenVersion: this._tokenVersion,
|
|
2190
|
+
kid: this._signing?.kid
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Registers a listener fired synchronously at the end of every applyConfig that changed
|
|
2195
|
+
* anything (including the first successful load). Returns an unsubscribe function.
|
|
2196
|
+
*/
|
|
2197
|
+
onChange(listener) {
|
|
2198
|
+
this._listeners.add(listener);
|
|
2199
|
+
return () => {
|
|
2200
|
+
this._listeners.delete(listener);
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
fingerprint() {
|
|
2204
|
+
return JSON.stringify([this._loaded, this._signing, this.state]);
|
|
2205
|
+
}
|
|
2206
|
+
get mode() {
|
|
2207
|
+
return this._mode;
|
|
2208
|
+
}
|
|
2209
|
+
get strictRevocation() {
|
|
2210
|
+
return this._strictRevocation;
|
|
2211
|
+
}
|
|
2212
|
+
/** True once signing keys have been loaded. */
|
|
2213
|
+
get ready() {
|
|
2214
|
+
return !!this._signing?.publicKey;
|
|
2215
|
+
}
|
|
2216
|
+
get tokenVersion() {
|
|
2217
|
+
return this._tokenVersion;
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Loads the authentication config, subscribes to its updates and starts a slow periodic
|
|
2221
|
+
* refresh. The first attempt is awaited; if it fails, retries continue in the background
|
|
2222
|
+
* with capped backoff until the config is loaded and the subscription is acknowledged.
|
|
2223
|
+
* Idempotent while running; never throws.
|
|
2224
|
+
*/
|
|
2225
|
+
async start() {
|
|
2226
|
+
if (this._running) return;
|
|
2227
|
+
this._running = true;
|
|
2228
|
+
const generation = ++this._generation;
|
|
2229
|
+
await this.attempt(generation, INITIAL_RETRY_MS);
|
|
2230
|
+
if (generation !== this._generation) return;
|
|
2231
|
+
this._refreshTimer = setInterval(() => void this.refetch(true), REFRESH_INTERVAL_MS);
|
|
2232
|
+
this._refreshTimer.unref?.();
|
|
2233
|
+
}
|
|
2234
|
+
stop() {
|
|
2235
|
+
this._running = false;
|
|
2236
|
+
this._generation++;
|
|
2237
|
+
if (this._retryTimer) clearTimeout(this._retryTimer);
|
|
2238
|
+
if (this._refreshTimer) clearInterval(this._refreshTimer);
|
|
2239
|
+
this._retryTimer = void 0;
|
|
2240
|
+
this._refreshTimer = void 0;
|
|
2241
|
+
if (this._subscribed) {
|
|
2242
|
+
try {
|
|
2243
|
+
this.grpcSdk.bus?.unsubscribe(SUBSCRIBER_ID);
|
|
2244
|
+
} catch {
|
|
2245
|
+
}
|
|
2246
|
+
this._subscribed = false;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
async attempt(generation, delay) {
|
|
2250
|
+
if (generation !== this._generation) return;
|
|
2251
|
+
try {
|
|
2252
|
+
await this.subscribe(generation);
|
|
2253
|
+
if (!this._loaded) {
|
|
2254
|
+
this.applyConfig(await this.grpcSdk.config.get("authentication"));
|
|
2255
|
+
}
|
|
2256
|
+
} catch {
|
|
2257
|
+
}
|
|
2258
|
+
if (generation !== this._generation) return;
|
|
2259
|
+
if (this._subscribed && this._loaded) return;
|
|
2260
|
+
this._retryTimer = setTimeout(
|
|
2261
|
+
() => void this.attempt(generation, Math.min(delay * 2, MAX_RETRY_MS)),
|
|
2262
|
+
delay
|
|
2263
|
+
);
|
|
2264
|
+
this._retryTimer.unref?.();
|
|
2265
|
+
}
|
|
2266
|
+
async subscribe(generation) {
|
|
2267
|
+
if (this._subscribed) return;
|
|
2268
|
+
const bus = this.grpcSdk.bus;
|
|
2269
|
+
if (!bus) return;
|
|
2270
|
+
await bus.subscribeAck(
|
|
2271
|
+
AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
|
|
2272
|
+
(message) => {
|
|
2273
|
+
try {
|
|
2274
|
+
this.applyConfig(JSON.parse(message));
|
|
2275
|
+
} catch {
|
|
2276
|
+
}
|
|
2277
|
+
},
|
|
2278
|
+
SUBSCRIBER_ID
|
|
2279
|
+
);
|
|
2280
|
+
if (generation !== this._generation) {
|
|
2281
|
+
bus.unsubscribe(SUBSCRIBER_ID);
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
this._subscribed = true;
|
|
2285
|
+
}
|
|
2286
|
+
/** Refetches the config from the authentication module; failures keep the previous state. */
|
|
2287
|
+
async refetch(force = false) {
|
|
2288
|
+
const now = Date.now();
|
|
2289
|
+
if (this._refetching) return;
|
|
2290
|
+
if (!force && now - this._lastRefetch < UNKNOWN_KID_REFETCH_MIN_MS) return;
|
|
2291
|
+
this._lastRefetch = now;
|
|
2292
|
+
this._refetching = true;
|
|
2293
|
+
try {
|
|
2294
|
+
this.applyConfig(await this.grpcSdk.config.get("authentication"));
|
|
2295
|
+
} catch {
|
|
2296
|
+
} finally {
|
|
2297
|
+
this._refetching = false;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
warnOnce(key, message) {
|
|
2301
|
+
if (this._warned.has(key)) return;
|
|
2302
|
+
this._warned.add(key);
|
|
2303
|
+
ConduitGrpcSdk10.Logger.warn(`AccessTokenVerifier: ${message}`);
|
|
2304
|
+
}
|
|
2305
|
+
static validPem(pem) {
|
|
2306
|
+
if (typeof pem !== "string" || !pem) return false;
|
|
2307
|
+
try {
|
|
2308
|
+
createPublicKey(pem);
|
|
2309
|
+
return true;
|
|
2310
|
+
} catch {
|
|
2311
|
+
return false;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
/**
|
|
2315
|
+
* Validates and normalizes the signing keys. Returns undefined when no signing block is
|
|
2316
|
+
* configured, null when the public key is invalid (the caller keeps the previous state).
|
|
2317
|
+
*/
|
|
2318
|
+
normalizeSigning(s) {
|
|
2319
|
+
if (!(s && typeof s.kid === "string" && s.kid && s.publicKey)) return void 0;
|
|
2320
|
+
if (!_AccessTokenVerifier.validPem(s.publicKey)) {
|
|
2321
|
+
this.warnOnce("publicKey", "invalid signing public key, keeping previous state");
|
|
2322
|
+
return null;
|
|
2323
|
+
}
|
|
2324
|
+
const previousPublicKey = this.validPreviousKey(s.previousPublicKey);
|
|
2325
|
+
return {
|
|
2326
|
+
kid: s.kid,
|
|
2327
|
+
publicKey: s.publicKey,
|
|
2328
|
+
previousKid: previousPublicKey ? s.previousKid || void 0 : void 0,
|
|
2329
|
+
previousPublicKey,
|
|
2330
|
+
previousRetiredAt: Number(s.previousRetiredAt ?? 0)
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
validPreviousKey(pem) {
|
|
2334
|
+
if (!pem) return void 0;
|
|
2335
|
+
if (_AccessTokenVerifier.validPem(pem)) return pem;
|
|
2336
|
+
this.warnOnce("previousPublicKey", "invalid previous public key, ignoring it");
|
|
2337
|
+
return void 0;
|
|
2338
|
+
}
|
|
2339
|
+
parseTokenVersion(raw) {
|
|
2340
|
+
const version = Number(raw ?? 0);
|
|
2341
|
+
if (Number.isNaN(version)) {
|
|
2342
|
+
this.warnOnce("tokenVersion", "invalid tokenVersion, all tokens will be rejected");
|
|
2343
|
+
}
|
|
2344
|
+
return version;
|
|
2345
|
+
}
|
|
2346
|
+
notifyListeners() {
|
|
2347
|
+
const state = this.state;
|
|
2348
|
+
for (const listener of [...this._listeners]) {
|
|
2349
|
+
try {
|
|
2350
|
+
listener(state);
|
|
2351
|
+
} catch (e) {
|
|
2352
|
+
ConduitGrpcSdk10.Logger.warn(
|
|
2353
|
+
`AccessTokenVerifier: change listener failed: ${e?.message}`
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Applies an authentication module config object (full config). Invalid input is ignored
|
|
2360
|
+
* and the previous state is kept; invalid keys are logged once.
|
|
2361
|
+
*/
|
|
2362
|
+
applyConfig(config) {
|
|
2363
|
+
if (!isConfigWithAccessTokens(config)) return;
|
|
2364
|
+
const tokens = config.accessTokens;
|
|
2365
|
+
const before = this.fingerprint();
|
|
2366
|
+
const signing = this.normalizeSigning(tokens.signing);
|
|
2367
|
+
if (signing === null) return;
|
|
2368
|
+
const version = this.parseTokenVersion(tokens.tokenVersion);
|
|
2369
|
+
this._mode = tokens.mode === "jwt" ? "jwt" : "session";
|
|
2370
|
+
this._tokenVersion = version;
|
|
2371
|
+
this._strictRevocation = tokens.strictRevocation === true;
|
|
2372
|
+
this._signing = signing;
|
|
2373
|
+
this._loaded = true;
|
|
2374
|
+
if (this.fingerprint() !== before) this.notifyListeners();
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Verifies an access token, throwing GrpcError(UNAUTHENTICATED) with a short reason on failure.
|
|
2378
|
+
* `options.now` is epoch milliseconds. An `unknown kid` triggers a throttled (at most once
|
|
2379
|
+
* per 10 s) background refetch of the config, to pick up a rotation sooner.
|
|
2380
|
+
*/
|
|
2381
|
+
verify(token, options) {
|
|
2382
|
+
if (!this._signing) {
|
|
2383
|
+
throw new GrpcError5(status7.UNAUTHENTICATED, "verifier not ready");
|
|
2384
|
+
}
|
|
2385
|
+
try {
|
|
2386
|
+
return verifyAccessToken(
|
|
2387
|
+
token,
|
|
2388
|
+
{ tokenVersion: this._tokenVersion, signing: this._signing },
|
|
2389
|
+
options
|
|
2390
|
+
);
|
|
2391
|
+
} catch (e) {
|
|
2392
|
+
if (e.message === "unknown kid") void this.refetch();
|
|
2393
|
+
throw e;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
|
|
1933
2398
|
// src/routing/RoutingController.ts
|
|
1934
|
-
import { status as
|
|
2399
|
+
import { status as status8 } from "@grpc/grpc-js";
|
|
1935
2400
|
var RoutingController = class {
|
|
1936
2401
|
_routeHandlers = {};
|
|
1937
2402
|
handleRequest(call, callback) {
|
|
@@ -1939,7 +2404,7 @@ var RoutingController = class {
|
|
|
1939
2404
|
this._routeHandlers[call.request.functionName](call, callback);
|
|
1940
2405
|
} else {
|
|
1941
2406
|
callback({
|
|
1942
|
-
code:
|
|
2407
|
+
code: status8.NOT_FOUND,
|
|
1943
2408
|
message: "Not found"
|
|
1944
2409
|
});
|
|
1945
2410
|
}
|
|
@@ -1998,13 +2463,13 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
1998
2463
|
await this.preRegisterLifecycle();
|
|
1999
2464
|
await this.grpcSdk.config.registerModule(this.name, this.address, this.healthState);
|
|
2000
2465
|
} catch (err) {
|
|
2001
|
-
|
|
2002
|
-
|
|
2466
|
+
ConduitGrpcSdk11.Logger.error("Failed to initialize server");
|
|
2467
|
+
ConduitGrpcSdk11.Logger.error(err);
|
|
2003
2468
|
process.exit(-1);
|
|
2004
2469
|
}
|
|
2005
2470
|
await this.postRegisterLifecycle().catch((err) => {
|
|
2006
|
-
|
|
2007
|
-
|
|
2471
|
+
ConduitGrpcSdk11.Logger.error("Failed to activate module");
|
|
2472
|
+
ConduitGrpcSdk11.Logger.error(err);
|
|
2008
2473
|
process.exit(-1);
|
|
2009
2474
|
});
|
|
2010
2475
|
}
|
|
@@ -2071,11 +2536,11 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2071
2536
|
* Registers common and module-specific metric types.
|
|
2072
2537
|
*/
|
|
2073
2538
|
async registerMetrics() {
|
|
2074
|
-
if (
|
|
2075
|
-
|
|
2539
|
+
if (ConduitGrpcSdk11.Metrics) {
|
|
2540
|
+
ConduitGrpcSdk11.Metrics.initializeDefaultMetrics();
|
|
2076
2541
|
if (this.metricsSchema) {
|
|
2077
2542
|
Object.values(this.metricsSchema).forEach((metric) => {
|
|
2078
|
-
|
|
2543
|
+
ConduitGrpcSdk11.Metrics.registerMetric(metric.type, metric.config);
|
|
2079
2544
|
});
|
|
2080
2545
|
}
|
|
2081
2546
|
}
|
|
@@ -2099,7 +2564,7 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2099
2564
|
const known = new Set(this.grpcSdk.getKnownModuleNames());
|
|
2100
2565
|
for (const n of names) {
|
|
2101
2566
|
if (!known.has(n)) {
|
|
2102
|
-
|
|
2567
|
+
ConduitGrpcSdk11.Logger.warn(
|
|
2103
2568
|
`Declared Conduit peer "${n}" is not a known built-in module (see ConduitGrpcSdk.getKnownModuleNames).`
|
|
2104
2569
|
);
|
|
2105
2570
|
}
|
|
@@ -2160,18 +2625,19 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2160
2625
|
this.service.functions
|
|
2161
2626
|
);
|
|
2162
2627
|
}
|
|
2628
|
+
RoutingManager.grpcSdk = this.grpcSdk;
|
|
2163
2629
|
RoutingManager.ClientController = new RoutingController();
|
|
2164
2630
|
RoutingManager.AdminController = new RoutingController();
|
|
2165
2631
|
await this.addHealthCheckService();
|
|
2166
2632
|
await this.addModuleService();
|
|
2167
2633
|
await this.grpcServer.start();
|
|
2168
|
-
|
|
2634
|
+
ConduitGrpcSdk11.Logger.log("gRPC server listening on " + this._port);
|
|
2169
2635
|
}
|
|
2170
2636
|
async setConfig(call, callback) {
|
|
2171
2637
|
try {
|
|
2172
2638
|
if (!this.config) {
|
|
2173
2639
|
return callback({
|
|
2174
|
-
code:
|
|
2640
|
+
code: status9.INVALID_ARGUMENT,
|
|
2175
2641
|
message: "Module is not configurable"
|
|
2176
2642
|
});
|
|
2177
2643
|
}
|
|
@@ -2190,7 +2656,7 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2190
2656
|
this.config = convict(this.configSchema);
|
|
2191
2657
|
this.config.load(previousConfig);
|
|
2192
2658
|
return callback({
|
|
2193
|
-
code:
|
|
2659
|
+
code: status9.INVALID_ARGUMENT,
|
|
2194
2660
|
message: "Invalid configuration values: " + e.message
|
|
2195
2661
|
});
|
|
2196
2662
|
}
|
|
@@ -2199,7 +2665,7 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2199
2665
|
await this._drainConfigQueue();
|
|
2200
2666
|
this.grpcSdk.bus?.publish(kebabCase(this.name) + ":config:update", configJson);
|
|
2201
2667
|
} catch (e) {
|
|
2202
|
-
return callback({ code:
|
|
2668
|
+
return callback({ code: status9.INTERNAL, message: e.message });
|
|
2203
2669
|
}
|
|
2204
2670
|
}
|
|
2205
2671
|
/** Used to update the module's configuration on initial Redis/DB reconciliation. */
|
|
@@ -2279,8 +2745,8 @@ var ManagedModule = class extends ConduitServiceModule {
|
|
|
2279
2745
|
};
|
|
2280
2746
|
|
|
2281
2747
|
// src/classes/ModuleError.ts
|
|
2282
|
-
import { GrpcError as
|
|
2283
|
-
var ModuleError = class extends
|
|
2748
|
+
import { GrpcError as GrpcError6 } from "@conduitplatform/grpc-sdk";
|
|
2749
|
+
var ModuleError = class extends GrpcError6 {
|
|
2284
2750
|
debugLogInfo;
|
|
2285
2751
|
constructor(errorDefinition, debugLogInfo) {
|
|
2286
2752
|
const { grpcCode, conduitCode, message } = errorDefinition;
|
|
@@ -2295,6 +2761,8 @@ var ModuleError = class extends GrpcError2 {
|
|
|
2295
2761
|
}
|
|
2296
2762
|
};
|
|
2297
2763
|
export {
|
|
2764
|
+
AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
|
|
2765
|
+
AccessTokenVerifier,
|
|
2298
2766
|
ConduitActiveSchema,
|
|
2299
2767
|
ConduitBoolean,
|
|
2300
2768
|
ConduitDate,
|
|
@@ -2305,6 +2773,7 @@ export {
|
|
|
2305
2773
|
ConduitServiceModule,
|
|
2306
2774
|
ConduitString,
|
|
2307
2775
|
ConfigController,
|
|
2776
|
+
DEFAULT_LEEWAY_SECONDS,
|
|
2308
2777
|
DEFAULT_STRIP_FIELDS,
|
|
2309
2778
|
GrpcServer,
|
|
2310
2779
|
ManagedModule,
|
|
@@ -2317,20 +2786,27 @@ export {
|
|
|
2317
2786
|
convictConfigParser,
|
|
2318
2787
|
createParsedRouterRequest,
|
|
2319
2788
|
createServer,
|
|
2789
|
+
createUserHydrator,
|
|
2320
2790
|
exportModel,
|
|
2791
|
+
getAuthMiddlewareKind,
|
|
2792
|
+
hydrateContextUser,
|
|
2321
2793
|
importModel,
|
|
2322
2794
|
initializeSdk,
|
|
2323
2795
|
linearBackoffTimeout,
|
|
2324
2796
|
linearBackoffTimeoutAsync,
|
|
2797
|
+
mapHydrationErrorCode,
|
|
2325
2798
|
merge,
|
|
2326
2799
|
readConduitPeersManifest,
|
|
2327
2800
|
reconcileStoredModuleConfig,
|
|
2328
2801
|
redactSensitiveConfig,
|
|
2329
2802
|
restoreRedactedSecrets,
|
|
2330
2803
|
sanitizeDocumentsForExport,
|
|
2804
|
+
selectVerificationKey,
|
|
2805
|
+
shouldHydrateUser,
|
|
2331
2806
|
stableConfigJson,
|
|
2332
2807
|
storedConfigsEquivalent,
|
|
2333
2808
|
stripInternalFields,
|
|
2809
|
+
verifyAccessToken,
|
|
2334
2810
|
wrapFunctionsAsync,
|
|
2335
2811
|
wrapGrpcFunctions,
|
|
2336
2812
|
wrapRouterGrpcFunction
|