diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/membership_table.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/membership_table.go index f11193d82..3c4e8d3af 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/membership_table.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/membership_table.go @@ -30,6 +30,11 @@ CREATE TABLE IF NOT EXISTS memberships ( room_id TEXT NOT NULL, -- The ID of the join membership event event_id TEXT NOT NULL, + -- Is the user still in the room? We use a boolean instead of just deleting + -- the row to know if the user has been in the room at some point + -- If set to false, event_id would be the event ID of the leave/kick/ban + -- membership event + still_in_room BOOLEAN NOT NULL, -- A user can only be member of a room once PRIMARY KEY (localpart, room_id) @@ -40,24 +45,26 @@ CREATE UNIQUE INDEX IF NOT EXISTS membership_event_id ON memberships(event_id); ` const insertMembershipSQL = ` - INSERT INTO memberships(localpart, room_id, event_id) VALUES ($1, $2, $3) - ON CONFLICT (localpart, room_id) DO UPDATE SET event_id = EXCLUDED.event_id + INSERT INTO memberships(localpart, room_id, event_id, still_in_room) + VALUES ($1, $2, $3, $4) + ON CONFLICT (localpart, room_id) DO + UPDATE SET event_id = EXCLUDED.event_id, still_in_room = EXCLUDED.still_in_room ` const selectMembershipSQL = "" + "SELECT * from memberships WHERE localpart = $1 AND room_id = $2" const selectMembershipsByLocalpartSQL = "" + - "SELECT room_id, event_id FROM memberships WHERE localpart = $1" + "SELECT room_id, event_id FROM memberships WHERE localpart = $1 AND still_in_room = true" const selectMembershipsByRoomIDSQL = "" + - "SELECT localpart, event_id FROM memberships WHERE room_id = $1" + "SELECT localpart, event_id FROM memberships WHERE room_id = $1 AND still_in_room = true" const deleteMembershipsByEventIDsSQL = "" + "DELETE FROM memberships WHERE event_id = ANY($1)" const updateMembershipByEventIDSQL = "" + - "UPDATE memberships SET event_id = $2 WHERE event_id = $1" + "UPDATE memberships SET event_id = $2, still_in_room = $3 WHERE event_id = $1" type membershipStatements struct { deleteMembershipsByEventIDsStmt *sql.Stmt @@ -91,8 +98,8 @@ func (s *membershipStatements) prepare(db *sql.DB) (err error) { return } -func (s *membershipStatements) insertMembership(localpart string, roomID string, eventID string, txn *sql.Tx) (err error) { - _, err = txn.Stmt(s.insertMembershipStmt).Exec(localpart, roomID, eventID) +func (s *membershipStatements) insertMembership(localpart string, roomID string, eventID string, stillInRoom bool, txn *sql.Tx) (err error) { + _, err = txn.Stmt(s.insertMembershipStmt).Exec(localpart, roomID, eventID, stillInRoom) return } diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go index eec3011e8..3b72acc3d 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/storage.go @@ -121,11 +121,12 @@ func (d *Database) SetPartitionOffset(topic string, partition int32, offset int6 } // SaveMembership saves the user matching a given localpart as a member of a given -// room. It also stores the ID of the `join` membership event. +// room. It also stores the ID of the membership event and a flag on whether the user +// is still in the room. // If a membership already exists between the user and the room, or of the // insert fails, returns the SQL error -func (d *Database) SaveMembership(localpart string, roomID string, eventID string, txn *sql.Tx) error { - return d.memberships.insertMembership(localpart, roomID, eventID, txn) +func (d *Database) SaveMembership(localpart string, roomID string, eventID string, stillInRoom bool, txn *sql.Tx) error { + return d.memberships.insertMembership(localpart, roomID, eventID, stillInRoom, txn) } // removeMembershipsByEventIDs removes the memberships of which the `join` membership @@ -135,9 +136,9 @@ func (d *Database) removeMembershipsByEventIDs(eventIDs []string, txn *sql.Tx) e return d.memberships.deleteMembershipsByEventIDs(eventIDs, txn) } -// UpdateMemberships adds the "join" membership events included in a given state -// events array, and removes those which ID is included in a given array of events -// IDs. All of the process is run in a transaction, which commits only once/if every +// UpdateMemberships saves the membership events included in a given state events +// array, and removes those which ID is included in a given array of events IDs. +// All of the process is run in a transaction, which commits only once/if every // insertion and deletion has been successfully processed. // Returns a SQL error if there was an issue with any part of the process func (d *Database) UpdateMemberships(eventsToAdd []gomatrixserverlib.Event, idsToRemove []string) error { @@ -179,8 +180,11 @@ func (d *Database) UpdateMembership(oldEventID string, newEventID string) error return d.memberships.updateMembershipByEventID(oldEventID, newEventID) } -// newMembership will save a new membership in the database if the given state -// event is a "join" membership event +// newMembership will save a new membership in the database, with a flag on whether +// the user is still in the room. This flag is set to true if the given state +// event is a "join" membership event and false if the event is a "leave" or "ban" +// membership. If the event isn't a m.room.member event with one of these three +// values, does nothing. // If the event isn't a "join" membership event, does nothing // If an error occurred, returns it func (d *Database) newMembership(ev gomatrixserverlib.Event, txn *sql.Tx) error { @@ -204,7 +208,13 @@ func (d *Database) newMembership(ev gomatrixserverlib.Event, txn *sql.Tx) error // Only "join" membership events can be considered as new memberships if membership == "join" { - if err := d.SaveMembership(localpart, roomID, eventID, txn); err != nil { + if err := d.SaveMembership(localpart, roomID, eventID, true, txn); err != nil { + return err + } + } else if membership == "leave" || membership == "ban" { + // If the user left the room, save the new event ID and mark them as + // not in the room anymore + if err := d.SaveMembership(localpart, roomID, eventID, false, txn); err != nil { return err } } diff --git a/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go b/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go index f4f209447..9f06fd26c 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go +++ b/src/github.com/matrix-org/dendrite/clientapi/readers/memberships.go @@ -29,9 +29,8 @@ func GetMemberships( req *http.Request, roomID string, accountDB *accounts.Database, queryAPI api.RoomserverQueryAPI, ) util.JSONResponse { - // TODO: Check if the user is or has been in the room, and respond with - // M_FORBIDDEN if not. If the user has been in the room before but isn't - // anymore, send the members list as it was before they left. + // TODO: If the user has been in the room before but isn't + // anymore, only send the members list as it was before they left. memberships, err := accountDB.GetMembershipsByRoomID(roomID) if err != nil {