-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy pathAllowDenyList.ts
125 lines (108 loc) · 4.17 KB
/
AllowDenyList.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import escapeStringRegexp from "escape-string-regexp";
export interface AllowDenyConfig {
allow?: {
slack?: string[];
matrix?: string[];
},
deny?: {
slack?: string[];
matrix?: string[];
}
}
export interface AllowDenyConfigSimple {
allow?: string[];
deny?: string[];
}
export enum DenyReason {
ALLOWED,
MATRIX,
SLACK,
}
export class AllowDenyList {
private static convertToRegex(str: string) {
if (!str.startsWith("/") || !str.endsWith("/")) {
// NOTE: String will still need to be escaped
str = escapeStringRegexp(str);
// Ensure the exact string matches.
return new RegExp(`^${str}$`);
}
// Otherwise, it's a real regex. Remove the leading and trailing slash.
return new RegExp(str.slice(1, str.length - 1));
}
private dmAllow?: {
matrix: RegExp[];
slack: RegExp[];
};
private dmDeny?: {
matrix: RegExp[];
slack: RegExp[];
};
private slackChannelAllow?: RegExp[];
private slackChannelDeny?: RegExp[];
constructor(dmConfig?: AllowDenyConfig, slackChannelConfig?: AllowDenyConfigSimple) {
if (dmConfig?.allow) {
this.dmAllow = {
matrix: (dmConfig.allow.matrix || []).map(AllowDenyList.convertToRegex),
slack: (dmConfig.allow.slack || []).map(AllowDenyList.convertToRegex)
};
}
if (dmConfig?.deny) {
this.dmDeny = {
matrix: (dmConfig.deny.matrix || []).map(AllowDenyList.convertToRegex),
slack: (dmConfig.deny.slack || []).map(AllowDenyList.convertToRegex)
};
}
if (slackChannelConfig?.allow) {
this.slackChannelAllow = slackChannelConfig.allow.map(AllowDenyList.convertToRegex);
}
if (slackChannelConfig?.deny) {
this.slackChannelDeny = slackChannelConfig.deny.map(AllowDenyList.convertToRegex);
}
}
/**
* Test if a DM is allowed to go from Matrix to Slack
* @param slackUser The Slack User ID
* @param matrixUser The Matrix MXID
*/
public allowDM(matrixUser: string, slackUser: string, slackUsername?: string): DenyReason {
const allow = this.dmAllow;
if (allow && allow.matrix?.length > 0 && !allow.matrix.some((e) => e.test(matrixUser))) {
return DenyReason.MATRIX; // Matrix user was not on the allow list
}
if (allow && allow.slack?.length > 0 && !allow.slack.some((e) => e.test(slackUser) ||
(slackUsername && e.test(slackUsername)))) {
return DenyReason.SLACK; // Slack user was not on the allow list
}
const deny = this.dmDeny;
if (deny && deny.matrix?.length > 0 && deny.matrix.some((e) => e.test(matrixUser))) {
return DenyReason.MATRIX; // Matrix user was on the deny list
}
if (deny && deny.slack?.length > 0 && deny.slack.some((e) => e.test(slackUser) ||
(slackUsername && e.test(slackUsername)))) {
return DenyReason.SLACK; // Slack user was on the deny list
}
return DenyReason.ALLOWED;
}
/**
* Check if a Slack channel can be bridged.
* @param slackChannelId The Slack channel ID e.g. CCZ41UJV7
* @param slackChannelName The Slack channel name e.g. #general
*/
public allowSlackChannel(slackChannelId: string, slackChannelName?: string): DenyReason.ALLOWED|DenyReason.SLACK {
if (slackChannelName?.startsWith('#')) {
slackChannelName = slackChannelName.slice(1);
}
// Test against both general and #general.
const testSlackChannel = (regex: RegExp) =>
regex.test(slackChannelId) || (slackChannelName && (regex.test(slackChannelName) || regex.test(`#${slackChannelName}`)));
const allow = this.slackChannelAllow;
if (allow && allow.length > 0 && !allow.some(testSlackChannel)) {
return DenyReason.SLACK;
}
const deny = this.slackChannelDeny;
if (deny && deny.length > 0 && deny.some(testSlackChannel)) {
return DenyReason.SLACK;
}
return DenyReason.ALLOWED;
}
}