Skip to content

Commit 58af9a2

Browse files
authored
Check if event is rejected before using it for auth (#421)
To be even more spec compliant, this adds a `IsRejected` type/function when resolving state. (Part 2 of #419)
1 parent 16e7431 commit 58af9a2

File tree

4 files changed

+80
-4
lines changed

4 files changed

+80
-4
lines changed

stateresolution.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ func ResolveConflicts(
331331
events []PDU,
332332
authEvents []PDU,
333333
userIDForSender spec.UserIDForSender,
334+
isRejectedFn IsRejected,
334335
) ([]PDU, error) {
335336
type stateKeyTuple struct {
336337
Type string
@@ -389,7 +390,7 @@ func ResolveConflicts(
389390
resolved = ResolveStateConflicts(conflicted, authEvents, userIDForSender)
390391
resolved = append(resolved, notConflicted...)
391392
case StateResV2:
392-
resolved = ResolveStateConflictsV2(conflicted, notConflicted, authEvents, userIDForSender)
393+
resolved = ResolveStateConflictsV2(conflicted, notConflicted, authEvents, userIDForSender, isRejectedFn)
393394
default:
394395
return nil, fmt.Errorf("unsupported state resolution algorithm %v", stateResAlgo)
395396
}

stateresolution_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func TestStateResV1(t *testing.T) {
4848

4949
authEvents := []PDU{a1, a2, a3}
5050

51-
resolved, err := ResolveConflicts(RoomVersionV1, conflicted, authEvents, UserIDForSenderTest)
51+
resolved, err := ResolveConflicts(RoomVersionV1, conflicted, authEvents, UserIDForSenderTest, isRejectedTest)
5252
if err != nil {
5353
t.Fatalf("failed to resolve conflicts: %s", err)
5454
}

stateresolutionv2.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,20 @@ type stateResolverV2 struct {
4646
resolvedMembers map[spec.SenderID]PDU // Resolved member events
4747
resolvedOthers map[StateKeyTuple]PDU // Resolved other events
4848
result []PDU // Final list of resolved events
49+
isRejectedFn IsRejected // Check if the given eventID is rejected
4950
}
5051

52+
// IsRejected should return if the given eventID is rejected or not.
53+
type IsRejected func(eventID string) bool
54+
5155
// ResolveStateConflicts takes a list of state events with conflicting state
5256
// keys and works out which event should be used for each state event. This
5357
// function returns the resolved state, including unconflicted state events.
5458
func ResolveStateConflictsV2(
55-
conflicted, unconflicted []PDU,
59+
conflicted, unconflicted,
5660
authEvents []PDU,
5761
userIDForSender spec.UserIDForSender,
62+
isRejectedFn IsRejected,
5863
) []PDU {
5964
// Prepare the state resolver.
6065
conflictedControlEvents := make([]PDU, 0, len(conflicted))
@@ -69,6 +74,7 @@ func ResolveStateConflictsV2(
6974
resolvedMembers: make(map[spec.SenderID]PDU, len(conflicted)),
7075
resolvedOthers: make(map[StateKeyTuple]PDU, len(conflicted)),
7176
result: make([]PDU, 0, len(conflicted)+len(unconflicted)),
77+
isRejectedFn: isRejectedFn,
7278
}
7379
var roomID *spec.RoomID
7480
if len(conflicted) > 0 {
@@ -459,6 +465,10 @@ func (r *stateResolverV2) authAndApplyEvents(events []PDU) {
459465
continue
460466
}
461467
if authEv.Type() == spec.MRoomMember && authEv.StateKeyEquals(needed) {
468+
// Don't use rejected events for auth
469+
if r.isRejectedFn(authEventID) {
470+
continue
471+
}
462472
_ = r.authProvider.AddEvent(authEv)
463473
}
464474
}

stateresolutionv2_test.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ func TestReverseTopologicalEventSorting(t *testing.T) {
426426
}
427427
}
428428

429+
func isRejectedTest(_ string) bool { return false }
430+
429431
func TestStateResolutionOtherEventDoesntOverpowerPowerEvent(t *testing.T) {
430432
eventJSONs := []string{
431433
/* create event */ `{"auth_events":[],"content":{"creator":"@anon-20220512_124253-1:localhost:8800","room_version":"6"},"depth":1,"hashes":{"sha256":"ej3MHt4EnQemwqnfLhgwN6RBArYc5JnWcZt1PI3m4hE"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":[],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"7Pu9f39yDWJtl8msrnz+sPSBEA2jOJ4tJsZ1Zb6Bi+vZQMzMWwT/U6GZipxQqaeJr0TpVMa7zq/YhivArRRbAA"}},"state_key":"","type":"m.room.create"}`,
@@ -463,6 +465,7 @@ func TestStateResolutionOtherEventDoesntOverpowerPowerEvent(t *testing.T) {
463465
unconflicted, // unconflicted set
464466
events, // full auth set
465467
UserIDForSenderTest,
468+
isRejectedTest,
466469
)
467470
t.Log("Resolved:")
468471
for k, v := range result {
@@ -489,6 +492,7 @@ func runStateResolutionV2(t *testing.T, additional []PDU, expected []string) {
489492
unconflicted, // unconflicted set
490493
input, // full auth set
491494
UserIDForSenderTest,
495+
isRejectedTest,
492496
)
493497

494498
t.Log("Result:")
@@ -571,7 +575,68 @@ func TestStateReset(t *testing.T) {
571575
"$IaXTO8US6DmRrPexxTucUaMoc7M0uwvMrOzWoFSsy5o": jr2Ev,
572576
}
573577

574-
resolved := ResolveStateConflictsV2(conflicted, unconflicted, authEvents, UserIDForSenderTest)
578+
resolved := ResolveStateConflictsV2(conflicted, unconflicted, authEvents, UserIDForSenderTest, isRejectedTest)
579+
unexpectedEvents := make(map[string]PDU)
580+
for _, resolvedEv := range resolved {
581+
if _, found := expectedEvents[resolvedEv.EventID()]; !found {
582+
unexpectedEvents[resolvedEv.EventID()] = resolvedEv
583+
continue
584+
}
585+
delete(expectedEvents, resolvedEv.EventID())
586+
}
587+
588+
if len(expectedEvents) > 0 {
589+
t.Error("Expected event missing after state resolution:")
590+
for evID, ev := range expectedEvents {
591+
t.Errorf("\t%s: %s", evID, ev.JSON())
592+
}
593+
}
594+
595+
if len(unexpectedEvents) > 0 {
596+
t.Error("Unexpected events after state resolution:")
597+
for evID, ev := range unexpectedEvents {
598+
t.Errorf("\t%s: %s", evID, ev.JSON())
599+
}
600+
}
601+
}
602+
603+
// Basically copy and paste of TestStateReset, with the difference
604+
// that the join event of Bob (bobJoinEv) is rejected when authing
605+
// the name change event. This results in Bob not being in the room.
606+
func TestStateResetWithRejectedJoin(t *testing.T) {
607+
// NOTE: The following events are taken from a Dendrite UT.
608+
createEv := mustParseEvent(t, []byte(`{"auth_events":[],"content":{"creator":"@1:test","room_version":"9"},"depth":1,"hashes":{"sha256":"OZriBeMNVoymY/JqjM3Ee6oKSfoWskCiy48dUq5crR8"},"origin":"test","origin_server_ts":1697134517143,"prev_events":[],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"eyVzDAWjFtEaDtcYD2aLOjwxYegJSRJTEg5eRkksL3rNJUB0nRim2iGGCznjNzTg3V84K4bmuIs41aR7A2TBBQ"}},"state_key":"","type":"m.room.create"}`))
609+
aliceJoinEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg"],"content":{"membership":"join"},"depth":2,"hashes":{"sha256":"xktNmFYn936RCil8B5h5Jfb+BFuyCKfUsOHCU92KHbE"},"origin":"test","origin_server_ts":1697134517143,"prev_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg"],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"cBaNa3UDzDE/TEH0ZmDciJj7aa5XOv8Ze1F+YGxea/TI86ivD6ULkylEl9+52A3kNC1/k8u7d7VZTDEMA/YZCw"}},"state_key":"@1:test","type":"m.room.member"}`))
610+
plEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc"],"content":{"ban":50,"events":{"m.room.avatar":50,"m.room.canonical_alias":50,"m.room.encryption":100,"m.room.history_visibility":100,"m.room.name":50,"m.room.power_levels":100,"m.room.server_acl":100,"m.room.tombstone":100},"events_default":0,"invite":0,"kick":50,"notifications":{"room":50},"redact":50,"state_default":50,"users":{"@1:test":100},"users_default":0},"depth":3,"hashes":{"sha256":"Tg8kVJh7Pam9Q9rkMa2qoPzkQ2febdBLGiB2dB+6aqQ"},"origin":"test","origin_server_ts":1697134517143,"prev_events":["$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc"],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"1BKFKdklWUkxeKn8+9lGUVRNYSTscFwP0JR6KPH/KvuqOdOOl896mIJ3lp9iLrrHFYEOP0+Tl/gWWjY0r4zoBA"}},"state_key":"","type":"m.room.power_levels"}`))
611+
jrEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0","$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc"],"content":{"join_rule":"public"},"depth":4,"hashes":{"sha256":"he+A0e/+4282sOZ0E6eXAGrQ7b2wAHfr4E/qMX2Sosg"},"origin":"test","origin_server_ts":1697134517144,"prev_events":["$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0"],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"tnX+YOaNhWipRTAsnXtxDeT0OLGQRhQN/cWF4cjLj92zjfyvGPIgoMXBpdFojq+0TiCsTPx673aWiYKFGap3AA"}},"state_key":"","type":"m.room.join_rules"}`))
612+
hisVisEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0","$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc"],"content":{"history_visibility":"shared"},"depth":5,"hashes":{"sha256":"keUaZvadB9S775FT2/WOog+XwqcquUMDLlsKjtx7HYI"},"origin":"test","origin_server_ts":1697134517144,"prev_events":["$ZDzKFnVFil6ea2QoMa5wpFW_RJe5kTEv2ZD4jctEWM4"],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"OX1cA63VvYy/xli+kzJqAy/0f20RKMxn8khqvWYry3bRQqXRN2Z+er3wMFo6dego9e37l1b4UWXnoFvAUYbaDA"}},"state_key":"","type":"m.room.history_visibility"}`))
613+
bobJoinEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$ZDzKFnVFil6ea2QoMa5wpFW_RJe5kTEv2ZD4jctEWM4","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0"],"content":{"membership":"join"},"depth":6,"hashes":{"sha256":"eMAArXQGPaJMbU22Bvgqzks1zLMiQTGXI28eT4CsEaM"},"origin":"test","origin_server_ts":1697134517145,"prev_events":["$KvMXxqhECWclFe58hgxr_s26ytJ57olFSMv2uVjbtSo"],"prev_state":[],"room_id":"!2:test","sender":"@2:test","signatures":{"test":{"ed25519:test":"TT+NLaNGzJbEe2B9AZ1rPUX/Af3S7rHpZK2Vqv3ioxrH7pzs7nDSs2+1G9+yxxjqLtybi1QuFbyGKAb2J/DmDA"}},"state_key":"@2:test","type":"m.room.member"}`))
614+
charlieJoinEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$ZDzKFnVFil6ea2QoMa5wpFW_RJe5kTEv2ZD4jctEWM4","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0"],"content":{"membership":"join"},"depth":7,"hashes":{"sha256":"lsD2HYq8ovMhfTznU4RH2vk/cZTa8nwQVM8LYyCTzZs"},"origin":"test","origin_server_ts":1697134517145,"prev_events":["$AbMj-EZa-blPfjNYj9dPUXAQM5oLxcnPdoUvtTVI-hs"],"prev_state":[],"room_id":"!2:test","sender":"@3:test","signatures":{"test":{"ed25519:test":"5wOjA9IZkN28/yd4bJl8elnsxGR9QjDzKhSxDq/Js24hnqErV0DRB4luETZZDTWdCJJPRy5q7mIOoCTr6Zv/DQ"}},"state_key":"@3:test","type":"m.room.member"}`))
615+
bobNameEv := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$ZDzKFnVFil6ea2QoMa5wpFW_RJe5kTEv2ZD4jctEWM4","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0","$AbMj-EZa-blPfjNYj9dPUXAQM5oLxcnPdoUvtTVI-hs"],"content":{"displayname":"Bob!","membership":"join"},"depth":10,"hashes":{"sha256":"GKm317RAdHnrAnESQRocRS5DhSJ756/3UjxBskjKEz4"},"origin":"test","origin_server_ts":1697134517386,"prev_events":["$sd5vMK06VQ28CEnyUfZuJbiD-HXSsQXAS2-6Mm906qk"],"prev_state":[],"room_id":"!2:test","sender":"@2:test","signatures":{"test":{"ed25519:test":"oXQalkq0zQ28+KFueHLTD+hXu2oq4/MgN9w2jThn79IICDl+RDp0svYzqaYRahwbBDpTjPvWXjjiN6oJ4Z53Bw"}},"state_key":"@2:test","type":"m.room.member"}`))
616+
jr2Ev := mustParseEvent(t, []byte(`{"auth_events":["$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg","$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0","$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc"],"content":{"join_rule":"invite"},"depth":11,"hashes":{"sha256":"HtEx3O2ORo/V5Q3FHQVuPi8AvXts5tcMV5BbmsHcm+E"},"origin":"test","origin_server_ts":1697134517410,"prev_events":["$7w83ropQafHxxgMcJFWgynFXN_f1L-DgVHUPYC8KYVY"],"prev_state":[],"room_id":"!2:test","sender":"@1:test","signatures":{"test":{"ed25519:test":"5euVd5N/01mjrYQoNLlSnQWqonb2DUPSwQ4ZQz0A34fQzV3z+QjsBaIByYh2ZwkhFHnmJUvC8rUH8+JSv28NAA"}},"state_key":"","type":"m.room.join_rules"}`))
617+
618+
// conflicted/unconflicted as calculated by Dendrite
619+
conflicted := []PDU{bobJoinEv, bobNameEv}
620+
unconflicted := []PDU{createEv, aliceJoinEv, plEv, jrEv, hisVisEv, charlieJoinEv, jr2Ev}
621+
authEvents := append(unconflicted, conflicted...)
622+
623+
// The events we expect after state resolution
624+
expectedEvents := map[string]PDU{
625+
"$0B4FVZWbziXiuaBZyVerHDfBs40toK4FhoT1DNLs_tg": createEv,
626+
"$pha7iGLaAXqkf_GAwBhPtdyjM0DF4qxiAbbc-zGJbRc": aliceJoinEv,
627+
"$B6yTeN_9fWhp5duir471Ac-OSC9BlsHnlRcbVpfmOH0": plEv,
628+
"$KvMXxqhECWclFe58hgxr_s26ytJ57olFSMv2uVjbtSo": hisVisEv,
629+
"$9IhOwrJzpmcAKaq05_MmKq7mqBUZENVoFOYz1_GQgX4": charlieJoinEv,
630+
"$IaXTO8US6DmRrPexxTucUaMoc7M0uwvMrOzWoFSsy5o": jr2Ev,
631+
}
632+
633+
// The join event of Bob is rejected, so don't use it to auth the name change event
634+
// This results in Bob not being in the room.
635+
var isRejected = func(eventID string) bool {
636+
return true
637+
}
638+
639+
resolved := ResolveStateConflictsV2(conflicted, unconflicted, authEvents, UserIDForSenderTest, isRejected)
575640
unexpectedEvents := make(map[string]PDU)
576641
for _, resolvedEv := range resolved {
577642
if _, found := expectedEvents[resolvedEv.EventID()]; !found {

0 commit comments

Comments
 (0)