@routr/connect 2.0.8-alpha.4 → 2.0.8-alpha.41

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