@ihazz/bitrix24 1.1.1 → 1.1.3
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/README.md +5 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/src/access-control.d.ts +43 -0
- package/dist/src/access-control.d.ts.map +1 -0
- package/dist/src/access-control.js +128 -0
- package/dist/src/access-control.js.map +1 -0
- package/dist/src/api.d.ts +161 -0
- package/dist/src/api.d.ts.map +1 -0
- package/dist/src/api.js +357 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/bot-avatar.d.ts +7 -0
- package/dist/src/bot-avatar.d.ts.map +1 -0
- package/dist/src/bot-avatar.js +7 -0
- package/dist/src/bot-avatar.js.map +1 -0
- package/dist/src/channel.d.ts +216 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +2324 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/commands.d.ts +22 -0
- package/dist/src/commands.d.ts.map +1 -0
- package/dist/src/commands.js +160 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/config-schema.d.ts +356 -0
- package/dist/src/config-schema.d.ts.map +1 -0
- package/dist/src/config-schema.js +43 -0
- package/dist/src/config-schema.js.map +1 -0
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +50 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dedup.d.ts +22 -0
- package/dist/src/dedup.d.ts.map +1 -0
- package/dist/src/dedup.js +49 -0
- package/dist/src/dedup.js.map +1 -0
- package/dist/src/group-access.d.ts +52 -0
- package/dist/src/group-access.d.ts.map +1 -0
- package/dist/src/group-access.js +180 -0
- package/dist/src/group-access.js.map +1 -0
- package/dist/src/history-cache.d.ts +41 -0
- package/dist/src/history-cache.d.ts.map +1 -0
- package/dist/src/history-cache.js +82 -0
- package/dist/src/history-cache.js.map +1 -0
- package/dist/src/i18n.d.ts +22 -0
- package/dist/src/i18n.d.ts.map +1 -0
- package/dist/src/i18n.js +175 -0
- package/dist/src/i18n.js.map +1 -0
- package/dist/src/inbound-handler.d.ts +92 -0
- package/dist/src/inbound-handler.d.ts.map +1 -0
- package/dist/src/inbound-handler.js +417 -0
- package/dist/src/inbound-handler.js.map +1 -0
- package/dist/src/media-service.d.ts +52 -0
- package/dist/src/media-service.d.ts.map +1 -0
- package/dist/src/media-service.js +423 -0
- package/dist/src/media-service.js.map +1 -0
- package/dist/src/message-utils.d.ts +34 -0
- package/dist/src/message-utils.d.ts.map +1 -0
- package/dist/src/message-utils.js +392 -0
- package/dist/src/message-utils.js.map +1 -0
- package/dist/src/polling-service.d.ts +39 -0
- package/dist/src/polling-service.d.ts.map +1 -0
- package/dist/src/polling-service.js +204 -0
- package/dist/src/polling-service.js.map +1 -0
- package/dist/src/rate-limiter.d.ts +22 -0
- package/dist/src/rate-limiter.d.ts.map +1 -0
- package/dist/src/rate-limiter.js +72 -0
- package/dist/src/rate-limiter.js.map +1 -0
- package/dist/src/runtime.d.ts +106 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +11 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/send-service.d.ts +66 -0
- package/dist/src/send-service.d.ts.map +1 -0
- package/dist/src/send-service.js +177 -0
- package/dist/src/send-service.js.map +1 -0
- package/dist/src/state-paths.d.ts +3 -0
- package/dist/src/state-paths.d.ts.map +1 -0
- package/dist/src/state-paths.js +23 -0
- package/dist/src/state-paths.js.map +1 -0
- package/dist/src/types.d.ts +381 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils.d.ts +60 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +131 -0
- package/dist/src/utils.js.map +1 -0
- package/index.ts +1 -1
- package/openclaw.plugin.json +278 -1
- package/package.json +19 -2
- package/src/api.ts +0 -3
- package/src/channel.ts +76 -73
- package/src/config-schema.ts +1 -2
- package/src/config.ts +6 -8
- package/src/group-access.ts +1 -8
- package/src/inbound-handler.ts +128 -15
- package/src/media-service.ts +229 -61
- package/src/polling-service.ts +2 -3
- package/src/send-service.ts +4 -3
- package/src/state-paths.ts +28 -0
- package/src/types.ts +1 -2
- package/src/utils.ts +31 -4
- package/tests/access-control.test.ts +0 -398
- package/tests/api.test.ts +0 -226
- package/tests/channel-flow.test.ts +0 -1692
- package/tests/channel.test.ts +0 -842
- package/tests/commands.test.ts +0 -57
- package/tests/config.test.ts +0 -210
- package/tests/dedup.test.ts +0 -50
- package/tests/fixtures/onimbotjoinchat.json +0 -48
- package/tests/fixtures/onimbotmessageadd-file.json +0 -86
- package/tests/fixtures/onimbotmessageadd-text.json +0 -59
- package/tests/fixtures/onimcommandadd.json +0 -45
- package/tests/group-access.test.ts +0 -340
- package/tests/history-cache.test.ts +0 -117
- package/tests/i18n.test.ts +0 -90
- package/tests/inbound-handler.test.ts +0 -1033
- package/tests/index.test.ts +0 -94
- package/tests/media-service.test.ts +0 -319
- package/tests/message-utils.test.ts +0 -184
- package/tests/polling-service.test.ts +0 -115
- package/tests/rate-limiter.test.ts +0 -52
- package/tests/send-service.test.ts +0 -162
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -9
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function resolveHomeDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
5
|
+
const homePath = env.HOME?.trim() || env.USERPROFILE?.trim();
|
|
6
|
+
if (homePath) {
|
|
7
|
+
return resolvePath(homePath);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return homedir();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveOpenClawStateDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
14
|
+
const override = env.OPENCLAW_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim();
|
|
15
|
+
if (override) {
|
|
16
|
+
return resolvePath(override);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return join(resolveHomeDir(env), '.openclaw');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function resolveManagedMediaDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
23
|
+
return join(resolveOpenClawStateDir(env), 'media', 'bitrix24');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolvePollingStateDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
27
|
+
return join(resolveOpenClawStateDir(env), 'state', 'bitrix24');
|
|
28
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -411,8 +411,7 @@ export interface Bitrix24AccountConfig {
|
|
|
411
411
|
groups?: Record<string, Bitrix24GroupConfig>;
|
|
412
412
|
agentWatch?: Record<string, Bitrix24AgentWatchConfig[]>;
|
|
413
413
|
showTyping?: boolean;
|
|
414
|
-
|
|
415
|
-
updateIntervalMs?: number;
|
|
414
|
+
verboseLog?: boolean;
|
|
416
415
|
}
|
|
417
416
|
|
|
418
417
|
export interface Bitrix24PluginConfig extends Bitrix24AccountConfig {
|
package/src/utils.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Logger } from './types.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Mask a token for safe logging: show first 4 and last 4 characters.
|
|
3
5
|
*/
|
|
@@ -7,11 +9,11 @@ export function maskToken(token: string): string {
|
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* Mask
|
|
12
|
+
* Mask sensitive URL parts before logging them.
|
|
11
13
|
*/
|
|
12
|
-
export function
|
|
14
|
+
export function maskUrlForLog(rawUrl: string): string {
|
|
13
15
|
try {
|
|
14
|
-
const url = new URL(
|
|
16
|
+
const url = new URL(rawUrl);
|
|
15
17
|
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
16
18
|
|
|
17
19
|
if (pathParts[0] === 'rest' && pathParts.length >= 3) {
|
|
@@ -25,10 +27,17 @@ export function maskWebhookUrl(webhookUrl: string): string {
|
|
|
25
27
|
url.search = '';
|
|
26
28
|
return url.toString();
|
|
27
29
|
} catch {
|
|
28
|
-
return '[masked
|
|
30
|
+
return '[masked url]';
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Mask Bitrix24 webhook credentials in a REST URL before logging it.
|
|
36
|
+
*/
|
|
37
|
+
export function maskWebhookUrl(webhookUrl: string): string {
|
|
38
|
+
return maskUrlForLog(webhookUrl);
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
/**
|
|
33
42
|
* Convert an unknown error into a log-friendly plain object.
|
|
34
43
|
*/
|
|
@@ -114,6 +123,24 @@ export function stripChannelPrefix(id: string): string {
|
|
|
114
123
|
return id.replace(CHANNEL_PREFIX_RE, '');
|
|
115
124
|
}
|
|
116
125
|
|
|
126
|
+
const noop = (): void => undefined;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Build a logger that keeps warnings/errors and enables info/debug only in verbose mode.
|
|
130
|
+
*/
|
|
131
|
+
export function createVerboseLogger(logger: Logger = defaultLogger, verbose = false): Logger {
|
|
132
|
+
if (verbose) {
|
|
133
|
+
return logger;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
info: noop,
|
|
138
|
+
debug: noop,
|
|
139
|
+
warn: (...args: unknown[]) => logger.warn(...args),
|
|
140
|
+
error: (...args: unknown[]) => logger.error(...args),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
117
144
|
/**
|
|
118
145
|
* Simple console logger fallback.
|
|
119
146
|
*/
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
checkAccess,
|
|
4
|
-
checkAccessWithPairing,
|
|
5
|
-
getWebhookUserId,
|
|
6
|
-
normalizeAllowEntry,
|
|
7
|
-
normalizeAllowList,
|
|
8
|
-
} from '../src/access-control.js';
|
|
9
|
-
import type { PluginRuntime, ChannelPairingAdapter } from '../src/runtime.js';
|
|
10
|
-
|
|
11
|
-
describe('normalizeAllowEntry', () => {
|
|
12
|
-
it('strips bitrix24: prefix', () => {
|
|
13
|
-
expect(normalizeAllowEntry('bitrix24:42')).toBe('42');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('strips b24: prefix', () => {
|
|
17
|
-
expect(normalizeAllowEntry('b24:100')).toBe('100');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('strips bx24: prefix', () => {
|
|
21
|
-
expect(normalizeAllowEntry('bx24:7')).toBe('7');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('strips prefixes case-insensitively', () => {
|
|
25
|
-
expect(normalizeAllowEntry('Bitrix24:42')).toBe('42');
|
|
26
|
-
expect(normalizeAllowEntry('B24:100')).toBe('100');
|
|
27
|
-
expect(normalizeAllowEntry('BX24:7')).toBe('7');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('returns plain ID as-is', () => {
|
|
31
|
-
expect(normalizeAllowEntry('42')).toBe('42');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('trims whitespace', () => {
|
|
35
|
-
expect(normalizeAllowEntry(' b24:42 ')).toBe('42');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('normalizeAllowList', () => {
|
|
40
|
-
it('normalizes, deduplicates and filters empty allowFrom entries', () => {
|
|
41
|
-
expect(normalizeAllowList([' bitrix24:42 ', '42', 'b24:7', ''])).toEqual(['42', '7']);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('returns empty list for missing allowFrom config', () => {
|
|
45
|
-
expect(normalizeAllowList(undefined)).toEqual([]);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('getWebhookUserId', () => {
|
|
50
|
-
it('extracts owner ID from webhook URL', () => {
|
|
51
|
-
expect(getWebhookUserId('https://test.bitrix24.com/rest/42/token/')).toBe('42');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('normalizes prefixed user IDs', () => {
|
|
55
|
-
expect(getWebhookUserId('https://test.bitrix24.com/rest/b24:42/token/')).toBe('42');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('returns null for invalid URLs', () => {
|
|
59
|
-
expect(getWebhookUserId('not-a-url')).toBeNull();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('returns null when rest segment is missing', () => {
|
|
63
|
-
expect(getWebhookUserId('https://test.bitrix24.com/api/42/token/')).toBeNull();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('checkAccess', () => {
|
|
68
|
-
it('defaults to webhookUser and denies without a valid webhook owner', () => {
|
|
69
|
-
expect(checkAccess('1', {})).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('allows only the webhook owner in webhookUser mode', () => {
|
|
73
|
-
const config = {
|
|
74
|
-
dmPolicy: 'webhookUser' as const,
|
|
75
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
expect(checkAccess('42', config)).toBe(true);
|
|
79
|
-
expect(checkAccess('99', config)).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('allows webhook owner when dmPolicy is omitted', () => {
|
|
83
|
-
expect(checkAccess('42', { webhookUrl: 'https://test.bitrix24.com/rest/42/token/' })).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('keeps senderId as the access identity even when direct dialogId differs', () => {
|
|
87
|
-
const config = {
|
|
88
|
-
dmPolicy: 'webhookUser' as const,
|
|
89
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
expect(checkAccess('42', config, { dialogId: '2386', isDirect: true })).toBe(true);
|
|
93
|
-
expect(checkAccess('77', config, { dialogId: '42', isDirect: true })).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('denies when webhookUser mode has no valid webhook owner', () => {
|
|
97
|
-
expect(checkAccess('42', { dmPolicy: 'webhookUser', webhookUrl: 'invalid' })).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('returns false for pairing mode without runtime state', () => {
|
|
101
|
-
expect(checkAccess('1', { dmPolicy: 'pairing' })).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('allows any sender in open mode', () => {
|
|
105
|
-
expect(checkAccess('1', { dmPolicy: 'open' })).toBe(true);
|
|
106
|
-
expect(checkAccess('77', { dmPolicy: 'open' })).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('allows only configured identities in allowlist mode', () => {
|
|
110
|
-
expect(checkAccess('42', { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] })).toBe(true);
|
|
111
|
-
expect(checkAccess('77', { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] })).toBe(false);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
function makeMockRuntime(storeAllowFrom: string[] = []): PluginRuntime {
|
|
116
|
-
return {
|
|
117
|
-
config: { loadConfig: () => ({}) },
|
|
118
|
-
channel: {
|
|
119
|
-
routing: { resolveAgentRoute: vi.fn() },
|
|
120
|
-
reply: {
|
|
121
|
-
finalizeInboundContext: vi.fn(),
|
|
122
|
-
dispatchReplyWithBufferedBlockDispatcher: vi.fn(),
|
|
123
|
-
},
|
|
124
|
-
session: { recordInboundSession: vi.fn() },
|
|
125
|
-
pairing: {
|
|
126
|
-
readAllowFromStore: vi.fn().mockResolvedValue(storeAllowFrom),
|
|
127
|
-
upsertPairingRequest: vi.fn().mockResolvedValue({ code: 'ABCD1234', created: true }),
|
|
128
|
-
buildPairingReply: vi.fn().mockReturnValue('Your pairing code: ABCD1234'),
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
logging: { getChildLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }) },
|
|
132
|
-
} as unknown as PluginRuntime;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const mockAdapter: ChannelPairingAdapter = {
|
|
136
|
-
idLabel: 'bitrix24UserId',
|
|
137
|
-
normalizeAllowEntry: (entry) => entry.replace(/^(bitrix24|b24|bx24):/i, ''),
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const silentLogger = { debug: vi.fn() };
|
|
141
|
-
|
|
142
|
-
describe('checkAccessWithPairing', () => {
|
|
143
|
-
it('defaults to webhookUser when dmPolicy is omitted', async () => {
|
|
144
|
-
const runtime = makeMockRuntime();
|
|
145
|
-
|
|
146
|
-
const result = await checkAccessWithPairing({
|
|
147
|
-
senderId: '42',
|
|
148
|
-
config: { webhookUrl: 'https://test.bitrix24.com/rest/42/token/' },
|
|
149
|
-
runtime,
|
|
150
|
-
accountId: 'default',
|
|
151
|
-
pairingAdapter: mockAdapter,
|
|
152
|
-
sendReply: vi.fn(),
|
|
153
|
-
logger: silentLogger,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
expect(result).toBe('allow');
|
|
157
|
-
expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('allows only the webhook owner in webhookUser mode', async () => {
|
|
161
|
-
const runtime = makeMockRuntime();
|
|
162
|
-
const sendReply = vi.fn();
|
|
163
|
-
|
|
164
|
-
const result = await checkAccessWithPairing({
|
|
165
|
-
senderId: '42',
|
|
166
|
-
config: {
|
|
167
|
-
dmPolicy: 'webhookUser',
|
|
168
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
169
|
-
},
|
|
170
|
-
runtime,
|
|
171
|
-
accountId: 'default',
|
|
172
|
-
pairingAdapter: mockAdapter,
|
|
173
|
-
sendReply,
|
|
174
|
-
logger: silentLogger,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
expect(result).toBe('allow');
|
|
178
|
-
expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
|
|
179
|
-
expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
|
|
180
|
-
expect(sendReply).not.toHaveBeenCalled();
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('keeps senderId as the identity in webhookUser mode even when dialogId differs', async () => {
|
|
184
|
-
const runtime = makeMockRuntime();
|
|
185
|
-
|
|
186
|
-
const allowed = await checkAccessWithPairing({
|
|
187
|
-
senderId: '42',
|
|
188
|
-
dialogId: '2386',
|
|
189
|
-
isDirect: true,
|
|
190
|
-
config: {
|
|
191
|
-
dmPolicy: 'webhookUser',
|
|
192
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
193
|
-
},
|
|
194
|
-
runtime,
|
|
195
|
-
accountId: 'default',
|
|
196
|
-
pairingAdapter: mockAdapter,
|
|
197
|
-
sendReply: vi.fn(),
|
|
198
|
-
logger: silentLogger,
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const denied = await checkAccessWithPairing({
|
|
202
|
-
senderId: '77',
|
|
203
|
-
dialogId: '42',
|
|
204
|
-
isDirect: true,
|
|
205
|
-
config: {
|
|
206
|
-
dmPolicy: 'webhookUser',
|
|
207
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
208
|
-
},
|
|
209
|
-
runtime,
|
|
210
|
-
accountId: 'default',
|
|
211
|
-
pairingAdapter: mockAdapter,
|
|
212
|
-
sendReply: vi.fn(),
|
|
213
|
-
logger: silentLogger,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(allowed).toBe('allow');
|
|
217
|
-
expect(denied).toBe('deny');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('denies non-owner users in webhookUser mode', async () => {
|
|
221
|
-
const runtime = makeMockRuntime();
|
|
222
|
-
const sendReply = vi.fn();
|
|
223
|
-
|
|
224
|
-
const result = await checkAccessWithPairing({
|
|
225
|
-
senderId: '77',
|
|
226
|
-
config: {
|
|
227
|
-
dmPolicy: 'webhookUser',
|
|
228
|
-
webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
|
|
229
|
-
},
|
|
230
|
-
runtime,
|
|
231
|
-
accountId: 'default',
|
|
232
|
-
pairingAdapter: mockAdapter,
|
|
233
|
-
sendReply,
|
|
234
|
-
logger: silentLogger,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
expect(result).toBe('deny');
|
|
238
|
-
expect(runtime.channel.pairing.readAllowFromStore).not.toHaveBeenCalled();
|
|
239
|
-
expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
|
|
240
|
-
expect(sendReply).not.toHaveBeenCalled();
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('returns allow when sender is in pairing store', async () => {
|
|
244
|
-
const runtime = makeMockRuntime(['42']);
|
|
245
|
-
|
|
246
|
-
const result = await checkAccessWithPairing({
|
|
247
|
-
senderId: '42',
|
|
248
|
-
config: { dmPolicy: 'pairing' },
|
|
249
|
-
runtime,
|
|
250
|
-
accountId: 'default',
|
|
251
|
-
pairingAdapter: mockAdapter,
|
|
252
|
-
sendReply: vi.fn(),
|
|
253
|
-
logger: silentLogger,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
expect(result).toBe('allow');
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('returns allow when sender is in config allowFrom', async () => {
|
|
260
|
-
const runtime = makeMockRuntime();
|
|
261
|
-
|
|
262
|
-
const result = await checkAccessWithPairing({
|
|
263
|
-
senderId: '42',
|
|
264
|
-
config: {
|
|
265
|
-
dmPolicy: 'pairing',
|
|
266
|
-
allowFrom: ['bitrix24:42'],
|
|
267
|
-
},
|
|
268
|
-
runtime,
|
|
269
|
-
accountId: 'default',
|
|
270
|
-
pairingAdapter: mockAdapter,
|
|
271
|
-
sendReply: vi.fn(),
|
|
272
|
-
logger: silentLogger,
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
expect(result).toBe('allow');
|
|
276
|
-
expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('returns allow for any sender in open mode', async () => {
|
|
280
|
-
const runtime = makeMockRuntime();
|
|
281
|
-
|
|
282
|
-
const result = await checkAccessWithPairing({
|
|
283
|
-
senderId: '77',
|
|
284
|
-
config: { dmPolicy: 'open' },
|
|
285
|
-
runtime,
|
|
286
|
-
accountId: 'default',
|
|
287
|
-
pairingAdapter: mockAdapter,
|
|
288
|
-
sendReply: vi.fn(),
|
|
289
|
-
logger: silentLogger,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
expect(result).toBe('allow');
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('returns deny for non-allowlisted sender in allowlist mode', async () => {
|
|
296
|
-
const runtime = makeMockRuntime();
|
|
297
|
-
|
|
298
|
-
const result = await checkAccessWithPairing({
|
|
299
|
-
senderId: '77',
|
|
300
|
-
config: { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] },
|
|
301
|
-
runtime,
|
|
302
|
-
accountId: 'default',
|
|
303
|
-
pairingAdapter: mockAdapter,
|
|
304
|
-
sendReply: vi.fn(),
|
|
305
|
-
logger: silentLogger,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
expect(result).toBe('deny');
|
|
309
|
-
expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('uses senderId as the pairing identity even when direct dialogId differs', async () => {
|
|
313
|
-
const runtime = makeMockRuntime(['42']);
|
|
314
|
-
|
|
315
|
-
const result = await checkAccessWithPairing({
|
|
316
|
-
senderId: '42',
|
|
317
|
-
dialogId: '42',
|
|
318
|
-
isDirect: true,
|
|
319
|
-
config: { dmPolicy: 'pairing' },
|
|
320
|
-
runtime,
|
|
321
|
-
accountId: 'default',
|
|
322
|
-
pairingAdapter: mockAdapter,
|
|
323
|
-
sendReply: vi.fn(),
|
|
324
|
-
logger: silentLogger,
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
expect(result).toBe('allow');
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('normalizes prefixed entries from pairing store', async () => {
|
|
331
|
-
const runtime = makeMockRuntime(['b24:42']);
|
|
332
|
-
|
|
333
|
-
const result = await checkAccessWithPairing({
|
|
334
|
-
senderId: '42',
|
|
335
|
-
config: { dmPolicy: 'pairing' },
|
|
336
|
-
runtime,
|
|
337
|
-
accountId: 'default',
|
|
338
|
-
pairingAdapter: mockAdapter,
|
|
339
|
-
sendReply: vi.fn(),
|
|
340
|
-
logger: silentLogger,
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
expect(result).toBe('allow');
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('upserts pairing request and sends reply for new pairing', async () => {
|
|
347
|
-
const runtime = makeMockRuntime();
|
|
348
|
-
const sendReply = vi.fn();
|
|
349
|
-
|
|
350
|
-
const result = await checkAccessWithPairing({
|
|
351
|
-
senderId: '77',
|
|
352
|
-
config: { dmPolicy: 'pairing' },
|
|
353
|
-
runtime,
|
|
354
|
-
accountId: 'default',
|
|
355
|
-
pairingAdapter: mockAdapter,
|
|
356
|
-
sendReply,
|
|
357
|
-
logger: silentLogger,
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
expect(result).toBe('pairing');
|
|
361
|
-
expect(runtime.channel.pairing.readAllowFromStore).toHaveBeenCalledWith('bitrix24', '', 'default');
|
|
362
|
-
expect(runtime.channel.pairing.upsertPairingRequest).toHaveBeenCalledWith({
|
|
363
|
-
channel: 'bitrix24',
|
|
364
|
-
id: '77',
|
|
365
|
-
accountId: 'default',
|
|
366
|
-
meta: {},
|
|
367
|
-
pairingAdapter: mockAdapter,
|
|
368
|
-
});
|
|
369
|
-
expect(runtime.channel.pairing.buildPairingReply).toHaveBeenCalledWith({
|
|
370
|
-
code: 'ABCD1234',
|
|
371
|
-
channel: 'bitrix24',
|
|
372
|
-
accountId: 'default',
|
|
373
|
-
});
|
|
374
|
-
expect(sendReply).toHaveBeenCalledWith('Your pairing code: ABCD1234');
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('does not send reply for duplicate pairing request', async () => {
|
|
378
|
-
const runtime = makeMockRuntime();
|
|
379
|
-
(runtime.channel.pairing.upsertPairingRequest as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
380
|
-
code: 'ABCD1234',
|
|
381
|
-
created: false,
|
|
382
|
-
});
|
|
383
|
-
const sendReply = vi.fn();
|
|
384
|
-
|
|
385
|
-
const result = await checkAccessWithPairing({
|
|
386
|
-
senderId: '77',
|
|
387
|
-
config: { dmPolicy: 'pairing' },
|
|
388
|
-
runtime,
|
|
389
|
-
accountId: 'default',
|
|
390
|
-
pairingAdapter: mockAdapter,
|
|
391
|
-
sendReply,
|
|
392
|
-
logger: silentLogger,
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
expect(result).toBe('pairing');
|
|
396
|
-
expect(sendReply).not.toHaveBeenCalled();
|
|
397
|
-
});
|
|
398
|
-
});
|