diff --git a/vendor/manifest b/vendor/manifest index 35065c88a..fe5d5bbc4 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -92,13 +92,13 @@ { "importpath": "github.com/matrix-org/gomatrixserverlib", "repository": "https://github.com/matrix-org/gomatrixserverlib", - "revision": "48ee56a33d195dc412dd919a0e81af70c9aaf4a3", + "revision": "ce2ae9c5812346444b0ca75d57834794cde03fb7", "branch": "master" }, { "importpath": "github.com/matrix-org/util", "repository": "https://github.com/matrix-org/util", - "revision": "28bd7491c8aafbf346ca23821664f0f9911ef52b", + "revision": "ec8896cd7d9ba6de6143c5f123d1e45413657e7d", "branch": "master" }, { @@ -206,4 +206,4 @@ "branch": "master" } ] -} \ No newline at end of file +} diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go index f7595c796..e1b6ee7d8 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go @@ -326,6 +326,21 @@ func (e Event) Depth() int64 { return e.fields.Depth } +// UnmarshalJSON implements json.Unmarshaller assuming the Event is from an untrusted source. +// This will cause more checks than might be necessary but is probably better to be safe than sorry. +func (e *Event) UnmarshalJSON(data []byte) (err error) { + *e, err = NewEventFromUntrustedJSON(data) + return +} + +// MarshalJSON implements json.Marshaller +func (e Event) MarshalJSON() ([]byte, error) { + if e.eventJSON == nil { + return nil, fmt.Errorf("gomatrixserverlib: cannot serialise uninitialised Event") + } + return e.eventJSON, nil +} + // UnmarshalJSON implements json.Unmarshaller func (er *EventReference) UnmarshalJSON(data []byte) error { var tuple []rawJSON diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go index 151c6b99e..913e652a9 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go @@ -18,7 +18,8 @@ package gomatrixserverlib import ( "encoding/json" "fmt" - "sort" + + "github.com/matrix-org/util" ) const ( @@ -43,105 +44,108 @@ type StateNeeded struct { ThirdPartyInvite []string } -// StateNeededForAuth returns the event types and state_keys needed to authenticate an event. -// This takes a list of events to facilitate bulk processing when doing auth checks as part of state conflict resolution. -func StateNeededForAuth(events []Event) (result StateNeeded) { - var members []string - var thirdpartyinvites []string - - for _, event := range events { - switch event.Type() { - case "m.room.create": - // The create event doesn't require any state to authenticate. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L123 - case "m.room.aliases": - // Alias events need: - // * The create event. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L128 - // Alias events need no further authentication. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L160 - result.Create = true - case "m.room.member": - // Member events need: - // * The previous membership of the target. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L355 - // * The current membership state of the sender. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L348 - // * The join rules for the room if the event is a join event. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L361 - // * The power levels for the room. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L370 - // * And optionally may require a m.third_party_invite event - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L393 - content, err := newMemberContentFromEvent(event) - if err != nil { - // If we hit an error decoding the content we ignore it here. - // The event will be rejected when the actual checks encounter the same error. - continue - } - result.Create = true - result.PowerLevels = true - stateKey := event.StateKey() - if stateKey != nil { - members = append(members, event.Sender(), *stateKey) - } - if content.Membership == join { - result.JoinRules = true - } - if content.ThirdPartyInvite != nil { - token, err := thirdPartyInviteToken(content.ThirdPartyInvite) - if err != nil { - // If we hit an error decoding the content we ignore it here. - // The event will be rejected when the actual checks encounter the same error. - continue - } else { - thirdpartyinvites = append(thirdpartyinvites, token) - } - } - - default: - // All other events need: - // * The membership of the sender. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L177 - // * The power levels for the room. - // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L196 - result.Create = true - result.PowerLevels = true - members = append(members, event.Sender()) +// StateNeededForEventBuilder returns the event types and state_keys needed to authenticate the +// event being built. These events should be put under 'auth_events' for the event being built. +// Returns an error if the state needed could not be calculated with the given builder, e.g +// if there is a m.room.member without a membership key. +func StateNeededForEventBuilder(builder *EventBuilder) (result StateNeeded, err error) { + // Extract the 'content' object from the event if it is m.room.member as we need to know 'membership' + var content *memberContent + if builder.Type == "m.room.member" { + if err = json.Unmarshal(builder.content, &content); err != nil { + err = errorf("unparsable member event content: %s", err.Error()) + return } } - - // Deduplicate the state keys. - sort.Strings(members) - result.Member = members[:unique(sort.StringSlice(members))] - sort.Strings(thirdpartyinvites) - result.ThirdPartyInvite = thirdpartyinvites[:unique(sort.StringSlice(thirdpartyinvites))] + err = accumulateStateNeeded(&result, builder.Type, builder.Sender, builder.StateKey, content) + result.Member = util.UniqueStrings(result.Member) + result.ThirdPartyInvite = util.UniqueStrings(result.ThirdPartyInvite) return } -// Remove duplicate items from a sorted list. -// Takes the same interface as sort.Sort -// Returns the length of the data without duplicates -// Uses the last occurrence of a duplicate. -// O(n). -func unique(data sort.Interface) int { - length := data.Len() - if length == 0 { - return 0 - } - j := 0 - for i := 1; i < length; i++ { - if data.Less(i-1, i) { - data.Swap(i-1, j) - j++ +// StateNeededForAuth returns the event types and state_keys needed to authenticate an event. +// This takes a list of events to facilitate bulk processing when doing auth checks as part of state conflict resolution. +func StateNeededForAuth(events []Event) (result StateNeeded) { + for _, event := range events { + // Extract the 'content' object from the event if it is m.room.member as we need to know 'membership' + var content *memberContent + if event.Type() == "m.room.member" { + c, err := newMemberContentFromEvent(event) + if err == nil { + content = &c + } } + // Ignore errors when accumulating state needed. + // The event will be rejected when the actual checks encounter the same error. + _ = accumulateStateNeeded(&result, event.Type(), event.Sender(), event.StateKey(), content) } - data.Swap(length-1, j) - return j + 1 + + // Deduplicate the state keys. + result.Member = util.UniqueStrings(result.Member) + result.ThirdPartyInvite = util.UniqueStrings(result.ThirdPartyInvite) + return +} + +func accumulateStateNeeded(result *StateNeeded, eventType, sender string, stateKey *string, content *memberContent) (err error) { + switch eventType { + case "m.room.create": + // The create event doesn't require any state to authenticate. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L123 + case "m.room.aliases": + // Alias events need: + // * The create event. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L128 + // Alias events need no further authentication. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L160 + result.Create = true + case "m.room.member": + // Member events need: + // * The previous membership of the target. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L355 + // * The current membership state of the sender. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L348 + // * The join rules for the room if the event is a join event. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L361 + // * The power levels for the room. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L370 + // * And optionally may require a m.third_party_invite event + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L393 + if content == nil { + err = errorf("missing memberContent for m.room.member event") + return + } + result.Create = true + result.PowerLevels = true + if stateKey != nil { + result.Member = append(result.Member, sender, *stateKey) + } + if content.Membership == join { + result.JoinRules = true + } + if content.ThirdPartyInvite != nil { + token, tokErr := thirdPartyInviteToken(content.ThirdPartyInvite) + if tokErr != nil { + err = errorf("could not get third-party token: %s", tokErr) + return + } + result.ThirdPartyInvite = append(result.ThirdPartyInvite, token) + } + + default: + // All other events need: + // * The membership of the sender. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L177 + // * The power levels for the room. + // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L196 + result.Create = true + result.PowerLevels = true + result.Member = append(result.Member, sender) + } + return } // thirdPartyInviteToken extracts the token from the third_party_invite. -func thirdPartyInviteToken(thirdPartyInviteData json.RawMessage) (string, error) { +func thirdPartyInviteToken(thirdPartyInviteData rawJSON) (string, error) { var thirdPartyInvite struct { Signed struct { Token string `json:"token"` diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go index ae9851146..71822e6d9 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go @@ -68,7 +68,7 @@ func (tel *testEventList) UnmarshalJSON(data []byte) error { return nil } -func testStateNeededForAuth(t *testing.T, eventdata string, want StateNeeded) { +func testStateNeededForAuth(t *testing.T, eventdata string, builder *EventBuilder, want StateNeeded) { var events testEventList if err := json.Unmarshal([]byte(eventdata), &events); err != nil { panic(err) @@ -77,11 +77,24 @@ func testStateNeededForAuth(t *testing.T, eventdata string, want StateNeeded) { if !stateNeededEquals(got, want) { t.Errorf("Testing StateNeededForAuth(%#v), wanted %#v got %#v", events, want, got) } + if builder != nil { + got, err := StateNeededForEventBuilder(builder) + if !stateNeededEquals(got, want) { + t.Errorf("Testing StateNeededForEventBuilder(%#v), wanted %#v got %#v", events, want, got) + } + if err != nil { + panic(err) + } + } } func TestStateNeededForCreate(t *testing.T) { // Create events don't need anything. - testStateNeededForAuth(t, `[{"type": "m.room.create"}]`, StateNeeded{}) + skey := "" + testStateNeededForAuth(t, `[{"type": "m.room.create"}]`, &EventBuilder{ + Type: "m.room.create", + StateKey: &skey, + }, StateNeeded{}) } func TestStateNeededForMessage(t *testing.T) { @@ -89,7 +102,10 @@ func TestStateNeededForMessage(t *testing.T) { testStateNeededForAuth(t, `[{ "type": "m.room.message", "sender": "@u1:a" - }]`, StateNeeded{ + }]`, &EventBuilder{ + Type: "m.room.message", + Sender: "@u1:a", + }, StateNeeded{ Create: true, PowerLevels: true, Member: []string{"@u1:a"}, @@ -98,18 +114,27 @@ func TestStateNeededForMessage(t *testing.T) { func TestStateNeededForAlias(t *testing.T) { // Alias events need only the create event. - testStateNeededForAuth(t, `[{"type": "m.room.aliases"}]`, StateNeeded{ + testStateNeededForAuth(t, `[{"type": "m.room.aliases"}]`, &EventBuilder{ + Type: "m.room.aliases", + }, StateNeeded{ Create: true, }) } func TestStateNeededForJoin(t *testing.T) { + skey := "@u1:a" + b := EventBuilder{ + Type: "m.room.member", + StateKey: &skey, + Sender: "@u1:a", + } + b.SetContent(memberContent{"join", nil}) testStateNeededForAuth(t, `[{ "type": "m.room.member", "state_key": "@u1:a", "sender": "@u1:a", "content": {"membership": "join"} - }]`, StateNeeded{ + }]`, &b, StateNeeded{ Create: true, JoinRules: true, PowerLevels: true, @@ -118,12 +143,19 @@ func TestStateNeededForJoin(t *testing.T) { } func TestStateNeededForInvite(t *testing.T) { + skey := "@u2:b" + b := EventBuilder{ + Type: "m.room.member", + StateKey: &skey, + Sender: "@u1:a", + } + b.SetContent(memberContent{"invite", nil}) testStateNeededForAuth(t, `[{ "type": "m.room.member", "state_key": "@u2:b", "sender": "@u1:a", "content": {"membership": "invite"} - }]`, StateNeeded{ + }]`, &b, StateNeeded{ Create: true, PowerLevels: true, Member: []string{"@u1:a", "@u2:b"}, @@ -131,6 +163,13 @@ func TestStateNeededForInvite(t *testing.T) { } func TestStateNeededForInvite3PID(t *testing.T) { + skey := "@u2:b" + b := EventBuilder{ + Type: "m.room.member", + StateKey: &skey, + Sender: "@u1:a", + } + b.SetContent(memberContent{"invite", rawJSON(`{"signed":{"token":"my_token"}}`)}) testStateNeededForAuth(t, `[{ "type": "m.room.member", "state_key": "@u2:b", @@ -143,7 +182,7 @@ func TestStateNeededForInvite3PID(t *testing.T) { } } } - }]`, StateNeeded{ + }]`, &b, StateNeeded{ Create: true, PowerLevels: true, Member: []string{"@u1:a", "@u2:b"}, diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go index 2d61d5d52..c355f8b7a 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go @@ -108,7 +108,7 @@ type memberContent struct { // We use the membership key in order to check if the user is in the room. Membership string `json:"membership"` // We use the third_party_invite key to special case thirdparty invites. - ThirdPartyInvite json.RawMessage `json:"third_party_invite"` + ThirdPartyInvite rawJSON `json:"third_party_invite,omitempty"` } // newMemberContentFromAuthEvents loads the member content from the member event for the user ID in the auth events. diff --git a/vendor/src/github.com/matrix-org/util/json.go b/vendor/src/github.com/matrix-org/util/json.go index 46c5396f5..3323b526b 100644 --- a/vendor/src/github.com/matrix-org/util/json.go +++ b/vendor/src/github.com/matrix-org/util/json.go @@ -80,7 +80,7 @@ func Protect(handler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { defer func() { if r := recover(); r != nil { - logger := req.Context().Value(ctxValueLogger).(*log.Entry) + logger := GetLogger(req.Context()) logger.WithFields(log.Fields{ "panic": r, }).Errorf( @@ -108,7 +108,7 @@ func MakeJSONAPI(handler JSONRequestHandler) http.HandlerFunc { ctx = context.WithValue(ctx, ctxValueRequestID, reqID) req = req.WithContext(ctx) - logger := req.Context().Value(ctxValueLogger).(*log.Entry) + logger := GetLogger(req.Context()) logger.Print("Incoming request") res := handler.OnIncomingRequest(req) @@ -122,7 +122,7 @@ func MakeJSONAPI(handler JSONRequestHandler) http.HandlerFunc { } func respond(w http.ResponseWriter, req *http.Request, res JSONResponse) { - logger := req.Context().Value(ctxValueLogger).(*log.Entry) + logger := GetLogger(req.Context()) // Set custom headers if res.Headers != nil { diff --git a/vendor/src/github.com/matrix-org/util/json_test.go b/vendor/src/github.com/matrix-org/util/json_test.go index 3ce03a883..aeb5a9e55 100644 --- a/vendor/src/github.com/matrix-org/util/json_test.go +++ b/vendor/src/github.com/matrix-org/util/json_test.go @@ -194,6 +194,28 @@ func TestProtect(t *testing.T) { } } +func TestProtectWithoutLogger(t *testing.T) { + log.SetLevel(log.PanicLevel) // suppress logs in test output + mockWriter := httptest.NewRecorder() + mockReq, _ := http.NewRequest("GET", "http://example.com/foo", nil) + h := Protect(func(w http.ResponseWriter, req *http.Request) { + panic("oh noes!") + }) + + h(mockWriter, mockReq) + + expectCode := 500 + if mockWriter.Code != expectCode { + t.Errorf("TestProtect wanted HTTP status %d, got %d", expectCode, mockWriter.Code) + } + + expectBody := `{"message":"Internal Server Error"}` + actualBody := mockWriter.Body.String() + if actualBody != expectBody { + t.Errorf("TestProtect wanted body %s, got %s", expectBody, actualBody) + } +} + func TestWithCORSOptions(t *testing.T) { log.SetLevel(log.PanicLevel) // suppress logs in test output mockWriter := httptest.NewRecorder()