@routr/connect 2.0.8-alpha.8 → 2.0.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/access.d.ts +25 -8
- package/dist/access.js +23 -32
- package/dist/assertions.d.ts +1 -0
- package/dist/assertions.js +31 -0
- package/dist/envs.d.ts +6 -0
- package/dist/envs.js +32 -0
- package/dist/errors.js +2 -2
- package/dist/handlers.d.ts +2 -2
- package/dist/handlers.js +52 -21
- package/dist/router.d.ts +1 -1
- package/dist/router.js +168 -39
- package/dist/runner.js +5 -5
- package/dist/service.js +5 -4
- package/dist/tailor.js +2 -4
- package/dist/tracer.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +14 -11
- package/dist/utils.js +103 -56
- package/package.json +10 -6
package/dist/access.d.ts
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
import { MessageRequest, CommonConnect as CC } from "@routr/common";
|
1
|
+
import { MessageRequest, CommonTypes as CT, CommonConnect as CC } from "@routr/common";
|
2
2
|
import { RoutingDirection } from "./types";
|
3
3
|
export declare const checkAccess: (accessRequest: {
|
4
|
-
|
4
|
+
apiClient: CC.APIClient;
|
5
5
|
request: MessageRequest;
|
6
|
-
caller: CC.
|
7
|
-
callee: CC.
|
6
|
+
caller: CC.RoutableResourceUnion;
|
7
|
+
callee: CC.RoutableResourceUnion;
|
8
8
|
routingDirection: RoutingDirection;
|
9
9
|
}) => Promise<Record<string, unknown>>;
|
10
|
-
export declare const
|
10
|
+
export declare const checkAgentOrPeerAccess: (request: MessageRequest, caller: CC.RoutableResourceUnion) => Promise<{
|
11
11
|
message: {
|
12
|
-
responseType:
|
12
|
+
responseType: CT.ResponseType;
|
13
|
+
reasonPhrase: string;
|
13
14
|
wwwAuthenticate: {
|
14
15
|
scheme: string;
|
15
16
|
realm: string;
|
@@ -21,8 +22,24 @@ export declare const checkAgentAccess: (dataAPI: CC.DataAPI, request: MessageReq
|
|
21
22
|
};
|
22
23
|
};
|
23
24
|
}>;
|
24
|
-
export declare const checkAccessFromPSTN: (
|
25
|
+
export declare const checkAccessFromPSTN: (apiClient: CC.APIClient, request: MessageRequest, callee: CC.INumber) => Promise<{
|
25
26
|
message: {
|
26
|
-
responseType:
|
27
|
+
responseType: CT.ResponseType;
|
28
|
+
reasonPhrase: string;
|
29
|
+
wwwAuthenticate: {
|
30
|
+
scheme: string;
|
31
|
+
realm: string;
|
32
|
+
qop: string;
|
33
|
+
opaque: string;
|
34
|
+
stale: boolean;
|
35
|
+
nonce: string;
|
36
|
+
algorithm: string;
|
37
|
+
};
|
38
|
+
};
|
39
|
+
} | {
|
40
|
+
metadata: Record<string, string>;
|
41
|
+
message: {
|
42
|
+
responseType: CT.ResponseType;
|
43
|
+
reasonPhrase: string;
|
27
44
|
};
|
28
45
|
}>;
|
package/dist/access.js
CHANGED
@@ -9,9 +9,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
9
9
|
});
|
10
10
|
};
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
-
exports.checkAccessFromPSTN = exports.
|
12
|
+
exports.checkAccessFromPSTN = exports.checkAgentOrPeerAccess = exports.checkAccess = void 0;
|
13
13
|
/*
|
14
|
-
* Copyright (C)
|
14
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
15
15
|
* http://github.com/fonoster
|
16
16
|
*
|
17
17
|
* This file is part of Routr.
|
@@ -31,32 +31,31 @@ exports.checkAccessFromPSTN = exports.checkAgentAccess = exports.checkAccess = v
|
|
31
31
|
const logger_1 = require("@fonoster/logger");
|
32
32
|
const common_1 = require("@routr/common");
|
33
33
|
const types_1 = require("./types");
|
34
|
-
const utils_1 = require("./utils");
|
35
34
|
const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
|
36
35
|
const checkAccess = (accessRequest) => __awaiter(void 0, void 0, void 0, function* () {
|
37
|
-
const {
|
36
|
+
const { apiClient, request, caller, callee, routingDirection } = accessRequest;
|
38
37
|
switch (routingDirection) {
|
38
|
+
case types_1.RoutingDirection.PEER_TO_PSTN:
|
39
39
|
case types_1.RoutingDirection.AGENT_TO_AGENT:
|
40
|
-
return (0, exports.checkAgentAccess)(dataAPI, request, caller);
|
41
40
|
case types_1.RoutingDirection.AGENT_TO_PSTN:
|
42
|
-
return (0, exports.
|
41
|
+
return (0, exports.checkAgentOrPeerAccess)(request, caller);
|
43
42
|
case types_1.RoutingDirection.FROM_PSTN:
|
44
|
-
return (0, exports.checkAccessFromPSTN)(
|
43
|
+
return (0, exports.checkAccessFromPSTN)(apiClient, request, callee);
|
45
44
|
case types_1.RoutingDirection.UNKNOWN:
|
46
45
|
return common_1.Auth.createForbideenResponse();
|
47
46
|
}
|
48
47
|
});
|
49
48
|
exports.checkAccess = checkAccess;
|
50
|
-
const
|
49
|
+
const checkAgentOrPeerAccess = (request, caller) => __awaiter(void 0, void 0, void 0, function* () {
|
51
50
|
// Calculate and return challenge
|
52
51
|
if (request.message.authorization) {
|
53
52
|
const auth = Object.assign({}, request.message.authorization);
|
54
53
|
auth.method = request.method;
|
55
|
-
const credentials =
|
54
|
+
const credentials = caller.credentials;
|
56
55
|
// Calculate response and compare with the one send by the endpoint
|
57
56
|
const calcRes = common_1.Auth.calculateAuthResponse(auth, {
|
58
|
-
username: credentials === null || credentials === void 0 ? void 0 : credentials.
|
59
|
-
secret: credentials === null || credentials === void 0 ? void 0 : credentials.
|
57
|
+
username: credentials === null || credentials === void 0 ? void 0 : credentials.username,
|
58
|
+
secret: credentials === null || credentials === void 0 ? void 0 : credentials.password
|
60
59
|
});
|
61
60
|
if (calcRes !== auth.response) {
|
62
61
|
return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
|
@@ -66,53 +65,45 @@ const checkAgentAccess = (dataAPI, request, caller) => __awaiter(void 0, void 0,
|
|
66
65
|
return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
|
67
66
|
}
|
68
67
|
});
|
69
|
-
exports.
|
70
|
-
const checkAccessFromPSTN = (
|
71
|
-
var _a, _b;
|
68
|
+
exports.checkAgentOrPeerAccess = checkAgentOrPeerAccess;
|
69
|
+
const checkAccessFromPSTN = (apiClient, request, callee) => __awaiter(void 0, void 0, void 0, function* () {
|
72
70
|
// Get the Trunk associated with the SIP URI
|
73
|
-
const trunk = yield
|
71
|
+
const trunk = (yield apiClient.trunks.findBy({
|
72
|
+
fieldName: "inboundUri",
|
73
|
+
fieldValue: request.message.requestUri.host
|
74
|
+
})).items[0];
|
74
75
|
// If the Trunk or Number doesn't exist reject the call
|
75
76
|
if (!callee || !trunk) {
|
76
77
|
return common_1.Auth.createForbideenResponse();
|
77
78
|
}
|
78
|
-
if (callee.
|
79
|
+
if (callee.trunk.ref !== trunk.ref) {
|
79
80
|
return common_1.Auth.createForbideenResponse();
|
80
81
|
}
|
81
82
|
// Verify that the IP is whitelisted which means getting the access control list for the trunk
|
82
|
-
if (
|
83
|
+
if (trunk.accessControlList) {
|
83
84
|
try {
|
84
|
-
const
|
85
|
-
if (!acl) {
|
86
|
-
// Should never happen since the ACL is required
|
87
|
-
return common_1.Auth.createServerInternalErrorResponse();
|
88
|
-
}
|
89
|
-
const allow = acl.spec.accessControlList.allow.filter((net) => {
|
85
|
+
const allow = trunk.accessControlList.allow.filter((net) => {
|
90
86
|
return common_1.IpUtils.hasIp(net, request.sender.host);
|
91
87
|
})[0];
|
92
88
|
if (!allow) {
|
93
|
-
// TODO: Replace with Unauthorized
|
94
89
|
return common_1.Auth.createUnauthorizedResponseWithoutChallenge();
|
95
90
|
}
|
96
91
|
}
|
97
92
|
catch (e) {
|
98
93
|
logger.error(e);
|
94
|
+
return common_1.Auth.createServerInternalErrorResponse();
|
99
95
|
}
|
100
96
|
}
|
101
97
|
// If the Trunk has a User/Password we must verify that the User/Password are valid
|
102
|
-
if (
|
103
|
-
const credentials = yield dataAPI.get(trunk.spec.inbound.credentialsRef);
|
104
|
-
if (!credentials) {
|
105
|
-
// Should never happen since the Credentials is required
|
106
|
-
return common_1.Auth.createServerInternalErrorResponse();
|
107
|
-
}
|
98
|
+
if (trunk.inboundCredentials) {
|
108
99
|
// Calculate and return challenge
|
109
100
|
if (request.message.authorization) {
|
110
101
|
const auth = Object.assign({}, request.message.authorization);
|
111
102
|
auth.method = request.method;
|
112
103
|
// Calculate response and compare with the one send by the endpoint
|
113
104
|
const calcRes = common_1.Auth.calculateAuthResponse(auth, {
|
114
|
-
username:
|
115
|
-
secret:
|
105
|
+
username: trunk.inboundCredentials.username,
|
106
|
+
secret: trunk.inboundCredentials.password
|
116
107
|
});
|
117
108
|
if (calcRes !== auth.response) {
|
118
109
|
return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const assertOnlyOneEnvIsSet: (envs: string[]) => void;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.assertOnlyOneEnvIsSet = void 0;
|
4
|
+
/*
|
5
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
6
|
+
* http://github.com/fonoster/routr
|
7
|
+
*
|
8
|
+
* This file is part of Routr
|
9
|
+
*
|
10
|
+
* Licensed under the MIT License (the "License");
|
11
|
+
* you may not use this file except in compliance with
|
12
|
+
* the License. You may obtain a copy of the License at
|
13
|
+
*
|
14
|
+
* https://opensource.org/licenses/MIT
|
15
|
+
*
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
19
|
+
* See the License for the specific language governing permissions and
|
20
|
+
* limitations under the License.
|
21
|
+
*/
|
22
|
+
const logger_1 = require("@fonoster/logger");
|
23
|
+
const logger = (0, logger_1.getLogger)({ service: "common", filePath: __filename });
|
24
|
+
const assertOnlyOneEnvIsSet = (envs) => {
|
25
|
+
const envsSet = envs.filter((env) => process.env[env] != null);
|
26
|
+
if (envsSet.length > 1) {
|
27
|
+
logger.error(`only one of the following environment variables can be set: ${envs.join(", ")}.`);
|
28
|
+
process.exit(1);
|
29
|
+
}
|
30
|
+
};
|
31
|
+
exports.assertOnlyOneEnvIsSet = assertOnlyOneEnvIsSet;
|
package/dist/envs.d.ts
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
export declare const BIND_ADDR: string;
|
2
|
+
export declare const LOCATION_ADDR: string;
|
3
|
+
export declare const API_ADDR: string;
|
4
|
+
export declare const CONNECT_VERIFIER_ADDR: string;
|
5
|
+
export declare const CONNECT_VERIFIER_PUBLIC_KEY_PATH: string;
|
6
|
+
export declare const CONNECT_VERIFIER_OPTIONS: any;
|
package/dist/envs.js
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
"use strict";
|
2
|
+
var _a;
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
4
|
+
exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
|
5
|
+
/*
|
6
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
7
|
+
* http://github.com/fonoster
|
8
|
+
*
|
9
|
+
* This file is part of Routr.
|
10
|
+
*
|
11
|
+
* Licensed under the MIT License (the "License");
|
12
|
+
* you may not use this file except in compliance with
|
13
|
+
* the License. You may obtain a copy of the License at
|
14
|
+
*
|
15
|
+
* https://opensource.org/licenses/MIT
|
16
|
+
*
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
20
|
+
* See the License for the specific language governing permissions and
|
21
|
+
* limitations under the License.
|
22
|
+
*/
|
23
|
+
const assertions_1 = require("./assertions");
|
24
|
+
(0, assertions_1.assertOnlyOneEnvIsSet)(["CONNECT_VERIFIER_ADDR", "CONNECT_VERIFIER_PUBLIC_KEY"]);
|
25
|
+
exports.BIND_ADDR = (_a = process.env.BIND_ADDR) !== null && _a !== void 0 ? _a : "0.0.0.0:51904";
|
26
|
+
exports.LOCATION_ADDR = process.env.LOCATION_ADDR;
|
27
|
+
exports.API_ADDR = process.env.API_ADDR;
|
28
|
+
exports.CONNECT_VERIFIER_ADDR = process.env.CONNECT_VERIFIER_ADDR;
|
29
|
+
exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = process.env.CONNECT_VERIFIER_PUBLIC_KEY_PATH;
|
30
|
+
exports.CONNECT_VERIFIER_OPTIONS = process.env.CONNECT_VERIFIER_OPTIONS
|
31
|
+
? JSON.parse(process.env.CONNECT_VERIFIER_OPTIONS)
|
32
|
+
: { expiresIn: "1h", algorithm: ["RS256"] };
|
package/dist/errors.js
CHANGED
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
26
26
|
exports.UnsuportedRoutingError = exports.ServiceUnavailableError = void 0;
|
27
27
|
/*
|
28
|
-
* Copyright (C)
|
28
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
29
29
|
* http://github.com/fonoster/routr
|
30
30
|
*
|
31
31
|
* This file is part of Routr
|
@@ -71,7 +71,7 @@ class UnsuportedRoutingError extends Error {
|
|
71
71
|
*/
|
72
72
|
constructor(routingDirection) {
|
73
73
|
super("unsupported routing direction: " + routingDirection);
|
74
|
-
this.code = grpc.status.
|
74
|
+
this.code = grpc.status.UNIMPLEMENTED;
|
75
75
|
// Set the prototype explicitly.
|
76
76
|
Object.setPrototypeOf(this, ServiceUnavailableError.prototype);
|
77
77
|
}
|
package/dist/handlers.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { MessageRequest, Response } from "@routr/processor";
|
2
2
|
import { ILocationService } from "@routr/location";
|
3
3
|
import { CommonConnect as CC } from "@routr/common";
|
4
|
-
export declare const handleRegister: (
|
4
|
+
export declare const handleRegister: (apiClient: CC.APIClient, location: ILocationService) => (request: MessageRequest, res: Response) => Promise<void>;
|
5
5
|
export declare const handleRegistry: (req: MessageRequest, res: Response) => void;
|
6
|
-
export declare const handleRequest: (location: ILocationService,
|
6
|
+
export declare const handleRequest: (location: ILocationService, apiClient?: CC.APIClient) => (request: MessageRequest, res: Response) => Promise<void>;
|
package/dist/handlers.js
CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
12
|
exports.handleRequest = exports.handleRegistry = exports.handleRegister = void 0;
|
13
13
|
/*
|
14
|
-
* Copyright (C)
|
14
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
15
15
|
* http://github.com/fonoster/routr
|
16
16
|
*
|
17
17
|
* This file is part of Routr
|
@@ -35,66 +35,97 @@ const function_1 = require("fp-ts/function");
|
|
35
35
|
const router_1 = require("./router");
|
36
36
|
const common_1 = require("@routr/common");
|
37
37
|
const utils_1 = require("./utils");
|
38
|
-
const
|
38
|
+
const logger_1 = require("@fonoster/logger");
|
39
|
+
const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
|
40
|
+
const enforceE164 = processor_1.Alterations.enforceE164(common_1.Environment.ENFORCE_E164, common_1.Environment.ENFORCE_E164_WITH_MOBILE_PREFIX);
|
41
|
+
const jwtVerifier = (0, utils_1.getVerifierImpl)();
|
42
|
+
const handleRegister = (apiClient, location) => {
|
39
43
|
return (request, res) => __awaiter(void 0, void 0, void 0, function* () {
|
40
44
|
// Calculate and return challenge
|
41
45
|
if (request.message.authorization) {
|
42
46
|
const auth = Object.assign({}, request.message.authorization);
|
43
47
|
auth.method = request.method;
|
44
48
|
const fromURI = request.message.from.address.uri;
|
45
|
-
const
|
46
|
-
if (!
|
49
|
+
const peerOrAgent = yield (0, utils_1.findResource)(apiClient, fromURI.host, fromURI.user);
|
50
|
+
if (!peerOrAgent) {
|
47
51
|
return res.send(common_1.Auth.createForbideenResponse());
|
48
52
|
}
|
49
|
-
const credentials =
|
53
|
+
const credentials = peerOrAgent.credentials;
|
50
54
|
// Calculate response and compare with the one send by the endpoint
|
51
55
|
const calcRes = common_1.Auth.calculateAuthResponse(auth, {
|
52
|
-
username: credentials === null || credentials === void 0 ? void 0 : credentials.
|
53
|
-
secret: credentials === null || credentials === void 0 ? void 0 : credentials.
|
56
|
+
username: credentials === null || credentials === void 0 ? void 0 : credentials.username,
|
57
|
+
secret: credentials === null || credentials === void 0 ? void 0 : credentials.password
|
54
58
|
});
|
55
59
|
if (calcRes !== auth.response) {
|
56
60
|
return res.send(common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host));
|
57
61
|
}
|
62
|
+
// TODO: Needs test
|
63
|
+
yield location.addRoute({
|
64
|
+
aor: "aor" in peerOrAgent ? peerOrAgent.aor : processor_1.Target.getTargetAOR(request),
|
65
|
+
route: location_1.Helper.createRoute(request)
|
66
|
+
});
|
67
|
+
res.sendOk();
|
68
|
+
}
|
69
|
+
else if ((0, utils_1.hasXConnectObjectHeader)(request)) {
|
70
|
+
const connectToken = processor_1.Extensions.getHeaderValue(request, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
|
71
|
+
try {
|
72
|
+
const payload = (yield jwtVerifier.verify(connectToken));
|
73
|
+
if (!payload.allowedMethods.includes(common_1.Method.REGISTER)) {
|
74
|
+
return res.send(common_1.Auth.createForbideenResponse());
|
75
|
+
}
|
76
|
+
yield location.addRoute({
|
77
|
+
aor: payload.aor,
|
78
|
+
route: location_1.Helper.createRoute(request)
|
79
|
+
});
|
80
|
+
}
|
81
|
+
catch (e) {
|
82
|
+
logger.verbose("unable to validate connect token", {
|
83
|
+
originalError: e.message
|
84
|
+
});
|
85
|
+
res.send(common_1.Auth.createForbideenResponse());
|
86
|
+
}
|
58
87
|
}
|
59
88
|
else {
|
60
|
-
|
89
|
+
res.send(common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host));
|
61
90
|
}
|
62
|
-
yield location.addRoute({
|
63
|
-
aor: processor_1.Target.getTargetAOR(request),
|
64
|
-
route: location_1.Helper.createRoute(request)
|
65
|
-
});
|
66
|
-
res.sendOk();
|
67
91
|
});
|
68
92
|
};
|
69
93
|
exports.handleRegister = handleRegister;
|
70
94
|
// TODO: Needs test
|
71
95
|
const handleRegistry = (req, res) => {
|
72
|
-
const route =
|
73
|
-
res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.
|
96
|
+
const route = location_1.Helper.createRouteFromLastMessage(req);
|
97
|
+
res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeSelfRoutes, processor_1.Alterations.removeXEdgePortRef));
|
74
98
|
};
|
75
99
|
exports.handleRegistry = handleRegistry;
|
76
100
|
// TODO: If request has X-Connect-Token then validate the JWT value and continue
|
77
|
-
const handleRequest = (location,
|
101
|
+
const handleRequest = (location, apiClient) => (request, res) => __awaiter(void 0, void 0, void 0, function* () {
|
78
102
|
try {
|
103
|
+
const req = common_1.Environment.ENFORCE_E164 ? enforceE164(request) : request;
|
104
|
+
// Must get the metadata here before the request is forwarded
|
79
105
|
const route = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.EDGEPORT_REF)
|
80
|
-
?
|
81
|
-
: yield (0, router_1.router)(location,
|
106
|
+
? location_1.Helper.createRouteFromLastMessage(req)
|
107
|
+
: yield (0, router_1.router)(location, apiClient)(req);
|
82
108
|
if (!route)
|
83
109
|
return res.sendNotFound();
|
84
110
|
// If route is not type Route then return
|
85
|
-
if (!("
|
111
|
+
if (!("listeningPoints" in route)) {
|
86
112
|
return res.send(route);
|
87
113
|
}
|
88
114
|
else {
|
89
115
|
// Forward request to peer edgeport
|
90
116
|
if (req.edgePortRef !== route.edgePortRef) {
|
91
|
-
return (0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.
|
117
|
+
return res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.addSelfRecordRoute(route),
|
118
|
+
// The order of the routes is important
|
119
|
+
processor_1.Alterations.addRouteToPeerEdgePort(route), processor_1.Alterations.addRouteToNextHop(route), processor_1.Alterations.addXEdgePortRef, processor_1.Alterations.decreaseMaxForwards));
|
92
120
|
}
|
121
|
+
// TODO: We should add this the Tailor API
|
122
|
+
req.metadata = route.metadata;
|
93
123
|
res.send((0, tailor_1.tailor)(route, req));
|
94
124
|
}
|
95
125
|
}
|
96
126
|
catch (err) {
|
97
|
-
|
127
|
+
logger.error(err);
|
128
|
+
res.sendInternalServerError();
|
98
129
|
}
|
99
130
|
});
|
100
131
|
exports.handleRequest = handleRequest;
|
package/dist/router.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
import { CommonConnect as CC, Route } from "@routr/common";
|
2
2
|
import { MessageRequest } from "@routr/processor";
|
3
3
|
import { ILocationService } from "@routr/location";
|
4
|
-
export declare function router(location: ILocationService,
|
4
|
+
export declare function router(location: ILocationService, apiClient: CC.APIClient): (request: MessageRequest) => Promise<Route | Record<string, unknown>>;
|
package/dist/router.js
CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
12
|
exports.router = void 0;
|
13
13
|
/*
|
14
|
-
* Copyright (C)
|
14
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
15
15
|
* http://github.com/fonoster/routr
|
16
16
|
*
|
17
17
|
* This file is part of Routr
|
@@ -32,26 +32,69 @@ const types_1 = require("./types");
|
|
32
32
|
const common_1 = require("@routr/common");
|
33
33
|
const utils_1 = require("./utils");
|
34
34
|
const processor_1 = require("@routr/processor");
|
35
|
-
const location_1 = require("@routr/location");
|
36
35
|
const errors_1 = require("./errors");
|
37
36
|
const logger_1 = require("@fonoster/logger");
|
38
37
|
const access_1 = require("./access");
|
39
38
|
const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
|
39
|
+
const jwtVerifier = (0, utils_1.getVerifierImpl)();
|
40
40
|
// eslint-disable-next-line require-jsdoc
|
41
|
-
function router(location,
|
41
|
+
function router(location, apiClient) {
|
42
42
|
return (request) => __awaiter(this, void 0, void 0, function* () {
|
43
|
+
var _a;
|
43
44
|
const fromURI = request.message.from.address.uri;
|
44
45
|
const requestURI = request.message.requestUri;
|
46
|
+
let caller;
|
47
|
+
let callee;
|
48
|
+
if ((0, utils_1.hasXConnectObjectHeader)(request)) {
|
49
|
+
const connectToken = processor_1.Extensions.getHeaderValue(request, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
|
50
|
+
try {
|
51
|
+
if (!jwtVerifier) {
|
52
|
+
return common_1.Auth.createServerInternalErrorResponse();
|
53
|
+
}
|
54
|
+
const payload = (yield jwtVerifier.verify(connectToken));
|
55
|
+
const domain = yield (0, utils_1.findDomain)(apiClient, payload.domain);
|
56
|
+
if (!payload.allowedMethods.includes(common_1.Method.INVITE)) {
|
57
|
+
return common_1.Auth.createForbideenResponse();
|
58
|
+
}
|
59
|
+
caller = {
|
60
|
+
apiVersion: common_1.CommonConnect.APIVersion.V2,
|
61
|
+
ref: payload.ref,
|
62
|
+
name: (_a = request.message.from.address.displayName) !== null && _a !== void 0 ? _a : common_1.CommonTypes.ANONYMOUS,
|
63
|
+
domain: domain,
|
64
|
+
domainRef: payload.domainRef,
|
65
|
+
username: common_1.CommonTypes.ANONYMOUS,
|
66
|
+
privacy: processor_1.Extensions.getHeaderValue(request, "Privacy"),
|
67
|
+
enabled: true
|
68
|
+
};
|
69
|
+
callee = (yield apiClient.peers.findBy({
|
70
|
+
fieldName: "aor",
|
71
|
+
fieldValue: payload.aorLink
|
72
|
+
})).items[0];
|
73
|
+
}
|
74
|
+
catch (e) {
|
75
|
+
logger.verbose("unable to validate connect token", {
|
76
|
+
originalError: e.message
|
77
|
+
});
|
78
|
+
return common_1.Auth.createForbideenResponse();
|
79
|
+
}
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
caller = yield (0, utils_1.findResource)(apiClient, fromURI.host, fromURI.user);
|
83
|
+
callee = yield (0, utils_1.findResource)(apiClient, requestURI.host, requestURI.user);
|
84
|
+
}
|
85
|
+
const routingDirection = (0, utils_1.getRoutingDirection)(caller, callee);
|
45
86
|
logger.verbose("routing request from: " +
|
46
87
|
(0, utils_1.getSIPURI)(fromURI) +
|
47
88
|
", to: " +
|
48
|
-
(0, utils_1.getSIPURI)(requestURI), {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
89
|
+
(0, utils_1.getSIPURI)(requestURI), {
|
90
|
+
fromURI: (0, utils_1.getSIPURI)(fromURI),
|
91
|
+
requestURI: (0, utils_1.getSIPURI)(requestURI),
|
92
|
+
routingDirection
|
93
|
+
});
|
94
|
+
if (!(0, utils_1.hasXConnectObjectHeader)(request) &&
|
95
|
+
request.method === common_1.CommonTypes.Method.INVITE) {
|
53
96
|
const failedCheck = yield (0, access_1.checkAccess)({
|
54
|
-
|
97
|
+
apiClient,
|
55
98
|
request,
|
56
99
|
caller,
|
57
100
|
callee,
|
@@ -61,13 +104,26 @@ function router(location, dataAPI) {
|
|
61
104
|
return failedCheck;
|
62
105
|
}
|
63
106
|
}
|
107
|
+
// We add metadata to the route object so we can use it later to link to an account
|
64
108
|
switch (routingDirection) {
|
65
|
-
case types_1.RoutingDirection.AGENT_TO_AGENT:
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
case types_1.RoutingDirection.
|
70
|
-
|
109
|
+
case types_1.RoutingDirection.AGENT_TO_AGENT: {
|
110
|
+
const route = yield agentToAgent(location, request);
|
111
|
+
return Object.assign(Object.assign({}, route), { metadata: caller.extended });
|
112
|
+
}
|
113
|
+
case types_1.RoutingDirection.AGENT_TO_PEER: {
|
114
|
+
const route = yield agentToPeer(location, callee, request);
|
115
|
+
return Object.assign(Object.assign({}, route), { metadata: caller.extended });
|
116
|
+
}
|
117
|
+
case types_1.RoutingDirection.AGENT_TO_PSTN: {
|
118
|
+
const route = yield agentToPSTN(request, caller, requestURI.user);
|
119
|
+
return Object.assign(Object.assign({}, route), { metadata: caller.extended });
|
120
|
+
}
|
121
|
+
case types_1.RoutingDirection.FROM_PSTN: {
|
122
|
+
const route = yield fromPSTN(apiClient, location, callee, request);
|
123
|
+
return Object.assign(Object.assign({}, route), { metadata: callee.extended });
|
124
|
+
}
|
125
|
+
case types_1.RoutingDirection.PEER_TO_PSTN:
|
126
|
+
return yield peerToPSTN(apiClient, request);
|
71
127
|
default:
|
72
128
|
throw new errors_1.UnsuportedRoutingError(routingDirection);
|
73
129
|
}
|
@@ -77,29 +133,46 @@ exports.router = router;
|
|
77
133
|
// eslint-disable-next-line require-jsdoc
|
78
134
|
function agentToAgent(location, req) {
|
79
135
|
return __awaiter(this, void 0, void 0, function* () {
|
80
|
-
return (yield location.findRoutes({ aor: processor_1.Target.getTargetAOR(req) }))[0];
|
136
|
+
return (yield location.findRoutes({ aor: processor_1.Target.getTargetAOR(req), callId: req.ref }))[0];
|
81
137
|
});
|
82
138
|
}
|
83
139
|
/**
|
84
140
|
* From PSTN routing.
|
85
141
|
*
|
142
|
+
* @param {APIClient} apiClient - API client
|
86
143
|
* @param {ILocationService} location - Location service
|
87
|
-
* @param {uknown} _
|
88
144
|
* @param {Resource} callee - The callee
|
145
|
+
* @param {MessageRequest} req - The request
|
89
146
|
* @return {Promise<Route>}
|
90
147
|
*/
|
91
|
-
function fromPSTN(location,
|
148
|
+
function fromPSTN(apiClient, location, callee, req) {
|
92
149
|
var _a;
|
93
150
|
return __awaiter(this, void 0, void 0, function* () {
|
151
|
+
const sessionAffinityRef = processor_1.Extensions.getHeaderValue(req, callee.sessionAffinityHeader);
|
152
|
+
let backend;
|
153
|
+
if (callee.aorLink.startsWith("backend:")) {
|
154
|
+
const peer = (yield apiClient.peers.findBy({
|
155
|
+
fieldName: "aor",
|
156
|
+
fieldValue: callee.aorLink
|
157
|
+
})).items[0];
|
158
|
+
backend = {
|
159
|
+
ref: peer.ref,
|
160
|
+
balancingAlgorithm: peer.balancingAlgorithm,
|
161
|
+
withSessionAffinity: peer.withSessionAffinity
|
162
|
+
};
|
163
|
+
}
|
94
164
|
const route = (yield location.findRoutes({
|
95
|
-
aor: callee.
|
165
|
+
aor: callee.aorLink,
|
166
|
+
callId: req.ref,
|
167
|
+
sessionAffinityRef,
|
168
|
+
backend
|
96
169
|
}))[0];
|
97
170
|
if (!route) {
|
98
|
-
|
171
|
+
return null;
|
99
172
|
}
|
100
173
|
if (!route.headers)
|
101
174
|
route.headers = [];
|
102
|
-
(_a = callee.
|
175
|
+
(_a = callee.extraHeaders) === null || _a === void 0 ? void 0 : _a.forEach((prop) => {
|
103
176
|
const p = {
|
104
177
|
name: prop.name,
|
105
178
|
value: prop.value,
|
@@ -111,26 +184,82 @@ function fromPSTN(location, _, callee) {
|
|
111
184
|
});
|
112
185
|
}
|
113
186
|
// eslint-disable-next-line require-jsdoc
|
114
|
-
function
|
115
|
-
var _a, _b;
|
187
|
+
function agentToPSTN(req, agent, calleeNumber) {
|
188
|
+
var _a, _b, _c, _d, _e;
|
116
189
|
return __awaiter(this, void 0, void 0, function* () {
|
117
|
-
|
190
|
+
if (!((_a = agent.domain) === null || _a === void 0 ? void 0 : _a.egressPolicies)) {
|
191
|
+
// TODO: Create custom error
|
192
|
+
throw new Error(`no egress policy found for Domain ref: ${agent.domain.ref}`);
|
193
|
+
}
|
118
194
|
// Look for Number in domain that matches regex callee
|
119
|
-
const policy =
|
195
|
+
const policy = agent.domain.egressPolicies.find((policy) => {
|
120
196
|
const regex = new RegExp(policy.rule);
|
121
197
|
return regex.test(calleeNumber);
|
122
198
|
});
|
123
|
-
const
|
124
|
-
const trunk = yield dataAPI.get(number === null || number === void 0 ? void 0 : number.spec.trunkRef);
|
125
|
-
if (!domain.spec.context.egressPolicies) {
|
126
|
-
// TODO: Create custom error
|
127
|
-
throw new Error(`no egress policy found for Domain ref: ${domain.ref}`);
|
128
|
-
}
|
199
|
+
const trunk = (_b = policy.number) === null || _b === void 0 ? void 0 : _b.trunk;
|
129
200
|
if (!trunk) {
|
130
|
-
//
|
131
|
-
throw new Error(`no trunk associated with Number ref: ${number === null ||
|
201
|
+
// This should never happen
|
202
|
+
throw new Error(`no trunk associated with Number ref: ${(_c = policy.number) === null || _c === void 0 ? void 0 : _c.ref}`);
|
132
203
|
}
|
133
204
|
const uri = (0, utils_1.getTrunkURI)(trunk);
|
205
|
+
return {
|
206
|
+
user: uri.user,
|
207
|
+
host: uri.host,
|
208
|
+
port: uri.port,
|
209
|
+
transport: (_d = uri.transport) === null || _d === void 0 ? void 0 : _d.toUpperCase(),
|
210
|
+
edgePortRef: req.edgePortRef,
|
211
|
+
listeningPoints: req.listeningPoints,
|
212
|
+
localnets: req.localnets,
|
213
|
+
externalAddrs: req.externalAddrs,
|
214
|
+
headers: [
|
215
|
+
// TODO: Find a more deterministic way to re-add the Privacy header
|
216
|
+
{
|
217
|
+
name: "Privacy",
|
218
|
+
action: common_1.CommonTypes.HeaderModifierAction.REMOVE
|
219
|
+
},
|
220
|
+
{
|
221
|
+
name: "Privacy",
|
222
|
+
value: ((_e = agent.privacy) === null || _e === void 0 ? void 0 : _e.toUpperCase()) === common_1.CommonTypes.Privacy.PRIVATE
|
223
|
+
? common_1.CommonTypes.Privacy.PRIVATE.toLowerCase()
|
224
|
+
: common_1.CommonTypes.Privacy.NONE.toLowerCase(),
|
225
|
+
action: common_1.CommonTypes.HeaderModifierAction.ADD
|
226
|
+
},
|
227
|
+
(0, utils_1.createRemotePartyId)(trunk, policy.number),
|
228
|
+
(0, utils_1.createPAssertedIdentity)(req, trunk, policy.number),
|
229
|
+
yield (0, utils_1.createTrunkAuthentication)(trunk)
|
230
|
+
]
|
231
|
+
};
|
232
|
+
});
|
233
|
+
}
|
234
|
+
// eslint-disable-next-line require-jsdoc
|
235
|
+
function agentToPeer(location, callee, req) {
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
237
|
+
const backend = {
|
238
|
+
ref: callee.ref,
|
239
|
+
balancingAlgorithm: callee.balancingAlgorithm,
|
240
|
+
withSessionAffinity: callee.withSessionAffinity
|
241
|
+
};
|
242
|
+
return (yield location.findRoutes({
|
243
|
+
aor: callee.aor,
|
244
|
+
callId: req.ref,
|
245
|
+
backend
|
246
|
+
}))[0];
|
247
|
+
});
|
248
|
+
}
|
249
|
+
// eslint-disable-next-line require-jsdoc
|
250
|
+
function peerToPSTN(apiClient, req) {
|
251
|
+
return __awaiter(this, void 0, void 0, function* () {
|
252
|
+
const numberTel = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_NUMBER);
|
253
|
+
const privacy = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_PRIVACY);
|
254
|
+
const number = yield (0, utils_1.findNumberByTelUrl)(apiClient, `tel:${numberTel}`);
|
255
|
+
if (!number) {
|
256
|
+
throw new Error(`no Number found for tel: ${numberTel}`);
|
257
|
+
}
|
258
|
+
if (!number.trunk) {
|
259
|
+
// TODO: Create custom error
|
260
|
+
throw new Error(`no trunk associated with Number ref: ${number.ref}`);
|
261
|
+
}
|
262
|
+
const uri = (0, utils_1.getTrunkURI)(number.trunk);
|
134
263
|
return {
|
135
264
|
user: uri.user,
|
136
265
|
host: uri.host,
|
@@ -148,14 +277,14 @@ function toPSTN(dataAPI, req, caller, calleeNumber) {
|
|
148
277
|
},
|
149
278
|
{
|
150
279
|
name: "Privacy",
|
151
|
-
value: (
|
152
|
-
? common_1.CommonTypes.Privacy.PRIVATE
|
153
|
-
: common_1.CommonTypes.Privacy.NONE,
|
280
|
+
value: (privacy === null || privacy === void 0 ? void 0 : privacy.toLocaleLowerCase()) === common_1.CommonTypes.Privacy.PRIVATE
|
281
|
+
? common_1.CommonTypes.Privacy.PRIVATE.toLowerCase()
|
282
|
+
: common_1.CommonTypes.Privacy.NONE.toLowerCase(),
|
154
283
|
action: common_1.CommonTypes.HeaderModifierAction.ADD
|
155
284
|
},
|
156
|
-
(0, utils_1.createRemotePartyId)(trunk, number),
|
157
|
-
(0, utils_1.createPAssertedIdentity)(req, trunk, number),
|
158
|
-
yield (0, utils_1.createTrunkAuthentication)(
|
285
|
+
(0, utils_1.createRemotePartyId)(number.trunk, number),
|
286
|
+
(0, utils_1.createPAssertedIdentity)(req, number.trunk, number),
|
287
|
+
yield (0, utils_1.createTrunkAuthentication)(number.trunk)
|
159
288
|
]
|
160
289
|
};
|
161
290
|
});
|
package/dist/runner.js
CHANGED
@@ -3,10 +3,9 @@
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
5
5
|
};
|
6
|
-
var _a;
|
7
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
8
7
|
/*
|
9
|
-
* Copyright (C)
|
8
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
10
9
|
* http://github.com/fonoster/routr
|
11
10
|
*
|
12
11
|
* This file is part of Routr
|
@@ -27,9 +26,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
26
|
require("./tracer").init("dispatcher");
|
28
27
|
const service_1 = __importDefault(require("./service"));
|
29
28
|
const common_1 = require("@routr/common");
|
29
|
+
const envs_1 = require("./envs");
|
30
30
|
common_1.Assertions.assertEnvsAreSet(["LOCATION_ADDR", "API_ADDR"]);
|
31
31
|
(0, service_1.default)({
|
32
|
-
bindAddr:
|
33
|
-
locationAddr:
|
34
|
-
apiAddr:
|
32
|
+
bindAddr: envs_1.BIND_ADDR,
|
33
|
+
locationAddr: envs_1.LOCATION_ADDR,
|
34
|
+
apiAddr: envs_1.API_ADDR
|
35
35
|
});
|
package/dist/service.js
CHANGED
@@ -59,10 +59,11 @@ function ConnectProcessor(config) {
|
|
59
59
|
// Remove the proxy via before forwarding response
|
60
60
|
return res.send(processor_1.Alterations.removeTopVia(req));
|
61
61
|
}
|
62
|
-
switch (req.method
|
62
|
+
switch (req.method) {
|
63
63
|
case common_1.Method.PUBLISH:
|
64
64
|
case common_1.Method.NOTIFY:
|
65
65
|
case common_1.Method.SUBSCRIBE:
|
66
|
+
case common_1.Method.MESSAGE:
|
66
67
|
res.sendMethodNotAllowed();
|
67
68
|
break;
|
68
69
|
case common_1.Method.REGISTER:
|
@@ -70,15 +71,15 @@ function ConnectProcessor(config) {
|
|
70
71
|
(0, handlers_1.handleRegistry)(req, res);
|
71
72
|
}
|
72
73
|
else {
|
73
|
-
(0, handlers_1.handleRegister)(common_2.CommonConnect.
|
74
|
+
(0, handlers_1.handleRegister)(common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }), location)(req, res);
|
74
75
|
}
|
75
76
|
break;
|
76
77
|
case common_1.Method.BYE:
|
77
78
|
case common_1.Method.ACK:
|
78
|
-
res.send((0, tailor_1.tailor)(
|
79
|
+
res.send((0, tailor_1.tailor)(location_1.Helper.createRouteFromLastMessage(req), req));
|
79
80
|
break;
|
80
81
|
default:
|
81
|
-
|
82
|
+
(0, handlers_1.handleRequest)(location, common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }))(req, res);
|
82
83
|
}
|
83
84
|
}));
|
84
85
|
}
|
package/dist/tailor.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.tailor = void 0;
|
4
4
|
/*
|
5
|
-
* Copyright (C)
|
5
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
6
6
|
* http://github.com/fonoster/routr
|
7
7
|
*
|
8
8
|
* This file is part of Routr
|
@@ -22,7 +22,5 @@ exports.tailor = void 0;
|
|
22
22
|
const processor_1 = require("@routr/processor");
|
23
23
|
const function_1 = require("fp-ts/function");
|
24
24
|
// Q: Should we add support for strict routing?
|
25
|
-
const tailor = (route, req) => (0, function_1.pipe)(req, processor_1.Alterations.
|
26
|
-
// Add updateContact for SIP.js support
|
27
|
-
);
|
25
|
+
const tailor = (route, req) => (0, function_1.pipe)(req, processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeSelfRoutes, processor_1.Alterations.removeXEdgePortRef, processor_1.Alterations.fixInvalidContact, processor_1.Alterations.fixRequestURI(route), processor_1.Alterations.addSelfVia(route), processor_1.Alterations.applyXHeaders(route), processor_1.Alterations.addSelfRecordRoute(route), processor_1.Alterations.addRouteToNextHop(route));
|
28
26
|
exports.tailor = tailor;
|
package/dist/tracer.js
CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.init = void 0;
|
7
7
|
/*
|
8
|
-
* Copyright (C)
|
8
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
9
9
|
* http://github.com/fonoster/routr
|
10
10
|
*
|
11
11
|
* This file is part of Routr
|
package/dist/types.d.ts
CHANGED
package/dist/types.js
CHANGED
@@ -6,6 +6,8 @@ var RoutingDirection;
|
|
6
6
|
RoutingDirection["FROM_PSTN"] = "from-pstn";
|
7
7
|
RoutingDirection["AGENT_TO_AGENT"] = "agent-to-agent";
|
8
8
|
RoutingDirection["AGENT_TO_PSTN"] = "agent-to-pstn";
|
9
|
+
RoutingDirection["AGENT_TO_PEER"] = "agent-to-peer";
|
10
|
+
// WARNING: This is not supported yet
|
9
11
|
RoutingDirection["PEER_TO_AGENT"] = "peer-to-agent";
|
10
12
|
RoutingDirection["PEER_TO_PSTN"] = "peer-to-pstn";
|
11
13
|
RoutingDirection["UNKNOWN"] = "unknown";
|
package/dist/utils.d.ts
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
import { HeaderModifier, MessageRequest, Transport, CommonConnect as CC } from "@routr/common";
|
1
|
+
import { HeaderModifier, MessageRequest, Transport, CommonConnect as CC, Verifier as V } from "@routr/common";
|
2
2
|
import { RoutingDirection } from "./types";
|
3
|
-
export declare const isKind: (res: CC.
|
4
|
-
export declare const findDomain: (
|
5
|
-
export declare const
|
6
|
-
export declare const
|
7
|
-
export declare const
|
8
|
-
export declare const
|
9
|
-
export declare const
|
10
|
-
export declare const
|
11
|
-
export declare const
|
12
|
-
export declare const getTrunkURI: (trunk: CC.Resource) => {
|
3
|
+
export declare const isKind: (res: CC.RoutableResourceUnion, kind: CC.Kind) => boolean;
|
4
|
+
export declare const findDomain: (apiClient: CC.APIClient, domainUri: string) => Promise<CC.Domain>;
|
5
|
+
export declare const findNumberByTelUrl: (apiClient: CC.APIClient, telUrl: string) => Promise<CC.INumber>;
|
6
|
+
export declare const findResource: (apiClient: CC.APIClient, domainUri: string, userpart: string) => Promise<CC.RoutableResourceUnion>;
|
7
|
+
export declare const getRoutingDirection: (caller: CC.RoutableResourceUnion, callee: CC.RoutableResourceUnion) => RoutingDirection;
|
8
|
+
export declare const createPAssertedIdentity: (req: MessageRequest, trunk: CC.Trunk, number: CC.INumber) => HeaderModifier;
|
9
|
+
export declare const createRemotePartyId: (trunk: CC.Trunk, number: CC.INumber) => HeaderModifier;
|
10
|
+
export declare const createTrunkAuthentication: (trunk: CC.Trunk) => Promise<HeaderModifier>;
|
11
|
+
export declare const getTrunkURI: (trunk: CC.Trunk) => {
|
13
12
|
host: string;
|
14
13
|
port: number;
|
15
14
|
user: string;
|
@@ -19,3 +18,7 @@ export declare const getSIPURI: (uri: {
|
|
19
18
|
user?: string;
|
20
19
|
host: string;
|
21
20
|
}) => string;
|
21
|
+
export declare const hasXConnectObjectHeader: (req: MessageRequest) => string;
|
22
|
+
export declare const getVerifierImpl: () => {
|
23
|
+
verify: (token: string) => Promise<import("@routr/common/dist/errors").ServiceUnavailableError | V.VerifyResponse>;
|
24
|
+
};
|
package/dist/utils.js
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
@@ -8,10 +31,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
8
31
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
32
|
});
|
10
33
|
};
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
36
|
+
};
|
11
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
-
exports.getSIPURI = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.
|
38
|
+
exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.getSIPURI = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
|
13
39
|
/*
|
14
|
-
* Copyright (C)
|
40
|
+
* Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
|
15
41
|
* http://github.com/fonoster/routr
|
16
42
|
*
|
17
43
|
* This file is part of Routr
|
@@ -28,64 +54,67 @@ exports.getSIPURI = exports.getTrunkURI = exports.createTrunkAuthentication = ex
|
|
28
54
|
* See the License for the specific language governing permissions and
|
29
55
|
* limitations under the License.
|
30
56
|
*/
|
57
|
+
const jwt = __importStar(require("jsonwebtoken"));
|
58
|
+
const fs_1 = __importDefault(require("fs"));
|
31
59
|
const common_1 = require("@routr/common");
|
60
|
+
const processor_1 = require("@routr/processor");
|
32
61
|
const types_1 = require("./types");
|
62
|
+
const envs_1 = require("./envs");
|
63
|
+
// OMG, this is so ugly and hacky
|
33
64
|
const isKind = (res, kind) => {
|
34
65
|
if (res == null && kind === common_1.CommonConnect.Kind.UNKNOWN) {
|
35
66
|
return true;
|
36
67
|
}
|
37
|
-
|
68
|
+
else if (res == null) {
|
69
|
+
return false;
|
70
|
+
}
|
71
|
+
else if ("privacy" in res && kind === common_1.CommonConnect.Kind.AGENT) {
|
72
|
+
return true;
|
73
|
+
}
|
74
|
+
else if ("telUrl" in res && kind === common_1.CommonConnect.Kind.NUMBER) {
|
75
|
+
return true;
|
76
|
+
}
|
77
|
+
else if ("username" in res && kind === common_1.CommonConnect.Kind.PEER) {
|
78
|
+
return true;
|
79
|
+
}
|
38
80
|
};
|
39
81
|
exports.isKind = isKind;
|
40
|
-
const findDomain = (
|
41
|
-
return (yield
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
domainUri
|
46
|
-
}
|
47
|
-
}))[0];
|
82
|
+
const findDomain = (apiClient, domainUri) => __awaiter(void 0, void 0, void 0, function* () {
|
83
|
+
return (yield apiClient.domains.findBy({
|
84
|
+
fieldName: "domainUri",
|
85
|
+
fieldValue: domainUri
|
86
|
+
})).items[0];
|
48
87
|
});
|
49
88
|
exports.findDomain = findDomain;
|
50
|
-
const
|
51
|
-
return (yield
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
requestUri
|
56
|
-
}
|
57
|
-
}))[0];
|
58
|
-
});
|
59
|
-
exports.findTrunkByRequestURI = findTrunkByRequestURI;
|
60
|
-
const findNumberByTelUrl = (dataAPI, telUrl) => __awaiter(void 0, void 0, void 0, function* () {
|
61
|
-
return (yield dataAPI.findBy({
|
62
|
-
kind: common_1.CommonConnect.Kind.NUMBER,
|
63
|
-
criteria: common_1.CommonConnect.FindCriteria.FIND_NUMBER_BY_TELURL,
|
64
|
-
parameters: {
|
65
|
-
telUrl
|
66
|
-
}
|
67
|
-
}))[0];
|
89
|
+
const findNumberByTelUrl = (apiClient, telUrl) => __awaiter(void 0, void 0, void 0, function* () {
|
90
|
+
return (yield apiClient.numbers.findBy({
|
91
|
+
fieldName: "telUrl",
|
92
|
+
fieldValue: telUrl
|
93
|
+
})).items[0];
|
68
94
|
});
|
69
95
|
exports.findNumberByTelUrl = findNumberByTelUrl;
|
70
|
-
const findResource = (
|
71
|
-
const domain = yield (0, exports.findDomain)(
|
72
|
-
//
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
}))[0]
|
83
|
-
: res;
|
84
|
-
if ((0, exports.isKind)(res, common_1.CommonConnect.Kind.AGENT) && res.spec.domainRef != (domain === null || domain === void 0 ? void 0 : domain.ref)) {
|
96
|
+
const findResource = (apiClient, domainUri, userpart) => __awaiter(void 0, void 0, void 0, function* () {
|
97
|
+
const domain = yield (0, exports.findDomain)(apiClient, domainUri);
|
98
|
+
// First, try to find a number
|
99
|
+
const number = yield (0, exports.findNumberByTelUrl)(apiClient, `tel:${userpart}`);
|
100
|
+
if (number != null)
|
101
|
+
return number;
|
102
|
+
// Next, try to find an agent
|
103
|
+
const agent = (yield apiClient.agents.findBy({
|
104
|
+
fieldName: "username",
|
105
|
+
fieldValue: userpart
|
106
|
+
})).items[0];
|
107
|
+
if (agent && agent.domain.ref != (domain === null || domain === void 0 ? void 0 : domain.ref)) {
|
85
108
|
// Not in the same domain
|
86
109
|
return null;
|
87
110
|
}
|
88
|
-
|
111
|
+
if (agent != null)
|
112
|
+
return agent;
|
113
|
+
// Next, try to find a peer
|
114
|
+
return (yield apiClient.peers.findBy({
|
115
|
+
fieldName: "username",
|
116
|
+
fieldValue: userpart
|
117
|
+
})).items[0];
|
89
118
|
});
|
90
119
|
exports.findResource = findResource;
|
91
120
|
const getRoutingDirection = (caller, callee) => {
|
@@ -98,6 +127,9 @@ const getRoutingDirection = (caller, callee) => {
|
|
98
127
|
if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.PEER) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.AGENT)) {
|
99
128
|
return types_1.RoutingDirection.PEER_TO_AGENT;
|
100
129
|
}
|
130
|
+
if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.AGENT) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.PEER)) {
|
131
|
+
return types_1.RoutingDirection.AGENT_TO_PEER;
|
132
|
+
}
|
101
133
|
// All we know is that the Number is managed by this instance of Routr
|
102
134
|
if ((0, exports.isKind)(callee, common_1.CommonConnect.Kind.NUMBER)) {
|
103
135
|
return types_1.RoutingDirection.FROM_PSTN;
|
@@ -110,7 +142,7 @@ const getRoutingDirection = (caller, callee) => {
|
|
110
142
|
exports.getRoutingDirection = getRoutingDirection;
|
111
143
|
const createPAssertedIdentity = (req, trunk, number) => {
|
112
144
|
const displayName = req.message.from.address.displayName;
|
113
|
-
const remoteNumber = number.
|
145
|
+
const remoteNumber = number.telUrl.split(":")[1];
|
114
146
|
const trunkHost = (0, exports.getTrunkURI)(trunk).host;
|
115
147
|
return {
|
116
148
|
name: "P-Asserted-Identity",
|
@@ -122,7 +154,7 @@ const createPAssertedIdentity = (req, trunk, number) => {
|
|
122
154
|
};
|
123
155
|
exports.createPAssertedIdentity = createPAssertedIdentity;
|
124
156
|
const createRemotePartyId = (trunk, number) => {
|
125
|
-
const remoteNumber = number.
|
157
|
+
const remoteNumber = number.telUrl.split(":")[1];
|
126
158
|
const trunkHost = (0, exports.getTrunkURI)(trunk).host;
|
127
159
|
return {
|
128
160
|
name: "Remote-Party-ID",
|
@@ -131,29 +163,44 @@ const createRemotePartyId = (trunk, number) => {
|
|
131
163
|
};
|
132
164
|
};
|
133
165
|
exports.createRemotePartyId = createRemotePartyId;
|
134
|
-
const createTrunkAuthentication = (
|
166
|
+
const createTrunkAuthentication = (trunk) => __awaiter(void 0, void 0, void 0, function* () {
|
135
167
|
var _a, _b;
|
136
|
-
const credentials = yield dataAPI.get(trunk.spec.outbound.credentialsRef);
|
137
168
|
return {
|
138
169
|
name: common_1.CommonTypes.ExtraHeader.GATEWAY_AUTH,
|
139
|
-
value: Buffer.from(`${(_a =
|
170
|
+
value: Buffer.from(`${(_a = trunk.outboundCredentials) === null || _a === void 0 ? void 0 : _a.username}:${(_b = trunk.outboundCredentials) === null || _b === void 0 ? void 0 : _b.password}`).toString("base64"),
|
140
171
|
action: common_1.CommonTypes.HeaderModifierAction.ADD
|
141
172
|
};
|
142
173
|
});
|
143
174
|
exports.createTrunkAuthentication = createTrunkAuthentication;
|
144
175
|
const getTrunkURI = (trunk) => {
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
: Object.values(common_1.Transport)[Object.values(common_1.Transport).indexOf(transport.toLowerCase())];
|
176
|
+
if (!trunk.uris) {
|
177
|
+
throw new Error(`trunk ${trunk.ref} has no outbound settings`);
|
178
|
+
}
|
179
|
+
const { user, host, port, transport } = trunk.uris[0];
|
150
180
|
return {
|
151
181
|
user,
|
152
182
|
host,
|
153
183
|
port: port !== null && port !== void 0 ? port : 5060,
|
154
|
-
transport:
|
184
|
+
transport: transport !== null && transport !== void 0 ? transport : common_1.Transport.UDP
|
155
185
|
};
|
156
186
|
};
|
157
187
|
exports.getTrunkURI = getTrunkURI;
|
158
188
|
const getSIPURI = (uri) => uri.user ? `sip:${uri.user}@${uri.host}` : `sip:${uri.host}`;
|
159
189
|
exports.getSIPURI = getSIPURI;
|
190
|
+
const hasXConnectObjectHeader = (req) => processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
|
191
|
+
exports.hasXConnectObjectHeader = hasXConnectObjectHeader;
|
192
|
+
const getVerifierImpl = () => {
|
193
|
+
if (envs_1.CONNECT_VERIFIER_PUBLIC_KEY_PATH) {
|
194
|
+
const publicKey = fs_1.default.readFileSync(envs_1.CONNECT_VERIFIER_PUBLIC_KEY_PATH, "utf8");
|
195
|
+
return {
|
196
|
+
verify: (token) => __awaiter(void 0, void 0, void 0, function* () {
|
197
|
+
return jwt.verify(token, publicKey, envs_1.CONNECT_VERIFIER_OPTIONS);
|
198
|
+
})
|
199
|
+
};
|
200
|
+
}
|
201
|
+
else if (envs_1.CONNECT_VERIFIER_ADDR) {
|
202
|
+
return { verify: common_1.Verifier.verifier(envs_1.CONNECT_VERIFIER_ADDR) };
|
203
|
+
}
|
204
|
+
return null;
|
205
|
+
};
|
206
|
+
exports.getVerifierImpl = getVerifierImpl;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@routr/connect",
|
3
|
-
"version": "2.0.
|
3
|
+
"version": "2.0.9",
|
4
4
|
"description": "Default processor",
|
5
5
|
"author": "Pedro Sanders <psanders@fonoster.com>",
|
6
6
|
"homepage": "https://github.com/fonoster/routr#readme",
|
@@ -19,7 +19,7 @@
|
|
19
19
|
"run_connect": "dist/runner.js"
|
20
20
|
},
|
21
21
|
"dependencies": {
|
22
|
-
"@fonoster/logger": "0.3.
|
22
|
+
"@fonoster/logger": "0.3.20",
|
23
23
|
"@opentelemetry/api": "^1.0.4",
|
24
24
|
"@opentelemetry/exporter-jaeger": "^1.0.4",
|
25
25
|
"@opentelemetry/instrumentation": "^0.27.0",
|
@@ -28,9 +28,13 @@
|
|
28
28
|
"@opentelemetry/sdk-trace-base": "^1.0.4",
|
29
29
|
"@opentelemetry/sdk-trace-node": "^1.0.4",
|
30
30
|
"@opentelemetry/semantic-conventions": "^1.0.4",
|
31
|
-
"@routr/common": "^2.0.
|
32
|
-
"@routr/location": "^2.0.
|
33
|
-
"@routr/processor": "^2.0.
|
31
|
+
"@routr/common": "^2.0.9",
|
32
|
+
"@routr/location": "^2.0.9",
|
33
|
+
"@routr/processor": "^2.0.9",
|
34
|
+
"jsonwebtoken": "^9.0.0"
|
35
|
+
},
|
36
|
+
"devDependencies": {
|
37
|
+
"@types/jsonwebtoken": "^9.0.1"
|
34
38
|
},
|
35
39
|
"files": [
|
36
40
|
"dist"
|
@@ -45,5 +49,5 @@
|
|
45
49
|
"bugs": {
|
46
50
|
"url": "https://github.com/fonoster/routr/issues"
|
47
51
|
},
|
48
|
-
"gitHead": "
|
52
|
+
"gitHead": "62eb05f0b3bfa05d84c9ce4b4644de5fb5453295"
|
49
53
|
}
|