diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 8111b34428..6eaa1150a3 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -207,6 +207,13 @@ class Auth(object):
user_id, room_id
))
+ if membership == Membership.LEAVE:
+ forgot = yield self.store.did_forget(user_id, room_id)
+ if forgot:
+ raise AuthError(403, "User %s not in room %s" % (
+ user_id, room_id
+ ))
+
defer.returnValue(member)
@defer.inlineCallbacks
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 3f04752581..023b4001b8 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -743,6 +743,9 @@ class RoomMemberHandler(BaseHandler):
)
defer.returnValue((token, public_key, key_validity_url, display_name))
+ def forget(self, user, room_id):
+ self.store.forget(user.to_string(), room_id)
+
class RoomListHandler(BaseHandler):
diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py
index 139dac1cc3..6952d269ec 100644
--- a/synapse/rest/client/v1/room.py
+++ b/synapse/rest/client/v1/room.py
@@ -448,7 +448,7 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
def register(self, http_server):
# /rooms/$roomid/[invite|join|leave]
PATTERN = ("/rooms/(?P<room_id>[^/]*)/"
- "(?P<membership_action>join|invite|leave|ban|kick)")
+ "(?P<membership_action>join|invite|leave|ban|kick|forget)")
register_txn_path(self, PATTERN, http_server)
@defer.inlineCallbacks
@@ -458,6 +458,8 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
allow_guest=True
)
+ effective_membership_action = membership_action
+
if is_guest and membership_action not in {Membership.JOIN, Membership.LEAVE}:
raise AuthError(403, "Guest access not allowed")
@@ -488,11 +490,13 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
UserID.from_string(state_key)
if membership_action == "kick":
- membership_action = "leave"
+ effective_membership_action = "leave"
+ elif membership_action == "forget":
+ effective_membership_action = "leave"
msg_handler = self.handlers.message_handler
- content = {"membership": unicode(membership_action)}
+ content = {"membership": unicode(effective_membership_action)}
if is_guest:
content["kind"] = "guest"
@@ -509,6 +513,9 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
is_guest=is_guest,
)
+ if membership_action == "forget":
+ self.handlers.room_member_handler.forget(user, room_id)
+
defer.returnValue((200, {}))
def _has_3pid_invite_keys(self, content):
diff --git a/synapse/storage/prepare_database.py b/synapse/storage/prepare_database.py
index 1a74d6e360..9800fd4203 100644
--- a/synapse/storage/prepare_database.py
+++ b/synapse/storage/prepare_database.py
@@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
# Remember to update this number every time a change is made to database
# schema files, so the users will be informed on server restarts.
-SCHEMA_VERSION = 25
+SCHEMA_VERSION = 26
dir_path = os.path.abspath(os.path.dirname(__file__))
diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py
index ae1ad56d9a..183855ba40 100644
--- a/synapse/storage/roommember.py
+++ b/synapse/storage/roommember.py
@@ -269,3 +269,39 @@ class RoomMemberStore(SQLBaseStore):
ret = len(room_id_lists.pop(0).intersection(*room_id_lists)) > 0
defer.returnValue(ret)
+
+ def forget(self, user_id, room_id):
+ def f(txn):
+ sql = (
+ "UPDATE"
+ " room_memberships"
+ " SET"
+ " forgotten = 1"
+ " WHERE"
+ " user_id = ?"
+ " AND"
+ " room_id = ?"
+ )
+ txn.execute(sql, (user_id, room_id))
+ self.runInteraction("forget_membership", f)
+
+ @defer.inlineCallbacks
+ def did_forget(self, user_id, room_id):
+ def f(txn):
+ sql = (
+ "SELECT"
+ " COUNT(*)"
+ "FROM"
+ " room_memberships"
+ " WHERE"
+ " user_id = ?"
+ " AND"
+ " room_id = ?"
+ " AND"
+ " forgotten = 1"
+ )
+ txn.execute(sql, (user_id, room_id))
+ rows = txn.fetchall()
+ return rows[0][0]
+ count = yield self.runInteraction("did_forget_membership", f)
+ defer.returnValue(count > 0)
diff --git a/synapse/storage/schema/delta/26/forgotten_memberships.sql b/synapse/storage/schema/delta/26/forgotten_memberships.sql
new file mode 100644
index 0000000000..df55b9c6f6
--- /dev/null
+++ b/synapse/storage/schema/delta/26/forgotten_memberships.sql
@@ -0,0 +1,24 @@
+/* Copyright 2015 OpenMarket Ltd
+ *
+ * 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.
+ */
+
+/*
+ * Keeps track of what rooms users have left and don't want to be able to
+ * access again.
+ *
+ * If all users on this server have left a room, we can delete the room
+ * entirely.
+ */
+
+ ALTER TABLE room_memberships ADD COLUMN forgotten INTEGER(1) DEFAULT 0;
|