@atproto/ozone 0.1.56 → 0.1.58
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 +18 -0
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +70 -13
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/api/setting/upsertOption.d.ts.map +1 -1
- package/dist/api/setting/upsertOption.js +7 -2
- package/dist/api/setting/upsertOption.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +12 -4
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +12 -2
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +2 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +1 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +22 -5
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +3 -2
- package/dist/mod-service/views.js.map +1 -1
- package/dist/setting/constants.d.ts +2 -0
- package/dist/setting/constants.d.ts.map +1 -0
- package/dist/setting/constants.js +5 -0
- package/dist/setting/constants.js.map +1 -0
- package/dist/setting/types.d.ts +7 -0
- package/dist/setting/types.d.ts.map +1 -0
- package/dist/setting/types.js +3 -0
- package/dist/setting/types.js.map +1 -0
- package/dist/setting/validators.d.ts +4 -0
- package/dist/setting/validators.d.ts.map +1 -0
- package/dist/setting/validators.js +43 -0
- package/dist/setting/validators.js.map +1 -0
- package/package.json +3 -3
- package/src/api/moderation/emitEvent.ts +125 -15
- package/src/api/setting/upsertOption.ts +10 -3
- package/src/lexicon/lexicons.ts +13 -2
- package/src/lexicon/types/app/bsky/actor/defs.ts +7 -1
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +1 -1
- package/src/mod-service/index.ts +30 -7
- package/src/mod-service/views.ts +3 -2
- package/src/setting/constants.ts +1 -0
- package/src/setting/types.ts +3 -0
- package/src/setting/validators.ts +61 -0
- package/tests/__snapshots__/moderation-events.test.ts.snap +123 -0
- package/tests/ack-all-subjects-of-account.test.ts +76 -30
- package/tests/moderation-events.test.ts +40 -0
- package/tests/protected-tags.test.ts +201 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Selectable } from 'kysely'
|
|
2
|
+
import { Setting } from '../db/schema/setting'
|
|
3
|
+
import { ProtectedTagSettingKey } from './constants'
|
|
4
|
+
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
5
|
+
|
|
6
|
+
export const settingValidators = new Map<
|
|
7
|
+
string,
|
|
8
|
+
(setting: Partial<Selectable<Setting>>) => Promise<void>
|
|
9
|
+
>([
|
|
10
|
+
[
|
|
11
|
+
ProtectedTagSettingKey,
|
|
12
|
+
async (setting: Partial<Selectable<Setting>>) => {
|
|
13
|
+
if (setting.managerRole !== 'tools.ozone.team.defs#roleAdmin') {
|
|
14
|
+
throw new InvalidRequestError(
|
|
15
|
+
'Only admins should be able to configure protected tags',
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof setting.value !== 'object') {
|
|
20
|
+
throw new InvalidRequestError('Invalid value')
|
|
21
|
+
}
|
|
22
|
+
for (const [key, val] of Object.entries(setting.value)) {
|
|
23
|
+
if (!val || typeof val !== 'object') {
|
|
24
|
+
throw new InvalidRequestError(`Invalid configuration for tag ${key}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!val['roles'] && !val['moderators']) {
|
|
28
|
+
throw new InvalidRequestError(
|
|
29
|
+
`Must define who a list of moderators or a role who can action subjects with ${key} tag`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (val['roles']) {
|
|
34
|
+
if (!Array.isArray(val['roles'])) {
|
|
35
|
+
throw new InvalidRequestError(
|
|
36
|
+
`Roles must be an array of moderator roles for tag ${key}`,
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
if (!val['roles']?.length) {
|
|
40
|
+
throw new InvalidRequestError(
|
|
41
|
+
`Must define at least one role for tag ${key}`,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (val['moderators']) {
|
|
47
|
+
if (!Array.isArray(val['moderators'])) {
|
|
48
|
+
throw new InvalidRequestError(
|
|
49
|
+
`Moderators must be an array of moderator DIDs for tag ${key}`,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
if (!val['moderators']?.length) {
|
|
53
|
+
throw new InvalidRequestError(
|
|
54
|
+
`Must define at least one moderator DID for tag ${key}`,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
])
|
|
@@ -197,3 +197,126 @@ Array [
|
|
|
197
197
|
},
|
|
198
198
|
]
|
|
199
199
|
`;
|
|
200
|
+
|
|
201
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 1`] = `
|
|
202
|
+
Array [
|
|
203
|
+
Object {
|
|
204
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
205
|
+
"createdBy": "user(1)",
|
|
206
|
+
"creatorHandle": "bob.test",
|
|
207
|
+
"event": Object {
|
|
208
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
209
|
+
"comment": "rainy days feel lazy",
|
|
210
|
+
"isReporterMuted": false,
|
|
211
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
212
|
+
},
|
|
213
|
+
"id": 16,
|
|
214
|
+
"subject": Object {
|
|
215
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
216
|
+
"did": "user(0)",
|
|
217
|
+
},
|
|
218
|
+
"subjectBlobCids": Array [],
|
|
219
|
+
"subjectHandle": "alice.test",
|
|
220
|
+
},
|
|
221
|
+
Object {
|
|
222
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
223
|
+
"createdBy": "user(1)",
|
|
224
|
+
"creatorHandle": "bob.test",
|
|
225
|
+
"event": Object {
|
|
226
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
227
|
+
"comment": "november rain",
|
|
228
|
+
"isReporterMuted": false,
|
|
229
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
230
|
+
},
|
|
231
|
+
"id": 15,
|
|
232
|
+
"subject": Object {
|
|
233
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
234
|
+
"did": "user(0)",
|
|
235
|
+
},
|
|
236
|
+
"subjectBlobCids": Array [],
|
|
237
|
+
"subjectHandle": "alice.test",
|
|
238
|
+
},
|
|
239
|
+
]
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 2`] = `
|
|
243
|
+
Array [
|
|
244
|
+
Object {
|
|
245
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
246
|
+
"createdBy": "user(1)",
|
|
247
|
+
"creatorHandle": "bob.test",
|
|
248
|
+
"event": Object {
|
|
249
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
250
|
+
"comment": "rainy days feel lazy",
|
|
251
|
+
"isReporterMuted": false,
|
|
252
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
253
|
+
},
|
|
254
|
+
"id": 16,
|
|
255
|
+
"subject": Object {
|
|
256
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
257
|
+
"did": "user(0)",
|
|
258
|
+
},
|
|
259
|
+
"subjectBlobCids": Array [],
|
|
260
|
+
"subjectHandle": "alice.test",
|
|
261
|
+
},
|
|
262
|
+
Object {
|
|
263
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
264
|
+
"createdBy": "user(1)",
|
|
265
|
+
"creatorHandle": "bob.test",
|
|
266
|
+
"event": Object {
|
|
267
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
268
|
+
"comment": "november rain",
|
|
269
|
+
"isReporterMuted": false,
|
|
270
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
271
|
+
},
|
|
272
|
+
"id": 15,
|
|
273
|
+
"subject": Object {
|
|
274
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
275
|
+
"did": "user(0)",
|
|
276
|
+
},
|
|
277
|
+
"subjectBlobCids": Array [],
|
|
278
|
+
"subjectHandle": "alice.test",
|
|
279
|
+
},
|
|
280
|
+
]
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 3`] = `
|
|
284
|
+
Array [
|
|
285
|
+
Object {
|
|
286
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
287
|
+
"createdBy": "user(1)",
|
|
288
|
+
"creatorHandle": "bob.test",
|
|
289
|
+
"event": Object {
|
|
290
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
291
|
+
"comment": "rainy days feel lazy",
|
|
292
|
+
"isReporterMuted": false,
|
|
293
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
294
|
+
},
|
|
295
|
+
"id": 16,
|
|
296
|
+
"subject": Object {
|
|
297
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
298
|
+
"did": "user(0)",
|
|
299
|
+
},
|
|
300
|
+
"subjectBlobCids": Array [],
|
|
301
|
+
"subjectHandle": "alice.test",
|
|
302
|
+
},
|
|
303
|
+
Object {
|
|
304
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
305
|
+
"createdBy": "user(1)",
|
|
306
|
+
"creatorHandle": "bob.test",
|
|
307
|
+
"event": Object {
|
|
308
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
309
|
+
"comment": "november rain",
|
|
310
|
+
"isReporterMuted": false,
|
|
311
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
312
|
+
},
|
|
313
|
+
"id": 15,
|
|
314
|
+
"subject": Object {
|
|
315
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
316
|
+
"did": "user(0)",
|
|
317
|
+
},
|
|
318
|
+
"subjectBlobCids": Array [],
|
|
319
|
+
"subjectHandle": "alice.test",
|
|
320
|
+
},
|
|
321
|
+
]
|
|
322
|
+
`;
|
|
@@ -35,28 +35,28 @@ describe('moderation', () => {
|
|
|
35
35
|
cid: ref.cidStr,
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const getReviewStateBySubject = (subjects: SubjectStatusView[]) => {
|
|
39
|
+
const states = new Map<string, SubjectStatusView>()
|
|
40
|
+
|
|
41
|
+
subjects.forEach((item) => {
|
|
42
|
+
if (ComAtprotoRepoStrongRef.isMain(item.subject)) {
|
|
43
|
+
states.set(item.subject.uri, item)
|
|
44
|
+
} else if (isRepoRef(item.subject)) {
|
|
45
|
+
states.set(item.subject.did, item)
|
|
46
|
+
}
|
|
41
47
|
})
|
|
42
|
-
sc = network.getSeedClient()
|
|
43
|
-
modClient = network.ozone.getModClient()
|
|
44
|
-
await basicSeed(sc)
|
|
45
|
-
await network.processAll()
|
|
46
|
-
})
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
49
|
+
return states
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
const postOne = sc.posts[
|
|
54
|
-
const postTwo = sc.posts[
|
|
52
|
+
const reportUserAndPost = async (did: string) => {
|
|
53
|
+
const postOne = sc.posts[did][0].ref
|
|
54
|
+
const postTwo = sc.posts[did][1].ref
|
|
55
55
|
await Promise.all([
|
|
56
56
|
sc.createReport({
|
|
57
57
|
reasonType: REASONSPAM,
|
|
58
|
-
subject: repoSubject(
|
|
59
|
-
reportedBy: sc.dids.
|
|
58
|
+
subject: repoSubject(did),
|
|
59
|
+
reportedBy: sc.dids.carol,
|
|
60
60
|
}),
|
|
61
61
|
sc.createReport({
|
|
62
62
|
reasonType: REASONOTHER,
|
|
@@ -71,7 +71,6 @@ describe('moderation', () => {
|
|
|
71
71
|
reportedBy: sc.dids.carol,
|
|
72
72
|
}),
|
|
73
73
|
])
|
|
74
|
-
|
|
75
74
|
await modClient.emitEvent({
|
|
76
75
|
event: {
|
|
77
76
|
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
@@ -80,6 +79,26 @@ describe('moderation', () => {
|
|
|
80
79
|
subject: recordSubject(postTwo),
|
|
81
80
|
})
|
|
82
81
|
|
|
82
|
+
return { postOne, postTwo }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
beforeAll(async () => {
|
|
86
|
+
network = await TestNetwork.create({
|
|
87
|
+
dbPostgresSchema: 'ozone_ack_all_subjects_of_account',
|
|
88
|
+
})
|
|
89
|
+
sc = network.getSeedClient()
|
|
90
|
+
modClient = network.ozone.getModClient()
|
|
91
|
+
await basicSeed(sc)
|
|
92
|
+
await network.processAll()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
afterAll(async () => {
|
|
96
|
+
await network.close()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('acknowledges all open/escalated review subjects with takedown.', async () => {
|
|
100
|
+
const { postOne, postTwo } = await reportUserAndPost(sc.dids.bob)
|
|
101
|
+
|
|
83
102
|
const { subjectStatuses: statusesBefore } = await modClient.queryStatuses({
|
|
84
103
|
subject: sc.dids.bob,
|
|
85
104
|
includeAllUserRecords: true,
|
|
@@ -95,19 +114,46 @@ describe('moderation', () => {
|
|
|
95
114
|
includeAllUserRecords: true,
|
|
96
115
|
})
|
|
97
116
|
|
|
98
|
-
const
|
|
99
|
-
|
|
117
|
+
const reviewStatesBefore = getReviewStateBySubject(statusesBefore)
|
|
118
|
+
const reviewStatesAfter = getReviewStateBySubject(statusesAfter)
|
|
119
|
+
|
|
120
|
+
// Check that review states before were different for different subjects
|
|
121
|
+
expect(reviewStatesBefore.get(postOne.uriStr)?.reviewState).toBe(REVIEWOPEN)
|
|
122
|
+
expect(reviewStatesBefore.get(postTwo.uriStr)?.reviewState).toBe(
|
|
123
|
+
REVIEWESCALATED,
|
|
124
|
+
)
|
|
125
|
+
expect(reviewStatesBefore.get(sc.dids.bob)?.reviewState).toBe(REVIEWOPEN)
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
// Check that review states after are all closed
|
|
128
|
+
expect(reviewStatesAfter.get(postOne.uriStr)?.reviewState).toBe(
|
|
129
|
+
REVIEWCLOSED,
|
|
130
|
+
)
|
|
131
|
+
expect(reviewStatesAfter.get(postTwo.uriStr)?.reviewState).toBe(
|
|
132
|
+
REVIEWCLOSED,
|
|
133
|
+
)
|
|
134
|
+
expect(reviewStatesAfter.get(sc.dids.bob)?.reviewState).toBe(REVIEWCLOSED)
|
|
135
|
+
})
|
|
108
136
|
|
|
109
|
-
|
|
110
|
-
}
|
|
137
|
+
it('acknowledges all open/escalated review subjects with acknowledge.', async () => {
|
|
138
|
+
const { postOne, postTwo } = await reportUserAndPost(sc.dids.alice)
|
|
139
|
+
|
|
140
|
+
const { subjectStatuses: statusesBefore } = await modClient.queryStatuses({
|
|
141
|
+
subject: sc.dids.alice,
|
|
142
|
+
includeAllUserRecords: true,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
await modClient.emitEvent({
|
|
146
|
+
subject: repoSubject(sc.dids.alice),
|
|
147
|
+
event: {
|
|
148
|
+
$type: 'tools.ozone.moderation.defs#modEventAcknowledge',
|
|
149
|
+
acknowledgeAccountSubjects: true,
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const { subjectStatuses: statusesAfter } = await modClient.queryStatuses({
|
|
154
|
+
subject: sc.dids.alice,
|
|
155
|
+
includeAllUserRecords: true,
|
|
156
|
+
})
|
|
111
157
|
|
|
112
158
|
const reviewStatesBefore = getReviewStateBySubject(statusesBefore)
|
|
113
159
|
const reviewStatesAfter = getReviewStateBySubject(statusesAfter)
|
|
@@ -117,7 +163,7 @@ describe('moderation', () => {
|
|
|
117
163
|
expect(reviewStatesBefore.get(postTwo.uriStr)?.reviewState).toBe(
|
|
118
164
|
REVIEWESCALATED,
|
|
119
165
|
)
|
|
120
|
-
expect(reviewStatesBefore.get(sc.dids.
|
|
166
|
+
expect(reviewStatesBefore.get(sc.dids.alice)?.reviewState).toBe(REVIEWOPEN)
|
|
121
167
|
|
|
122
168
|
// Check that review states after are all closed
|
|
123
169
|
expect(reviewStatesAfter.get(postOne.uriStr)?.reviewState).toBe(
|
|
@@ -126,6 +172,6 @@ describe('moderation', () => {
|
|
|
126
172
|
expect(reviewStatesAfter.get(postTwo.uriStr)?.reviewState).toBe(
|
|
127
173
|
REVIEWCLOSED,
|
|
128
174
|
)
|
|
129
|
-
expect(reviewStatesAfter.get(sc.dids.
|
|
175
|
+
expect(reviewStatesAfter.get(sc.dids.alice)?.reviewState).toBe(REVIEWCLOSED)
|
|
130
176
|
})
|
|
131
177
|
})
|
|
@@ -224,6 +224,46 @@ describe('moderation-events', () => {
|
|
|
224
224
|
expect(eventsWithComment.events.length).toEqual(10)
|
|
225
225
|
})
|
|
226
226
|
|
|
227
|
+
it('returns events matching multiple keywords in comment', async () => {
|
|
228
|
+
await sc.createReport({
|
|
229
|
+
reasonType: REASONSPAM,
|
|
230
|
+
reason: 'november rain',
|
|
231
|
+
subject: {
|
|
232
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
233
|
+
did: sc.dids.alice,
|
|
234
|
+
},
|
|
235
|
+
reportedBy: sc.dids.bob,
|
|
236
|
+
})
|
|
237
|
+
await sc.createReport({
|
|
238
|
+
reasonType: REASONSPAM,
|
|
239
|
+
reason: 'rainy days feel lazy',
|
|
240
|
+
subject: {
|
|
241
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
242
|
+
did: sc.dids.alice,
|
|
243
|
+
},
|
|
244
|
+
reportedBy: sc.dids.bob,
|
|
245
|
+
})
|
|
246
|
+
const [eventsMatchingBothKeywords, unusedTrailingSeparator, extraSpaces] =
|
|
247
|
+
await Promise.all([
|
|
248
|
+
modClient.queryEvents({
|
|
249
|
+
hasComment: true,
|
|
250
|
+
comment: 'november||lazy',
|
|
251
|
+
}),
|
|
252
|
+
modClient.queryEvents({
|
|
253
|
+
hasComment: true,
|
|
254
|
+
comment: 'november||lazy||',
|
|
255
|
+
}),
|
|
256
|
+
modClient.queryEvents({
|
|
257
|
+
hasComment: true,
|
|
258
|
+
comment: '||november||lazy|| ',
|
|
259
|
+
}),
|
|
260
|
+
])
|
|
261
|
+
|
|
262
|
+
expect(forSnapshot(eventsMatchingBothKeywords.events)).toMatchSnapshot()
|
|
263
|
+
expect(forSnapshot(unusedTrailingSeparator.events)).toMatchSnapshot()
|
|
264
|
+
expect(forSnapshot(extraSpaces.events)).toMatchSnapshot()
|
|
265
|
+
})
|
|
266
|
+
|
|
227
267
|
it('returns events matching filter params for labels', async () => {
|
|
228
268
|
const [negatedLabelEvent, createdLabelEvent] = await Promise.all([
|
|
229
269
|
modClient.emitEvent({
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TestNetwork,
|
|
3
|
+
SeedClient,
|
|
4
|
+
basicSeed,
|
|
5
|
+
ModeratorClient,
|
|
6
|
+
} from '@atproto/dev-env'
|
|
7
|
+
import { ProtectedTagSettingKey } from '../src/setting/constants'
|
|
8
|
+
import {
|
|
9
|
+
ROLEADMIN,
|
|
10
|
+
ROLEMODERATOR,
|
|
11
|
+
} from '../dist/lexicon/types/tools/ozone/team/defs'
|
|
12
|
+
|
|
13
|
+
describe('protected-tags', () => {
|
|
14
|
+
let network: TestNetwork
|
|
15
|
+
let sc: SeedClient
|
|
16
|
+
let modClient: ModeratorClient
|
|
17
|
+
const basicSetting = {
|
|
18
|
+
key: ProtectedTagSettingKey,
|
|
19
|
+
scope: 'instance',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
network = await TestNetwork.create({
|
|
24
|
+
dbPostgresSchema: 'ozone_protected_tags',
|
|
25
|
+
})
|
|
26
|
+
sc = network.getSeedClient()
|
|
27
|
+
modClient = network.ozone.getModClient()
|
|
28
|
+
await basicSeed(sc)
|
|
29
|
+
await network.processAll()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
afterAll(async () => {
|
|
33
|
+
await network.close()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('Settings management', () => {
|
|
37
|
+
it('validates settings', async () => {
|
|
38
|
+
await expect(
|
|
39
|
+
modClient.upsertSettingOption({
|
|
40
|
+
...basicSetting,
|
|
41
|
+
managerRole: ROLEMODERATOR,
|
|
42
|
+
value: {
|
|
43
|
+
vip: {},
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
).rejects.toThrow(
|
|
47
|
+
'Only admins should be able to configure protected tags',
|
|
48
|
+
)
|
|
49
|
+
await expect(
|
|
50
|
+
modClient.upsertSettingOption({
|
|
51
|
+
...basicSetting,
|
|
52
|
+
managerRole: ROLEADMIN,
|
|
53
|
+
value: ['test'],
|
|
54
|
+
}),
|
|
55
|
+
).rejects.toThrow('Invalid configuration')
|
|
56
|
+
await expect(
|
|
57
|
+
modClient.upsertSettingOption({
|
|
58
|
+
...basicSetting,
|
|
59
|
+
managerRole: ROLEADMIN,
|
|
60
|
+
value: { vip: 'test' },
|
|
61
|
+
}),
|
|
62
|
+
).rejects.toThrow('Invalid configuration')
|
|
63
|
+
await expect(
|
|
64
|
+
modClient.upsertSettingOption({
|
|
65
|
+
...basicSetting,
|
|
66
|
+
managerRole: ROLEADMIN,
|
|
67
|
+
value: { vip: { weirdValue: 1 } },
|
|
68
|
+
}),
|
|
69
|
+
).rejects.toThrow(/Must define who a list of moderators or a role/gi)
|
|
70
|
+
await expect(
|
|
71
|
+
modClient.upsertSettingOption({
|
|
72
|
+
...basicSetting,
|
|
73
|
+
managerRole: ROLEADMIN,
|
|
74
|
+
value: { vip: { roles: 'test' } },
|
|
75
|
+
}),
|
|
76
|
+
).rejects.toThrow(/Roles must be an array of moderator/gi)
|
|
77
|
+
await expect(
|
|
78
|
+
modClient.upsertSettingOption({
|
|
79
|
+
...basicSetting,
|
|
80
|
+
managerRole: ROLEADMIN,
|
|
81
|
+
value: { vip: { roles: 'test' } },
|
|
82
|
+
}),
|
|
83
|
+
).rejects.toThrow(/Roles must be an array of moderator/gi)
|
|
84
|
+
await expect(
|
|
85
|
+
modClient.upsertSettingOption({
|
|
86
|
+
...basicSetting,
|
|
87
|
+
managerRole: ROLEADMIN,
|
|
88
|
+
value: { vip: { moderators: 1 } },
|
|
89
|
+
}),
|
|
90
|
+
).rejects.toThrow(/Moderators must be an array of moderator/gi)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
describe('Protected subject via tags', () => {
|
|
94
|
+
afterEach(async () => {
|
|
95
|
+
await modClient.removeSettingOptions({
|
|
96
|
+
keys: [ProtectedTagSettingKey],
|
|
97
|
+
scope: 'instance',
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
it('only allows configured roles to add/remove protected tags', async () => {
|
|
101
|
+
await modClient.upsertSettingOption({
|
|
102
|
+
...basicSetting,
|
|
103
|
+
managerRole: ROLEADMIN,
|
|
104
|
+
value: { vip: { roles: ['tools.ozone.team.defs#roleAdmin'] } },
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
await expect(
|
|
108
|
+
modClient.emitEvent({
|
|
109
|
+
subject: {
|
|
110
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
111
|
+
did: sc.dids.bob,
|
|
112
|
+
},
|
|
113
|
+
event: {
|
|
114
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
115
|
+
add: ['vip'],
|
|
116
|
+
remove: [],
|
|
117
|
+
},
|
|
118
|
+
}),
|
|
119
|
+
).rejects.toThrow(/Can not manage tag vip/gi)
|
|
120
|
+
|
|
121
|
+
await modClient.emitEvent(
|
|
122
|
+
{
|
|
123
|
+
subject: {
|
|
124
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
125
|
+
did: sc.dids.bob,
|
|
126
|
+
},
|
|
127
|
+
event: {
|
|
128
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
129
|
+
add: ['vip'],
|
|
130
|
+
remove: [],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
'admin',
|
|
134
|
+
)
|
|
135
|
+
await expect(
|
|
136
|
+
modClient.emitEvent({
|
|
137
|
+
subject: {
|
|
138
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
139
|
+
did: sc.dids.bob,
|
|
140
|
+
},
|
|
141
|
+
event: {
|
|
142
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
143
|
+
add: [],
|
|
144
|
+
remove: ['vip'],
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
).rejects.toThrow(/Can not manage tag vip/gi)
|
|
148
|
+
})
|
|
149
|
+
it('only allows configured moderators to add/remove protected tags', async () => {
|
|
150
|
+
await modClient.upsertSettingOption({
|
|
151
|
+
...basicSetting,
|
|
152
|
+
managerRole: ROLEADMIN,
|
|
153
|
+
value: { vip: { moderators: [network.ozone.adminAccnt.did] } },
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// By default, this query is made with moderator account's did
|
|
157
|
+
await expect(
|
|
158
|
+
modClient.emitEvent({
|
|
159
|
+
subject: {
|
|
160
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
161
|
+
did: sc.dids.bob,
|
|
162
|
+
},
|
|
163
|
+
event: {
|
|
164
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
165
|
+
add: ['vip'],
|
|
166
|
+
remove: [],
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
).rejects.toThrow(/Not allowed to manage tag: vip/gi)
|
|
170
|
+
|
|
171
|
+
await modClient.emitEvent(
|
|
172
|
+
{
|
|
173
|
+
subject: {
|
|
174
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
175
|
+
did: sc.dids.bob,
|
|
176
|
+
},
|
|
177
|
+
event: {
|
|
178
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
179
|
+
add: ['vip'],
|
|
180
|
+
remove: [],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
'admin',
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
await expect(
|
|
187
|
+
modClient.emitEvent({
|
|
188
|
+
subject: {
|
|
189
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
190
|
+
did: sc.dids.bob,
|
|
191
|
+
},
|
|
192
|
+
event: {
|
|
193
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
194
|
+
add: [],
|
|
195
|
+
remove: ['vip'],
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
).rejects.toThrow(/Not allowed to manage tag: vip/gi)
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
})
|