// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package auth import ( "encoding/json" "fmt" "github.com/matrix-org/gomatrixserverlib" ) const ( shared = "shared" ) // TODO: This logic should live in gomatrixserverlib // IsServerAllowed returns true if the server is allowed to see events in the room // at this particular state. This function implements https://matrix.org/docs/spec/client_server/r0.6.0#id87 func IsServerAllowed( serverName gomatrixserverlib.ServerName, serverCurrentlyInRoom bool, authEvents []gomatrixserverlib.Event, ) bool { var hisVisEvent *gomatrixserverlib.Event for i, ae := range authEvents { if ae.Type() == gomatrixserverlib.MRoomHistoryVisibility && ae.StateKeyEquals("") { hisVisEvent = &authEvents[i] break } } historyVisibility, err := HistoryVisibilityForRoom(hisVisEvent) if err != nil { return false } // 1. If the history_visibility was set to world_readable, allow. if historyVisibility == "world_readable" { return true } // 2. If the user's membership was join, allow. joinedUserExists := IsAnyUserOnServerWithMembership(serverName, authEvents, gomatrixserverlib.Join) if joinedUserExists { return true } // 3. If history_visibility was set to shared, and the user joined the room at any point after the event was sent, allow. if historyVisibility == shared && serverCurrentlyInRoom { return true } // 4. If the user's membership was invite, and the history_visibility was set to invited, allow. invitedUserExists := IsAnyUserOnServerWithMembership(serverName, authEvents, gomatrixserverlib.Invite) if invitedUserExists && historyVisibility == "invited" { return true } // 5. Otherwise, deny. return false } func HistoryVisibilityForRoom(hisVisEvent *gomatrixserverlib.Event) (string, error) { // https://matrix.org/docs/spec/client_server/r0.6.0#id87 // By default if no history_visibility is set, or if the value is not understood, the visibility is assumed to be shared. if hisVisEvent == nil { return shared, nil } visibility := shared knownStates := []string{"invited", "joined", shared, "world_readable"} if hisVisEvent.Type() != gomatrixserverlib.MRoomHistoryVisibility { return "", fmt.Errorf("HistoryVisibilityForRoom: passed a non history visibility event: %s", hisVisEvent.Type()) } // TODO: This should be HistoryVisibilityContent to match things like 'MemberContent'. Do this when moving to GMSL content := struct { HistoryVisibility string `json:"history_visibility"` }{} if err := json.Unmarshal(hisVisEvent.Content(), &content); err != nil { return visibility, nil // value is not understood } for _, s := range knownStates { if s == content.HistoryVisibility { visibility = s break } } return visibility, nil } func IsAnyUserOnServerWithMembership(serverName gomatrixserverlib.ServerName, authEvents []gomatrixserverlib.Event, wantMembership string) bool { for _, ev := range authEvents { membership, err := ev.Membership() if err != nil || membership != wantMembership { continue } stateKey := ev.StateKey() if stateKey == nil { continue } _, domain, err := gomatrixserverlib.SplitID('@', *stateKey) if err != nil { continue } if domain == serverName { return true } } return false }