@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 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
- dataAPI: CC.DataAPI;
4
+ apiClient: CC.APIClient;
5
5
  request: MessageRequest;
6
- caller: CC.Resource;
7
- callee: CC.Resource;
6
+ caller: CC.RoutableResourceUnion;
7
+ callee: CC.RoutableResourceUnion;
8
8
  routingDirection: RoutingDirection;
9
9
  }) => Promise<Record<string, unknown>>;
10
- export declare const checkAgentAccess: (dataAPI: CC.DataAPI, request: MessageRequest, caller: CC.Resource) => Promise<{
10
+ export declare const checkAgentOrPeerAccess: (request: MessageRequest, caller: CC.RoutableResourceUnion) => Promise<{
11
11
  message: {
12
- responseType: number;
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: (dataAPI: CC.DataAPI, request: MessageRequest, callee: CC.Resource) => Promise<{
25
+ export declare const checkAccessFromPSTN: (apiClient: CC.APIClient, request: MessageRequest, callee: CC.INumber) => Promise<{
25
26
  message: {
26
- responseType: number;
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.checkAgentAccess = exports.checkAccess = void 0;
12
+ exports.checkAccessFromPSTN = exports.checkAgentOrPeerAccess = exports.checkAccess = 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
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 { dataAPI, request, caller, callee, routingDirection } = accessRequest;
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.checkAgentAccess)(dataAPI, request, caller);
41
+ return (0, exports.checkAgentOrPeerAccess)(request, caller);
43
42
  case types_1.RoutingDirection.FROM_PSTN:
44
- return (0, exports.checkAccessFromPSTN)(dataAPI, request, callee);
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 checkAgentAccess = (dataAPI, request, caller) => __awaiter(void 0, void 0, void 0, function* () {
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 = yield dataAPI.get(caller.spec.credentialsRef);
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.spec.credentials.username,
59
- secret: credentials === null || credentials === void 0 ? void 0 : credentials.spec.credentials.password
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.checkAgentAccess = checkAgentAccess;
70
- const checkAccessFromPSTN = (dataAPI, request, callee) => __awaiter(void 0, void 0, void 0, function* () {
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 (0, utils_1.findTrunkByRequestURI)(dataAPI, request.message.requestUri.host);
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.spec.trunkRef !== trunk.ref) {
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 ((_a = trunk.spec.inbound) === null || _a === void 0 ? void 0 : _a.accessControlListRef) {
83
+ if (trunk.accessControlList) {
83
84
  try {
84
- const acl = yield dataAPI.get(trunk.spec.inbound.accessControlListRef);
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 ((_b = trunk.spec.inbound) === null || _b === void 0 ? void 0 : _b.credentialsRef) {
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: credentials === null || credentials === void 0 ? void 0 : credentials.spec.credentials.username,
115
- secret: credentials === null || credentials === void 0 ? void 0 : credentials.spec.credentials.password
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) 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
@@ -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.UNKNOWN;
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
2
  import { ILocationService } from "@routr/location";
3
3
  import { CommonConnect as CC } from "@routr/common";
4
- export declare const handleRegister: (dataAPI: CC.DataAPI, 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
@@ -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 handleRegister = (dataAPI, location) => {
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 agent = yield (0, utils_1.findResource)(dataAPI, fromURI.host, fromURI.user);
46
- if (!agent) {
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 = yield dataAPI.get(agent === null || agent === void 0 ? void 0 : agent.spec.credentialsRef);
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.spec.credentials.username,
53
- secret: credentials === null || credentials === void 0 ? void 0 : credentials.spec.credentials.password
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
- return res.send(common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host));
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 = processor_1.Helper.createRouteFromLastMessage(req);
73
- 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));
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, 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* () {
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
- ? processor_1.Helper.createRouteFromLastMessage(req)
81
- : yield (0, router_1.router)(location, dataAPI)(req);
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 (!("user" in route)) {
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.addRouteToPeerEdgePort(route), processor_1.Alterations.addXEdgePortRef, processor_1.Alterations.decreaseMaxForwards);
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
- res.sendError(err);
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, dataAPI: CC.DataAPI): (request: MessageRequest) => Promise<Route | Record<string, unknown>>;
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
@@ -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, dataAPI) {
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), { fromURI: (0, utils_1.getSIPURI)(fromURI), requestURI: (0, utils_1.getSIPURI)(requestURI) });
49
- const caller = yield (0, utils_1.findResource)(dataAPI, fromURI.host, fromURI.user);
50
- const callee = yield (0, utils_1.findResource)(dataAPI, requestURI.host, requestURI.user);
51
- const routingDirection = (0, utils_1.getRoutingDirection)(caller, callee);
52
- if (request.method === common_1.CommonTypes.Method.INVITE) {
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
- dataAPI,
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
- return agentToAgent(location, request);
67
- case types_1.RoutingDirection.AGENT_TO_PSTN:
68
- return yield toPSTN(dataAPI, request, caller, requestURI.user);
69
- case types_1.RoutingDirection.FROM_PSTN:
70
- return yield fromPSTN(location, dataAPI, callee);
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, _, callee) {
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.spec.location.aorLink
165
+ aor: callee.aorLink,
166
+ callId: req.ref,
167
+ sessionAffinityRef,
168
+ backend
96
169
  }))[0];
97
170
  if (!route) {
98
- throw new location_1.NotRoutesFoundForAOR(callee.spec.location.aorLink);
171
+ return null;
99
172
  }
100
173
  if (!route.headers)
101
174
  route.headers = [];
102
- (_a = callee.spec.location.props) === null || _a === void 0 ? void 0 : _a.forEach((prop) => {
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 toPSTN(dataAPI, req, caller, calleeNumber) {
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
- const domain = yield dataAPI.get(caller.spec.domainRef);
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 = (_a = domain.spec.context.egressPolicies) === null || _a === void 0 ? void 0 : _a.find((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 number = yield dataAPI.get(policy === null || policy === void 0 ? void 0 : policy.numberRef);
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
- // TODO: Create custom error
131
- throw new Error(`no trunk associated with Number ref: ${number === null || number === void 0 ? void 0 : number.ref}`);
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: ((_b = caller.spec.privacy) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === common_1.CommonTypes.Privacy.PRIVATE
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)(dataAPI, trunk)
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) 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)(common_2.CommonConnect.dataAPI(config.apiAddr), 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.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) 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
@@ -3,6 +3,7 @@ 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
@@ -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.Resource, kind: CC.Kind) => boolean;
4
- export declare const findDomain: (dataAPI: CC.DataAPI, domainUri: string) => Promise<CC.Resource>;
5
- export declare const findTrunkByRequestURI: (dataAPI: CC.DataAPI, requestUri: string) => Promise<CC.Resource>;
6
- export declare const findNumberByTelUrl: (dataAPI: CC.DataAPI, telUrl: string) => Promise<CC.Resource>;
7
- export declare const findResource: (dataAPI: CC.DataAPI, domainUri: string, userpart: string) => Promise<CC.Resource>;
8
- export declare const getRoutingDirection: (caller: CC.Resource, callee: CC.Resource) => RoutingDirection;
9
- export declare const createPAssertedIdentity: (req: MessageRequest, trunk: CC.Resource, number: CC.Resource) => HeaderModifier;
10
- export declare const createRemotePartyId: (trunk: CC.Resource, number: CC.Resource) => HeaderModifier;
11
- export declare const createTrunkAuthentication: (dataAPI: CC.DataAPI, trunk: CC.Resource) => Promise<HeaderModifier>;
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.findTrunkByRequestURI = 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,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
- return (res === null || res === void 0 ? void 0 : res.kind.toLowerCase()) === kind;
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 = (dataAPI, domainUri) => __awaiter(void 0, void 0, void 0, function* () {
41
- return (yield dataAPI.findBy({
42
- kind: common_1.CommonConnect.Kind.DOMAIN,
43
- criteria: common_1.CommonConnect.FindCriteria.FIND_DOMAIN_BY_DOMAINURI,
44
- parameters: {
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 findTrunkByRequestURI = (dataAPI, requestUri) => __awaiter(void 0, void 0, void 0, function* () {
51
- return (yield dataAPI.findBy({
52
- kind: common_1.CommonConnect.Kind.TRUNK,
53
- criteria: common_1.CommonConnect.FindCriteria.FIND_TRUNK_BY_REQUEST_URI,
54
- parameters: {
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 = (dataAPI, domainUri, userpart) => __awaiter(void 0, void 0, void 0, function* () {
71
- const domain = yield (0, exports.findDomain)(dataAPI, domainUri);
72
- // TODO: Fix jsonpath not working for logical AND and OR
73
- let res = yield (0, exports.findNumberByTelUrl)(dataAPI, `tel:${userpart}`);
74
- res =
75
- res == null
76
- ? (yield dataAPI.findBy({
77
- kind: common_1.CommonConnect.Kind.AGENT,
78
- criteria: common_1.CommonConnect.FindCriteria.FIND_AGENT_BY_USERNAME,
79
- parameters: {
80
- username: userpart
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
- 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];
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.spec.location.telUrl.split(":")[1];
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.spec.location.telUrl.split(":")[1];
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 = (dataAPI, trunk) => __awaiter(void 0, void 0, void 0, function* () {
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 = 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"),
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
- var _a;
146
- const { user, host, port, transport } = (_a = trunk.spec.outbound) === null || _a === void 0 ? void 0 : _a.uris[0].uri;
147
- const t = !transport
148
- ? common_1.Transport.UDP
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: t
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.8-alpha.8",
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.18",
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.8-alpha.8",
32
- "@routr/location": "^2.0.8-alpha.8",
33
- "@routr/processor": "^2.0.8-alpha.8"
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": "bcc116fba248b34f661d88e425e5060f409be77c"
52
+ "gitHead": "62eb05f0b3bfa05d84c9ce4b4644de5fb5453295"
49
53
  }