sipgate-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 - 2026-08-30
4
+
5
+ - Default to MCP-level user scope resolved from sipgate's authenticated user.
6
+ - Restrict user-scoped users, numbers, devices, phonelines, settings, and call
7
+ history to the authenticated user's resources.
8
+ - Validate user-scoped write targets and outbound caller identities before
9
+ sending changes or chargeable actions to sipgate.
10
+ - Avoid account-wide number and active-call snapshots in user-scoped actions.
11
+ - Add explicit account scope that requires a verified sipgate administrator.
12
+ - Advertise active scope and read-only behavior through MCP server instructions.
13
+
14
+ ## 0.1.0 - 2026-08-30
15
+
16
+ - Initial local stdio MCP server with focused sipgate read and write tools.
17
+ - Personal Access Token authentication and optional read-only mode.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  `sipgate-mcp` is an open-source, self-hosted Model Context Protocol server for inspecting and configuring a sipgate account. It exposes the sipgate REST API v2 as focused tools for agents such as Claude Code, Claude Desktop, and Codex.
4
4
 
5
+ Version 0.2 defaults to user-scoped access: tools are constrained to the
6
+ authenticated sipgate user's resources. Account-wide access is an explicit
7
+ administrator-only mode.
8
+
5
9
  The server uses stdio only. It does not start an HTTP server or route credentials
6
10
  through a third-party service. It sends authentication only from the local MCP
7
11
  process directly to `https://api.sipgate.com/v2`.
@@ -12,8 +16,7 @@ process directly to `https://api.sipgate.com/v2`.
12
16
  - A sipgate account with a Personal Access Token (PAT)
13
17
  - An MCP client with stdio support
14
18
 
15
- After the first npm release, install the command globally with any of the
16
- supported package managers:
19
+ Install the command globally with any of the supported package managers:
17
20
 
18
21
  ```bash
19
22
  npm install --global sipgate-mcp
@@ -26,13 +29,12 @@ Then start it with:
26
29
  ```bash
27
30
  export SIPGATE_TOKEN_ID="your-token-id"
28
31
  export SIPGATE_TOKEN="your-token"
32
+ export SIPGATE_MCP_SCOPE="user"
29
33
  sipgate-mcp
30
34
  ```
31
35
 
32
36
  For clients that manage MCP commands on demand, `npx -y sipgate-mcp` remains
33
- supported without a global installation. The package name was available when
34
- checked on 2026-08-30; this repository is configured for `sipgate-mcp`, but the
35
- initial npm publish still needs to be completed.
37
+ supported without a global installation.
36
38
 
37
39
  ## Create a Personal Access Token
38
40
 
@@ -45,30 +47,58 @@ sipgate PAT authentication uses HTTP Basic Auth with `token-id:token` as the cre
45
47
 
46
48
  Do not put either value in this repository, an MCP config committed to source control, command output, or an issue report.
47
49
 
50
+ ## MCP access scopes
51
+
52
+ `SIPGATE_MCP_SCOPE` controls the resource boundary enforced by the MCP in
53
+ addition to sipgate's own user role and PAT scopes:
54
+
55
+ | Value | Behavior |
56
+ | --- | --- |
57
+ | `user` (default) | Resolves the authenticated user through `/authorization/userinfo`; returns only that user and their assigned numbers; forces user-specific device, routing, and settings reads; constrains call history to owned connection IDs; and validates every write target against owned numbers, phonelines, or devices. |
58
+ | `account` | Enables account-wide reads and writes. Startup fails unless `/users/{authenticatedUserId}` reports `admin: true`. Requires `users:read` for the administrator check. |
59
+
60
+ Token scopes are permission ceilings, not role elevation. For example,
61
+ `numbers:write` does not turn a regular sipgate user into an administrator.
62
+ The effective permission is the intersection of the sipgate user role, PAT
63
+ scopes, MCP access scope, and read-only mode.
64
+
65
+ Use account scope only when account-wide administration is intended:
66
+
67
+ ```bash
68
+ export SIPGATE_MCP_SCOPE="account"
69
+ npx -y sipgate-mcp
70
+ ```
71
+
48
72
  ## Tools and PAT scopes
49
73
 
50
- The table lists the non-`all` scopes named by sipgate's live Swagger document, plus scopes required by this server's pre/post state reads. sipgate also exposes broader parent scopes such as `sessions:write`; select the listed specific and parent scopes offered by the PAT UI when in doubt.
74
+ Every mode identifies the authenticated user with `GET /authorization/userinfo`.
75
+ The table lists the non-`all` scopes named by sipgate's live Swagger document,
76
+ including ownership checks performed in user scope and pre/post state reads.
77
+ sipgate also exposes broader parent scopes such as `sessions:write`; select the
78
+ listed specific and parent scopes offered by the PAT UI when in doubt.
51
79
 
52
80
  | Tool | Access | sipgate API calls | PAT scopes |
53
81
  | --- | --- | --- | --- |
54
- | `account_info` | Read | `GET /account`, `GET /authorization/userinfo` | `account:read` (`userinfo` has no scope declaration in Swagger) |
55
- | `list_users` | Read | `GET /users` | `users:read` |
56
- | `list_numbers` | Read | `GET /numbers` | `numbers:read` |
57
- | `list_devices` | Read | `GET /users`, `GET /{userId}/devices` | `devices:read`; `users:read` when `user_id` is omitted |
58
- | `get_routing` | Read | `GET /numbers`, `GET /users`, `GET /{userId}/phonelines`, `GET /{userId}/phonelines/{phonelineId}/numbers`, `GET /{userId}/phonelines/{phonelineId}/forwardings` | `numbers:read`, `phonelines:read`, `phonelines:numbers:read`, `phonelines:forwardings:read`; `users:read` when `user_id` is omitted |
59
- | `call_history` | Read | `GET /history` | `history:read` |
82
+ | `account_info` | Read | User: cached `/authorization/userinfo`; account: plus `GET /account` | Account: `account:read` (`userinfo` has no scope declaration in Swagger) |
83
+ | `list_users` | Read | User: `GET /users/{self}`; account: `GET /users` | `users:read` |
84
+ | `list_numbers` | Read | User: `GET /{self}/phonelines` and each phoneline's `/numbers`; account: `GET /numbers` | User: `phonelines:read`, `phonelines:numbers:read`; account: `numbers:read` |
85
+ | `list_devices` | Read | User: `GET /{self}/devices`; account: `GET /users`, `GET /{userId}/devices` | `devices:read`; account also needs `users:read` when `user_id` is omitted |
86
+ | `get_routing` | Read | User: own phonelines, numbers, and forwardings; account: also `GET /numbers` and `GET /users` | `phonelines:read`, `phonelines:numbers:read`, `phonelines:forwardings:read`; account also needs `numbers:read` and, when `user_id` is omitted, `users:read` |
87
+ | `call_history` | Read | User: ownership reads for own phonelines/devices, then filtered `GET /history`; account: `GET /history` | `history:read`; user also needs `phonelines:read`, `devices:read` |
60
88
  | `get_settings` | Read | `GET /users[/userId]`, `GET /{userId}/devices`, `GET /{userId}/phonelines[/phonelineId]` | `users:read`, `devices:read`, `phonelines:read` |
61
- | `set_number_routing` | Write | pre/post `GET /numbers`, `PUT /numbers/{numberId}` | `numbers:read`, `numbers:write` |
62
- | `set_forwarding` | Write | pre/post `GET /{userId}/phonelines/{phonelineId}/forwardings`, `PUT` to the same path | `phonelines:read`, `phonelines:write`, `phonelines:forwardings:read`, `phonelines:forwardings:write` |
63
- | `set_dnd` | Write | pre/post `GET /devices/{deviceId}`, `PUT /devices/{deviceId}` | `devices:read`, `devices:write` |
89
+ | `set_number_routing` | Write | User: pre/post reads through own phonelines; account: pre/post `GET /numbers`; all modes: `PUT /numbers/{numberId}` | `numbers:write`; user also needs `phonelines:read`, `phonelines:numbers:read`; account needs `numbers:read` |
90
+ | `set_forwarding` | Write | User: phoneline ownership read; then pre/post forwarding reads and `PUT` | `phonelines:read`, `phonelines:write`, `phonelines:forwardings:read`, `phonelines:forwardings:write` |
91
+ | `set_dnd` | Write | User: device ownership read; then pre/post `GET /devices/{deviceId}` and `PUT` | `devices:read`, `devices:write` |
64
92
  | `send_sms` | Write/action | `GET /{userId}/sms`, pre/post `GET /history`, `POST /sessions/sms` | `sms:read`, `history:read`, `sessions:write`, `sessions:sms:write` |
65
- | `initiate_call` | Write/action | pre/post `GET /calls`, `POST /sessions/calls` | `rtcm:read`, `sessions:write`, `sessions:calls:write` |
93
+ | `initiate_call` | Write/action | User: device/number ownership reads, then `POST /sessions/calls`; account: pre/post `GET /calls` plus `POST` | `sessions:write`, `sessions:calls:write`; user also needs `devices:read`, `phonelines:read`, `phonelines:numbers:read`; account needs `rtcm:read` |
66
94
 
67
95
  Every write tool reads current state first and returns a JSON object with `before` and `after`. SMS history can update asynchronously, and `/calls` only contains established calls, so those action snapshots also include an acceptance/session marker.
68
96
 
69
97
  ### Tool notes
70
98
 
71
99
  - `list_devices` resolves devices through users because the documented account-wide route is `GET /{userId}/devices`; the live v2 Swagger document does not define `GET /devices`.
100
+ - User scope resolves assigned numbers through the authenticated user's phonelines and never calls account-wide `GET /users` or `GET /numbers` for read tools.
101
+ - User-scoped number-routing snapshots are also resolved through owned phonelines, and user-scoped Click2Dial deliberately omits account-wide `/calls` snapshots.
72
102
  - Number routing uses sipgate's documented `endpointId`. Obtain existing IDs from the read tools; a phoneline ID such as `p0` is the documented example.
73
103
  - `set_forwarding` replaces the complete phoneline forwarding list. Pass `forwardings: []` to remove all forwardings. A `timeout` of `0` represents immediate forwarding.
74
104
  - `send_sms` refuses to post unless `GET /{userId}/sms` returns the requested (or first available) SMS extension.
@@ -80,6 +110,7 @@ Set `SIPGATE_MCP_READONLY=1` to register only the seven read tools. Write tools
80
110
 
81
111
  ```bash
82
112
  export SIPGATE_MCP_READONLY=1
113
+ export SIPGATE_MCP_SCOPE=user
83
114
  npx -y sipgate-mcp
84
115
  ```
85
116
 
@@ -95,12 +126,13 @@ Claude Code expands `${VAR}` references in MCP environment entries. Single quote
95
126
  claude mcp add \
96
127
  --env 'SIPGATE_TOKEN_ID=${SIPGATE_TOKEN_ID}' \
97
128
  --env 'SIPGATE_TOKEN=${SIPGATE_TOKEN}' \
129
+ --env SIPGATE_MCP_SCOPE=user \
98
130
  --transport stdio \
99
131
  --scope user \
100
132
  sipgate -- npx -y sipgate-mcp
101
133
  ```
102
134
 
103
- Add `--env SIPGATE_MCP_READONLY=1` before `--transport` for read-only mode. Verify the connection with `claude mcp get sipgate`. See the official [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp).
135
+ Add `--env SIPGATE_MCP_READONLY=1` before `--transport` for read-only mode. Replace the scope with `account` only for deliberate administrator access. Verify the connection with `claude mcp get sipgate`. See the official [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp).
104
136
 
105
137
  ### Claude Desktop
106
138
 
@@ -127,18 +159,22 @@ Codex can forward named variables from its local environment without storing the
127
159
  [mcp_servers.sipgate]
128
160
  command = "npx"
129
161
  args = ["-y", "sipgate-mcp"]
130
- env_vars = ["SIPGATE_TOKEN_ID", "SIPGATE_TOKEN", "SIPGATE_MCP_READONLY"]
162
+ env_vars = ["SIPGATE_TOKEN_ID", "SIPGATE_TOKEN", "SIPGATE_MCP_SCOPE", "SIPGATE_MCP_READONLY"]
131
163
  ```
132
164
 
133
165
  Export the variables before starting Codex, then use `/mcp` or `codex mcp list` to confirm the server. The `env_vars` forwarding form is documented in the [official OpenAI MCP documentation](https://developers.openai.com/codex/mcp/).
134
166
 
135
167
  ## Manual smoke test with MCP Inspector
136
168
 
137
- This test makes real sipgate API calls. Start with a PAT containing only `account:read` and `numbers:read`, plus the required environment variables:
169
+ This test makes real sipgate API calls. Start in user/read-only mode with a PAT
170
+ containing `phonelines:read` and `phonelines:numbers:read`, plus the required
171
+ environment variables:
138
172
 
139
173
  ```bash
140
174
  export SIPGATE_TOKEN_ID="your-token-id"
141
175
  export SIPGATE_TOKEN="your-token"
176
+ export SIPGATE_MCP_SCOPE="user"
177
+ export SIPGATE_MCP_READONLY=1
142
178
  npx @modelcontextprotocol/inspector npx -y sipgate-mcp
143
179
  ```
144
180
 
@@ -158,10 +194,11 @@ The MCP layer depends only on the backend interface:
158
194
  ```text
159
195
  MCP stdio server
160
196
  -> validated tool definitions (Zod)
161
- -> TelephonyBackend
162
- -> SipgateBackend (v1 implementation)
163
- -> SipgateClient
164
- -> native fetch -> https://api.sipgate.com/v2
197
+ -> user/account access policy
198
+ -> TelephonyBackend
199
+ -> SipgateBackend
200
+ -> SipgateClient
201
+ -> native fetch -> https://api.sipgate.com/v2
165
202
  ```
166
203
 
167
204
  `TelephonyBackend` contains the stable, provider-neutral operations. `SipgateBackend` is the only v1 implementation, so a future second telephony provider can reuse the same MCP tool surface.
@@ -169,6 +206,8 @@ MCP stdio server
169
206
  ## Security
170
207
 
171
208
  - PAT values are read only from `SIPGATE_TOKEN_ID` and `SIPGATE_TOKEN`.
209
+ - User scope is the default and validates user IDs plus number, phoneline, device, call, and history ownership before delegation.
210
+ - Account scope fails startup unless the authenticated sipgate user reports `admin: true`.
172
211
  - The Basic Auth header exists only in memory and is sent only to the fixed sipgate API base URL.
173
212
  - API error bodies are discarded. User-facing errors never include request headers, response bodies, or credentials.
174
213
  - Potentially sensitive response properties such as `credentials`, `password`, `token`, and `secret` are redacted before tool output.
@@ -184,7 +223,7 @@ npm run build
184
223
  npm test
185
224
  ```
186
225
 
187
- Tests use `node:test` and mocked `fetch`; they never call the real sipgate API. The suite includes client authentication/error behavior, exact critical write payloads, credential redaction, one test per MCP tool, and read-only registration.
226
+ Tests use `node:test` and mocked `fetch`; they never call the real sipgate API. The suite includes client authentication/error behavior, user/account access-policy enforcement, exact critical write payloads, credential redaction, one test per MCP tool, and read-only registration.
188
227
 
189
228
  ## Releases
190
229
 
@@ -203,8 +242,9 @@ The endpoint paths, query parameters, request bodies, response models, and scope
203
242
 
204
243
  ## Roadmap
205
244
 
206
- - v1: local self-hosted stdio server (this release)
207
- - v2: optional remote deployment, including a Cloudflare Workers backend, without changing the MCP tool surface
245
+ - v0.1: local self-hosted stdio server and sipgate REST API tools
246
+ - v0.2: user-scoped access by default plus explicit administrator-only account scope
247
+ - Future: optional remote deployment, including a Cloudflare Workers backend, without changing the MCP tool surface
208
248
  - Additional `TelephonyBackend` implementation(s)
209
249
  - Product-aware Click2Dial behavior for classic and Neo PBX accounts
210
250
 
@@ -0,0 +1,57 @@
1
+ import type { AccessScope, AuthenticatedUserContext, DeviceType, ForwardingRule, HistoryQuery, JsonValue, MutationResult, PaginationInput, TelephonyBackend } from "./telephony-backend.js";
2
+ export declare class AccessPolicyError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * Enforces MCP-level resource boundaries in addition to sipgate's own role and
7
+ * token-scope checks. Account scope is rejected unless sipgate identifies the
8
+ * authenticated user as an administrator.
9
+ */
10
+ export declare class AccessControlledBackend implements TelephonyBackend {
11
+ private readonly delegate;
12
+ private readonly scope;
13
+ private readonly context;
14
+ constructor(delegate: TelephonyBackend, scope: AccessScope, context: AuthenticatedUserContext);
15
+ getAuthenticatedUser(): Promise<AuthenticatedUserContext>;
16
+ getUser(userId: string): Promise<JsonValue>;
17
+ getAccountInfo(): Promise<JsonValue>;
18
+ listUsers(): Promise<JsonValue>;
19
+ listNumbers({ offset, limit }: PaginationInput): Promise<JsonValue>;
20
+ listUserNumbers(userId: string, pagination: PaginationInput): Promise<JsonValue>;
21
+ listPhonelines(userId: string): Promise<JsonValue>;
22
+ listDevices(userId?: string, types?: DeviceType[]): Promise<JsonValue>;
23
+ getRouting(userId?: string): Promise<JsonValue>;
24
+ getCallHistory(query: HistoryQuery): Promise<JsonValue>;
25
+ getSettings(userId?: string): Promise<JsonValue>;
26
+ setNumberRouting(numberId: string, endpointId: string): Promise<MutationResult>;
27
+ setUserNumberRouting(userId: string, numberId: string, endpointId: string): Promise<MutationResult>;
28
+ setForwarding(userId: string, phonelineId: string, forwardings: ForwardingRule[]): Promise<MutationResult>;
29
+ setDnd(deviceId: string, enabled: boolean): Promise<MutationResult>;
30
+ sendSms(input: {
31
+ userId: string;
32
+ smsId?: string;
33
+ recipient: string;
34
+ message: string;
35
+ sendAt?: number;
36
+ }): Promise<MutationResult>;
37
+ initiateCall(input: {
38
+ caller: string;
39
+ callee: string;
40
+ callerId?: string;
41
+ deviceId?: string;
42
+ }): Promise<MutationResult>;
43
+ initiateUserCall(input: {
44
+ caller: string;
45
+ callee: string;
46
+ callerId?: string;
47
+ deviceId?: string;
48
+ }): Promise<MutationResult>;
49
+ private assertUser;
50
+ private assertOwned;
51
+ private ownedConnectionIds;
52
+ private ownedPhonelineIds;
53
+ private ownedDeviceIds;
54
+ private ownedNumbers;
55
+ }
56
+ export declare function createAccessControlledBackend(delegate: TelephonyBackend, scope: AccessScope): Promise<AccessControlledBackend>;
57
+ //# sourceMappingURL=access-controlled-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-controlled-backend.d.ts","sourceRoot":"","sources":["../../src/backend/access-controlled-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,227 @@
1
+ export class AccessPolicyError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "AccessPolicyError";
5
+ }
6
+ }
7
+ function asObject(value) {
8
+ return value && !Array.isArray(value) && typeof value === "object" ? value : undefined;
9
+ }
10
+ function objectArray(value) {
11
+ return Array.isArray(value)
12
+ ? value.filter((item) => Boolean(asObject(item)))
13
+ : [];
14
+ }
15
+ function stringField(value, key) {
16
+ const field = value[key];
17
+ return typeof field === "string" ? field : undefined;
18
+ }
19
+ function items(value) {
20
+ return objectArray(asObject(value)?.items);
21
+ }
22
+ function withAccessScope(value, context, scope) {
23
+ const object = asObject(value);
24
+ return object
25
+ ? { ...object, accessScope: scope }
26
+ : { value, authenticatedUser: context.identity, accessScope: scope };
27
+ }
28
+ /**
29
+ * Enforces MCP-level resource boundaries in addition to sipgate's own role and
30
+ * token-scope checks. Account scope is rejected unless sipgate identifies the
31
+ * authenticated user as an administrator.
32
+ */
33
+ export class AccessControlledBackend {
34
+ delegate;
35
+ scope;
36
+ context;
37
+ constructor(delegate, scope, context) {
38
+ this.delegate = delegate;
39
+ this.scope = scope;
40
+ this.context = context;
41
+ }
42
+ getAuthenticatedUser() {
43
+ return Promise.resolve(this.context);
44
+ }
45
+ async getUser(userId) {
46
+ this.assertUser(userId);
47
+ return this.delegate.getUser(userId);
48
+ }
49
+ async getAccountInfo() {
50
+ if (this.scope === "user") {
51
+ return {
52
+ authenticatedUser: this.context.identity,
53
+ accessScope: this.scope,
54
+ };
55
+ }
56
+ return withAccessScope(await this.delegate.getAccountInfo(), this.context, this.scope);
57
+ }
58
+ async listUsers() {
59
+ if (this.scope === "account")
60
+ return this.delegate.listUsers();
61
+ const user = await this.delegate.getUser(this.context.userId);
62
+ return { items: [user] };
63
+ }
64
+ async listNumbers({ offset, limit }) {
65
+ if (this.scope === "account")
66
+ return this.delegate.listNumbers({ offset, limit });
67
+ return this.delegate.listUserNumbers(this.context.userId, { offset, limit });
68
+ }
69
+ listUserNumbers(userId, pagination) {
70
+ this.assertUser(userId);
71
+ return this.delegate.listUserNumbers(userId, pagination);
72
+ }
73
+ listPhonelines(userId) {
74
+ this.assertUser(userId);
75
+ return this.delegate.listPhonelines(userId);
76
+ }
77
+ listDevices(userId, types) {
78
+ if (this.scope === "account")
79
+ return this.delegate.listDevices(userId, types);
80
+ this.assertUser(userId);
81
+ return this.delegate.listDevices(this.context.userId, types);
82
+ }
83
+ getRouting(userId) {
84
+ if (this.scope === "account")
85
+ return this.delegate.getRouting(userId);
86
+ this.assertUser(userId);
87
+ return this.delegate.getRouting(this.context.userId);
88
+ }
89
+ async getCallHistory(query) {
90
+ if (this.scope === "account")
91
+ return this.delegate.getCallHistory(query);
92
+ const connectionIds = await this.ownedConnectionIds();
93
+ const requested = query.connectionIds;
94
+ if (requested) {
95
+ for (const connectionId of requested) {
96
+ this.assertOwned(connectionIds, connectionId, "connection");
97
+ }
98
+ }
99
+ const scopedConnectionIds = requested ?? [...connectionIds];
100
+ if (scopedConnectionIds.length === 0) {
101
+ return {
102
+ items: [],
103
+ pagination: {
104
+ offset: query.offset,
105
+ limit: query.limit,
106
+ totalCount: 0,
107
+ nextOffset: null,
108
+ },
109
+ };
110
+ }
111
+ return this.delegate.getCallHistory({ ...query, connectionIds: scopedConnectionIds });
112
+ }
113
+ getSettings(userId) {
114
+ if (this.scope === "account")
115
+ return this.delegate.getSettings(userId);
116
+ this.assertUser(userId);
117
+ return this.delegate.getSettings(this.context.userId);
118
+ }
119
+ async setNumberRouting(numberId, endpointId) {
120
+ if (this.scope === "user") {
121
+ const [numbers, phonelineIds] = await Promise.all([
122
+ this.ownedNumbers(),
123
+ this.ownedPhonelineIds(),
124
+ ]);
125
+ this.assertOwned(numbers.numberIds, numberId, "phone number");
126
+ this.assertOwned(phonelineIds, endpointId, "destination phoneline");
127
+ }
128
+ return this.scope === "user"
129
+ ? this.delegate.setUserNumberRouting(this.context.userId, numberId, endpointId)
130
+ : this.delegate.setNumberRouting(numberId, endpointId);
131
+ }
132
+ setUserNumberRouting(userId, numberId, endpointId) {
133
+ this.assertUser(userId);
134
+ return this.setNumberRouting(numberId, endpointId);
135
+ }
136
+ async setForwarding(userId, phonelineId, forwardings) {
137
+ if (this.scope === "user") {
138
+ this.assertUser(userId);
139
+ this.assertOwned(await this.ownedPhonelineIds(), phonelineId, "phoneline");
140
+ }
141
+ return this.delegate.setForwarding(userId, phonelineId, forwardings);
142
+ }
143
+ async setDnd(deviceId, enabled) {
144
+ if (this.scope === "user") {
145
+ this.assertOwned(await this.ownedDeviceIds(), deviceId, "device");
146
+ }
147
+ return this.delegate.setDnd(deviceId, enabled);
148
+ }
149
+ async sendSms(input) {
150
+ if (this.scope === "user")
151
+ this.assertUser(input.userId);
152
+ return this.delegate.sendSms(input);
153
+ }
154
+ async initiateCall(input) {
155
+ if (this.scope === "user") {
156
+ const [numbers, deviceIds] = await Promise.all([
157
+ this.ownedNumbers(),
158
+ this.ownedDeviceIds(),
159
+ ]);
160
+ const callerIsOwned = deviceIds.has(input.caller)
161
+ || numbers.phoneNumbers.has(input.caller);
162
+ if (!callerIsOwned) {
163
+ throw new AccessPolicyError("User scope only permits calls from the authenticated user's devices or phone numbers.");
164
+ }
165
+ if (input.deviceId)
166
+ this.assertOwned(deviceIds, input.deviceId, "device");
167
+ if (input.callerId)
168
+ this.assertOwned(numbers.phoneNumbers, input.callerId, "caller ID");
169
+ }
170
+ return this.scope === "user"
171
+ ? this.delegate.initiateUserCall(input)
172
+ : this.delegate.initiateCall(input);
173
+ }
174
+ initiateUserCall(input) {
175
+ if (this.scope === "account")
176
+ return this.delegate.initiateUserCall(input);
177
+ return this.initiateCall(input);
178
+ }
179
+ assertUser(userId) {
180
+ if (this.scope === "account" || userId === undefined || userId === this.context.userId)
181
+ return;
182
+ throw new AccessPolicyError(`User scope is restricted to the authenticated sipgate user ${this.context.userId}.`);
183
+ }
184
+ assertOwned(allowed, value, resource) {
185
+ if (allowed.has(value))
186
+ return;
187
+ throw new AccessPolicyError(`User scope does not permit access to the requested ${resource}.`);
188
+ }
189
+ async ownedConnectionIds() {
190
+ const [phonelineIds, deviceIds] = await Promise.all([
191
+ this.ownedPhonelineIds(),
192
+ this.ownedDeviceIds(),
193
+ ]);
194
+ return new Set([...phonelineIds, ...deviceIds]);
195
+ }
196
+ async ownedPhonelineIds() {
197
+ const phonelinesResponse = await this.delegate.listPhonelines(this.context.userId);
198
+ return new Set(items(phonelinesResponse)
199
+ .map((phoneline) => stringField(phoneline, "id"))
200
+ .filter((id) => Boolean(id)));
201
+ }
202
+ async ownedDeviceIds() {
203
+ const devicesResponse = await this.delegate.listDevices(this.context.userId);
204
+ return new Set(items(devicesResponse)
205
+ .map((device) => stringField(device, "id"))
206
+ .filter((id) => Boolean(id)));
207
+ }
208
+ async ownedNumbers() {
209
+ const numbersResponse = await this.delegate.listUserNumbers(this.context.userId, { offset: 0, limit: 1000 });
210
+ const numbers = items(numbersResponse);
211
+ return {
212
+ numberIds: new Set(numbers.map((number) => stringField(number, "id")).filter((id) => Boolean(id))),
213
+ phoneNumbers: new Set(numbers.map((number) => stringField(number, "number")).filter((number) => Boolean(number))),
214
+ };
215
+ }
216
+ }
217
+ export async function createAccessControlledBackend(delegate, scope) {
218
+ const context = await delegate.getAuthenticatedUser();
219
+ if (scope === "account") {
220
+ const user = asObject(await delegate.getUser(context.userId));
221
+ if (user?.admin !== true) {
222
+ throw new AccessPolicyError("Account scope requires the authenticated sipgate user to be an administrator.");
223
+ }
224
+ }
225
+ return new AccessControlledBackend(delegate, scope, context);
226
+ }
227
+ //# sourceMappingURL=access-controlled-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-controlled-backend.js","sourceRoot":"","sources":["../../src/backend/access-controlled-backend.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAmB,OAAe;QAChC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAOD,SAAS,QAAQ,CAAC,KAA4B;IAC5C,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACzF,CAAC;AAED,SAAS,WAAW,CAAC,KAA4B;IAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAsB,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACrE,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,SAAS,WAAW,CAAC,KAAiB,EAAE,GAAW;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,KAAK,CAAC,KAAgB;IAC7B,OAAO,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,eAAe,CAAC,KAAgB,EAAE,OAAiC,EAAE,KAAkB;IAC9F,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,MAAM;QACX,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE;QACnC,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IAEf,QAAQ;IACR,KAAK;IACL,OAAO;IAH1B,YACmB,QAA0B,EAC1B,KAAkB,EAClB,OAAiC;wBAFjC,QAAQ;qBACR,KAAK;uBACL,OAAO;IACvB,CAAC;IAEG,oBAAoB;QACzB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,MAAc;QACjC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,cAAc;QACzB,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,OAAO;gBACL,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;gBACxC,WAAW,EAAE,IAAI,CAAC,KAAK;aACxB,CAAC;QACJ,CAAC;QACD,OAAO,eAAe,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACzF,CAAC;IAEM,KAAK,CAAC,SAAS;QACpB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9D,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAmB;QACzD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IAEM,eAAe,CAAC,MAAc,EAAE,UAA2B;QAChE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC;IAEM,cAAc,CAAC,MAAc;QAClC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IAEM,WAAW,CAAC,MAAe,EAAE,KAAoB;QACtD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC9E,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC;IAEM,UAAU,CAAC,MAAe;QAC/B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC;IAEM,KAAK,CAAC,cAAc,CAAC,KAAmB;QAC7C,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACzE,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,KAAK,CAAC,aAAa,CAAC;QACtC,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,MAAM,YAAY,IAAI,SAAS,EAAE,CAAC;gBACrC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QACD,MAAM,mBAAmB,GAAG,SAAS,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;QAC5D,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,OAAO;gBACL,KAAK,EAAE,EAAE;gBACT,UAAU,EAAE;oBACV,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,UAAU,EAAE,CAAC;oBACb,UAAU,EAAE,IAAI;iBACjB;aACF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC,CAAC;IACxF,CAAC;IAEM,WAAW,CAAC,MAAe;QAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACvE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IAEM,KAAK,CAAC,gBAAgB,CAAC,QAAgB,EAAE,UAAkB;QAChE,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChD,IAAI,CAAC,YAAY,EAAE;gBACnB,IAAI,CAAC,iBAAiB,EAAE;aACzB,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;YAC9D,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,KAAK,MAAM;YAC1B,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC;YAC/E,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC;IAEM,oBAAoB,CACzB,MAAc,EACd,QAAgB,EAChB,UAAkB;QAElB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;IAEM,KAAK,CAAC,aAAa,CACxB,MAAc,EACd,WAAmB,EACnB,WAA6B;QAE7B,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IACvE,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,OAAgB;QACpD,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,KAMpB;QACC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM;YAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,KAKzB;QACC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC7C,IAAI,CAAC,YAAY,EAAE;gBACnB,IAAI,CAAC,cAAc,EAAE;aACtB,CAAC,CAAC;YACH,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;mBAC5C,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,MAAM,IAAI,iBAAiB,CACzB,uFAAuF,CACxF,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,QAAQ;gBAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC1E,IAAI,KAAK,CAAC,QAAQ;gBAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,KAAK,MAAM;YAC1B,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC;YACvC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IAEM,gBAAgB,CAAC,KAKvB;QACC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAEO,UAAU,CAAC,MAA0B;QAC3C,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO;QAC/F,MAAM,IAAI,iBAAiB,CACzB,8DAA8D,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CACrF,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,OAAoB,EAAE,KAAa,EAAE,QAAgB;QACvE,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO;QAC/B,MAAM,IAAI,iBAAiB,CACzB,sDAAsD,QAAQ,GAAG,CAClE,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,kBAAkB;QAC9B,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAClD,IAAI,CAAC,iBAAiB,EAAE;YACxB,IAAI,CAAC,cAAc,EAAE;SACtB,CAAC,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IAClD,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnF,OAAO,IAAI,GAAG,CACZ,KAAK,CAAC,kBAAkB,CAAC;aACtB,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;aAChD,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC7C,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7E,OAAO,IAAI,GAAG,CACZ,KAAK,CAAC,eAAe,CAAC;aACnB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;aAC1C,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC7C,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,eAAe,CACzD,IAAI,CAAC,OAAO,CAAC,MAAM,EACnB,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAC3B,CAAC;QACF,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;QACvC,OAAO;YACL,SAAS,EAAE,IAAI,GAAG,CAChB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC7F;YACD,YAAY,EAAE,IAAI,GAAG,CACnB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAoB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAC7G;SACF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,QAA0B,EAC1B,KAAkB;IAElB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,oBAAoB,EAAE,CAAC;IACtD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,IAAI,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,iBAAiB,CACzB,+EAA+E,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,uBAAuB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAC/D,CAAC"}
@@ -1,16 +1,21 @@
1
1
  import { SipgateClient } from "./sipgate-client.js";
2
- import type { DeviceType, ForwardingRule, HistoryQuery, JsonValue, MutationResult, PaginationInput, TelephonyBackend } from "./telephony-backend.js";
2
+ import type { AuthenticatedUserContext, DeviceType, ForwardingRule, HistoryQuery, JsonValue, MutationResult, PaginationInput, TelephonyBackend } from "./telephony-backend.js";
3
3
  export declare class SipgateBackend implements TelephonyBackend {
4
4
  private readonly client;
5
5
  constructor(client: SipgateClient);
6
+ getAuthenticatedUser(): Promise<AuthenticatedUserContext>;
7
+ getUser(userId: string): Promise<JsonValue>;
6
8
  getAccountInfo(): Promise<JsonValue>;
7
9
  listUsers(): Promise<JsonValue>;
8
10
  listNumbers({ offset, limit }: PaginationInput): Promise<JsonValue>;
11
+ listPhonelines(userId: string): Promise<JsonValue>;
12
+ listUserNumbers(userId: string, { offset, limit }: PaginationInput): Promise<JsonValue>;
9
13
  listDevices(userId?: string, types?: DeviceType[]): Promise<JsonValue>;
10
14
  getRouting(userId?: string): Promise<JsonValue>;
11
15
  getCallHistory(query: HistoryQuery): Promise<JsonValue>;
12
16
  getSettings(userId?: string): Promise<JsonValue>;
13
17
  setNumberRouting(numberId: string, endpointId: string): Promise<MutationResult>;
18
+ setUserNumberRouting(userId: string, numberId: string, endpointId: string): Promise<MutationResult>;
14
19
  setForwarding(userId: string, phonelineId: string, forwardings: ForwardingRule[]): Promise<MutationResult>;
15
20
  setDnd(deviceId: string, enabled: boolean): Promise<MutationResult>;
16
21
  sendSms(input: {
@@ -26,7 +31,14 @@ export declare class SipgateBackend implements TelephonyBackend {
26
31
  callerId?: string;
27
32
  deviceId?: string;
28
33
  }): Promise<MutationResult>;
34
+ initiateUserCall(input: {
35
+ caller: string;
36
+ callee: string;
37
+ callerId?: string;
38
+ deviceId?: string;
39
+ }): Promise<MutationResult>;
29
40
  private findNumber;
41
+ private findUserNumber;
30
42
  private latestHistory;
31
43
  }
32
44
  //# sourceMappingURL=sipgate-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sipgate-backend.d.ts","sourceRoot":"","sources":["../../src/backend/sipgate-backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,YAAY,EAEZ,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAqChC,qBAAa,cAAe,YAAW,gBAAgB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAA1C,YAAoC,MAAM,EAAE,aAAa,EAAI;IAEhD,cAAc,IAAI,OAAO,CAAC,SAAS,CAAC,CAMhD;IAEY,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,CAG3C;IAEY,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAI/E;IAEY,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAgBlF;IAEY,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA6B3D;IAEY,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAqBnE;IAEY,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA8B5D;IAEY,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;IAEY,aAAa,CACxB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,cAAc,EAAE,GAC5B,OAAO,CAAC,cAAc,CAAC,CASzB;IAEY,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAM/E;IAEY,OAAO,CAAC,KAAK,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqC1B;IAEY,YAAY,CAAC,KAAK,EAAE;QAC/B,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoB1B;YAEa,UAAU;YAOV,aAAa;CAY5B"}
1
+ {"version":3,"file":"sipgate-backend.d.ts","sourceRoot":"","sources":["../../src/backend/sipgate-backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EACV,wBAAwB,EACxB,UAAU,EACV,cAAc,EACd,YAAY,EAEZ,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAqChC,qBAAa,cAAe,YAAW,gBAAgB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAA1C,YAAoC,MAAM,EAAE,aAAa,EAAI;IAEhD,oBAAoB,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAUrE;IAEY,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAGvD;IAEY,cAAc,IAAI,OAAO,CAAC,SAAS,CAAC,CAMhD;IAEY,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,CAG3C;IAEY,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAI/E;IAEY,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAG9D;IAEY,eAAe,CAC1B,MAAM,EAAE,MAAM,EACd,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,eAAe,GACjC,OAAO,CAAC,SAAS,CAAC,CAuBpB;IAEY,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAgBlF;IAEY,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA2C3D;IAEY,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAqBnE;IAEY,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA8B5D;IAEY,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;IAEY,oBAAoB,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAQzB;IAEY,aAAa,CACxB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,cAAc,EAAE,GAC5B,OAAO,CAAC,cAAc,CAAC,CASzB;IAEY,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAM/E;IAEY,OAAO,CAAC,KAAK,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqC1B;IAEY,YAAY,CAAC,KAAK,EAAE;QAC/B,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoB1B;IAEY,gBAAgB,CAAC,KAAK,EAAE;QACnC,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAkB1B;YAEa,UAAU;YAOV,cAAc;YAOd,aAAa;CAiB5B"}