Skip to content

Commit 785f4df

Browse files
authored
Improve ServerBanSyrnchronisationProtection (#1014)
* Update ServerBanSynchronisationCapabilityRenderer. the-draupnir-project/planning#87. * Add migration for old server ban sync capabilities. the-draupnir-project/planning#87. * Test migration of serverConsequences capability provider set. the-draupnir-project/planning#87 * Update MPS for new ServerBanSychrnosation protection. - Only render results of changes when the ACL capability when the `m.room.server_acl` failed to send. - Only apply ACL once every 15seconds - Migrate `serverConsequences` from MPS to server ban synchronisation specific capability provider.
1 parent 81301f2 commit 785f4df

7 files changed

Lines changed: 259 additions & 91 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"@sinclair/typebox": "0.34.13",
5555
"@the-draupnir-project/interface-manager": "4.2.5",
5656
"@the-draupnir-project/matrix-basic-types": "1.4.1",
57-
"@the-draupnir-project/mps-interface-adaptor": "0.5.2",
57+
"@the-draupnir-project/mps-interface-adaptor": "0.5.3",
5858
"better-sqlite3": "^9.4.3",
5959
"body-parser": "^1.20.2",
6060
"config": "^3.3.9",
@@ -64,7 +64,7 @@
6464
"jsdom": "^24.0.0",
6565
"matrix-appservice-bridge": "^10.3.1",
6666
"matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@^0.7.1-element.6",
67-
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@5.1.0",
67+
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@6.0.0",
6868
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@4.0.2",
6969
"pg": "^8.8.0",
7070
"yaml": "^2.3.2"

src/capabilities/ServerACLConsequencesRenderer.tsx

Lines changed: 41 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import {
1212
ActionResult,
1313
Capability,
1414
DescriptionMeta,
15-
PolicyListRevisionIssuer,
1615
RoomSetResult,
17-
ServerACLConsequencesContext,
18-
ServerConsequences,
16+
ServerACLSynchronisationCapabilityContext,
17+
ServerBanIntentProjection,
18+
ServerBanSynchronisationCapability,
1919
describeCapabilityContextGlue,
2020
describeCapabilityRenderer,
2121
isError,
@@ -32,11 +32,13 @@ import {
3232
renderRoomSetResult,
3333
} from "@the-draupnir-project/mps-interface-adaptor";
3434

35-
class StandardServerConsequencesRenderer implements ServerConsequences {
35+
class StandardServerBanSynchronisationCapabilityRenderer
36+
implements ServerBanSynchronisationCapability
37+
{
3638
constructor(
3739
private readonly description: DescriptionMeta,
3840
private readonly messageCollector: RendererMessageCollector,
39-
private readonly capability: ServerConsequences
41+
private readonly capability: ServerBanSynchronisationCapability
4042
) {
4143
// nothing to do.
4244
}
@@ -45,20 +47,21 @@ class StandardServerConsequencesRenderer implements ServerConsequences {
4547
public readonly requiredPermissions = this.capability.requiredPermissions;
4648
public readonly requiredStatePermissions =
4749
this.capability.requiredStatePermissions;
48-
public async consequenceForServersInRoom(
50+
public async outcomeFromIntentInRoom(
4951
roomID: StringRoomID,
50-
issuer: PolicyListRevisionIssuer
52+
projection: ServerBanIntentProjection
5153
): Promise<ActionResult<boolean>> {
52-
const capabilityResult = await this.capability.consequenceForServersInRoom(
54+
const capabilityResult = await this.capability.outcomeFromIntentInRoom(
5355
roomID,
54-
issuer
56+
projection
5557
);
5658
const title = (
5759
<fragment>
5860
Setting server ACL in {Permalinks.forRoom(roomID)} as it is out of sync
5961
with watched policies.
6062
</fragment>
6163
);
64+
// only add the message if we failed, otherwise it's too spammy.
6265
if (isError(capabilityResult)) {
6366
this.messageCollector.addMessage(
6467
this.description,
@@ -71,21 +74,13 @@ class StandardServerConsequencesRenderer implements ServerConsequences {
7174
);
7275
return capabilityResult;
7376
}
74-
// only add the message if we changed anything in the room.
75-
if (capabilityResult.ok) {
76-
this.messageCollector.addOneliner(
77-
this.description,
78-
this.capability,
79-
title
80-
);
81-
}
8277
return capabilityResult;
8378
}
84-
public async consequenceForServersInRoomSet(
85-
issuer: PolicyListRevisionIssuer
79+
public async outcomeFromIntentInRoomSet(
80+
projection: ServerBanIntentProjection
8681
): Promise<ActionResult<RoomSetResult>> {
8782
const capabilityResult =
88-
await this.capability.consequenceForServersInRoomSet(issuer);
83+
await this.capability.outcomeFromIntentInRoomSet(projection);
8984
const title = <fragment>Updating server ACL in protected rooms.</fragment>;
9085
if (isError(capabilityResult)) {
9186
this.messageCollector.addMessage(
@@ -99,65 +94,31 @@ class StandardServerConsequencesRenderer implements ServerConsequences {
9994
);
10095
return capabilityResult;
10196
}
102-
this.messageCollector.addMessage(
103-
this.description,
104-
this.capability,
105-
renderRoomSetResult(capabilityResult.ok, {
106-
summary: (
107-
<fragment>
108-
<code>{this.description.name}</code>: {title}
109-
</fragment>
110-
),
111-
})
112-
);
113-
return capabilityResult;
114-
}
115-
public async unbanServerFromRoomSet(
116-
serverName: string,
117-
reason: string
118-
): Promise<ActionResult<RoomSetResult>> {
119-
const capabilityResult = await this.capability.unbanServerFromRoomSet(
120-
serverName,
121-
reason
122-
);
123-
const title = (
124-
<fragment>
125-
Removing {serverName} from denied servers in protected rooms.
126-
</fragment>
127-
);
128-
if (isError(capabilityResult)) {
97+
// Only show this when results are failing.
98+
if (!capabilityResult.ok.isEveryResultOk) {
12999
this.messageCollector.addMessage(
130100
this.description,
131101
this.capability,
132-
renderFailedSingularConsequence(
133-
this.description,
134-
title,
135-
capabilityResult.error
136-
)
102+
renderRoomSetResult(capabilityResult.ok, {
103+
summary: (
104+
<fragment>
105+
<code>{this.description.name}</code>: {title}
106+
</fragment>
107+
),
108+
showOnlyFailed: true,
109+
})
137110
);
138-
return capabilityResult;
139111
}
140-
this.messageCollector.addMessage(
141-
this.description,
142-
this.capability,
143-
renderRoomSetResult(capabilityResult.ok, {
144-
summary: (
145-
<fragment>
146-
<code>{this.description.name}</code>: {title}
147-
</fragment>
148-
),
149-
})
150-
);
151112
return capabilityResult;
152113
}
153114
}
154115

155-
describeCapabilityRenderer<ServerConsequences, Draupnir>({
156-
name: "ServerACLConsequences",
157-
description: "Render server consequences.",
158-
interface: "ServerConsequences",
116+
describeCapabilityRenderer<ServerBanSynchronisationCapability, Draupnir>({
117+
name: "ServerACLSynchronisationCapability",
118+
description: "Render the server ban capability.",
119+
interface: "ServerBanSynchronisationCapability",
159120
factory(description, draupnir, capability) {
160-
return new StandardServerConsequencesRenderer(
121+
return new StandardServerBanSynchronisationCapabilityRenderer(
161122
description,
162123
draupnir.capabilityMessageRenderer,
163124
capability
@@ -166,8 +127,11 @@ describeCapabilityRenderer<ServerConsequences, Draupnir>({
166127
isDefaultForInterface: true,
167128
});
168129

169-
describeCapabilityContextGlue<Draupnir, ServerACLConsequencesContext>({
170-
name: "ServerACLConsequences",
130+
describeCapabilityContextGlue<
131+
Draupnir,
132+
ServerACLSynchronisationCapabilityContext
133+
>({
134+
name: "ServerACLSynchronisationCapability",
171135
glueMethod: function (
172136
protectionDescription,
173137
draupnir,
@@ -180,15 +144,18 @@ describeCapabilityContextGlue<Draupnir, ServerACLConsequencesContext>({
180144
},
181145
});
182146

183-
describeCapabilityContextGlue<Draupnir, ServerACLConsequencesContext>({
184-
name: "SimulatedServerConsequences",
147+
describeCapabilityContextGlue<
148+
Draupnir,
149+
ServerACLSynchronisationCapabilityContext
150+
>({
151+
name: "SimulatedServerBanSynchronisationCapability",
185152
glueMethod: function (
186153
protectionDescription,
187154
draupnir,
188155
capabilityProvider
189156
): Capability {
190157
return capabilityProvider.factory(protectionDescription, {
191158
protectedRoomsSet: draupnir.protectedRoomsSet,
192-
} as ServerACLConsequencesContext);
159+
} as ServerACLSynchronisationCapabilityContext);
193160
},
194161
});

src/draupnirfactory/DraupnirProtectedRoomsSet.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
PolicyRoomManager,
2929
ProtectedRoomsConfig,
3030
ProtectedRoomsSet,
31+
ProtectionHandleRegistryDescription,
3132
ProtectionsManager,
3233
RoomJoiner,
3334
RoomMembershipManager,
@@ -47,7 +48,7 @@ import {
4748
BotSDKRoomStateConfigBackend,
4849
MatrixSendClient,
4950
} from "matrix-protection-suite-for-matrix-bot-sdk";
50-
import { DefaultEnabledProtectionsMigration } from "../protections/DefaultEnabledProtectionsMigration";
51+
import { DefaultEnabledProtectionsMigration } from "../protections/ConfigMigration/DefaultEnabledProtectionsMigration";
5152
import "../protections/DraupnirProtectionsIndex";
5253
import { IConfig } from "../config";
5354
import { runProtectionConfigHooks } from "../protections/ConfigHooks";
@@ -155,7 +156,8 @@ async function makeProtectionsManager(
155156
description.name
156157
)
157158
)
158-
)
159+
),
160+
ProtectionHandleRegistryDescription
159161
)
160162
);
161163
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
import { Ok, ResultError } from "@gnuxie/typescript-result";
6+
import {
7+
CapabilityProviderConfig,
8+
DRAUPNIR_SCHEMA_VERSION_KEY,
9+
Logger,
10+
SchemedDataManager,
11+
ServerACLSynchronisationCapability,
12+
SimulatedServerBanSynchronisationCapability,
13+
Value,
14+
} from "matrix-protection-suite";
15+
16+
const log = new Logger("CapabilitySetProviderMigration");
17+
18+
export async function serverBanSynchronisationCapabilityRename(
19+
input: CapabilityProviderConfig,
20+
toVersion: number
21+
) {
22+
// Annoyingly, having a record type on the top level mixed with known
23+
// properties is really terrible for typescript to deal with.
24+
if (!Value.Check(CapabilityProviderConfig, input)) {
25+
return ResultError.Result(
26+
`The data for the capability provider config is corrupted.`
27+
);
28+
}
29+
const oldServerConsequencesInterfaceName = "serverConsequences";
30+
const oldSimulatedServerConsequencesName = "SimulatedServerConsequences";
31+
const oldServerACLConsequencesName = "ServerACLConsequences";
32+
const oldServerConsequencesSet = input[oldServerConsequencesInterfaceName];
33+
if (oldServerConsequencesSet === undefined) {
34+
return Ok({
35+
...input,
36+
[DRAUPNIR_SCHEMA_VERSION_KEY]: toVersion,
37+
} as unknown as CapabilityProviderConfig);
38+
}
39+
log.debug(
40+
`Migrating capability provider from ${oldServerConsequencesInterfaceName} to ServerBanSynchronisationCapability`
41+
);
42+
const makeProviderSet = (
43+
capabilityName: string
44+
): CapabilityProviderConfig => {
45+
return {
46+
["ServerBanSynchronisationCapability"]: {
47+
capability_provider_name: capabilityName,
48+
},
49+
[DRAUPNIR_SCHEMA_VERSION_KEY]: toVersion,
50+
} as unknown as CapabilityProviderConfig;
51+
};
52+
switch (oldServerConsequencesSet.capability_provider_name) {
53+
case oldSimulatedServerConsequencesName:
54+
return Ok(
55+
makeProviderSet(SimulatedServerBanSynchronisationCapability.name)
56+
);
57+
case oldServerACLConsequencesName:
58+
return Ok(makeProviderSet(ServerACLSynchronisationCapability.name));
59+
default:
60+
// if someone has written their own custom thing, they probably need to know that we've
61+
// change the interface name.
62+
throw new TypeError(
63+
`Unknown capability provider name: ${oldServerConsequencesSet.capability_provider_name}`
64+
);
65+
}
66+
}
67+
68+
export const DefaultEnabledProtectionsMigration =
69+
new SchemedDataManager<CapabilityProviderConfig>([
70+
serverBanSynchronisationCapabilityRename,
71+
]);

src/protections/DefaultEnabledProtectionsMigration.ts renamed to src/protections/ConfigMigration/DefaultEnabledProtectionsMigration.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import {
1414
Value,
1515
findProtection,
1616
} from "matrix-protection-suite";
17-
import { RedactionSynchronisationProtection } from "./RedactionSynchronisation";
18-
import { PolicyChangeNotification } from "./PolicyChangeNotification";
19-
import { JoinRoomsOnInviteProtection } from "./invitation/JoinRoomsOnInviteProtection";
20-
import { RoomsSetBehaviour } from "./ProtectedRooms/RoomsSetBehaviourProtection";
21-
import { InvalidEventProtection } from "./InvalidEventProtection";
17+
import { RedactionSynchronisationProtection } from "../RedactionSynchronisation";
18+
import { PolicyChangeNotification } from "../PolicyChangeNotification";
19+
import { JoinRoomsOnInviteProtection } from "../invitation/JoinRoomsOnInviteProtection";
20+
import { RoomsSetBehaviour } from "../ProtectedRooms/RoomsSetBehaviourProtection";
21+
import { InvalidEventProtection } from "../InvalidEventProtection";
2222

2323
export const DefaultEnabledProtectionsMigration =
2424
new SchemedDataManager<MjolnirEnabledProtectionsEvent>([

0 commit comments

Comments
 (0)