Implement power level lists, default power levels and send_evnet_level/add_state_level events.

This commit is contained in:
Erik Johnston 2014-09-01 13:44:19 +01:00
parent 10efca1a74
commit 865469f233
7 changed files with 217 additions and 47 deletions

View File

@ -17,9 +17,10 @@
from twisted.internet import defer from twisted.internet import defer
from synapse.api.constants import Membership from synapse.api.constants import Membership, JoinRules
from synapse.api.errors import AuthError, StoreError, Codes from synapse.api.errors import AuthError, StoreError, Codes
from synapse.api.events.room import RoomMemberEvent from synapse.api.events.room import RoomMemberEvent
from synapse.util.logutils import log_function
import logging import logging
@ -47,13 +48,20 @@ class Auth(object):
if event.type == RoomMemberEvent.TYPE: if event.type == RoomMemberEvent.TYPE:
allowed = yield self.is_membership_change_allowed(event) allowed = yield self.is_membership_change_allowed(event)
defer.returnValue(allowed) defer.returnValue(allowed)
return
self._check_joined_room(
member=snapshot.membership_state,
user_id=snapshot.user_id,
room_id=snapshot.room_id,
)
if hasattr(event, "state_key"):
yield self._can_add_state(event)
else: else:
self._check_joined_room( yield self._can_send_event(event)
member=snapshot.membership_state,
user_id=snapshot.user_id, defer.returnValue(True)
room_id=snapshot.room_id,
)
defer.returnValue(True)
else: else:
raise AuthError(500, "Unknown event: %s" % event) raise AuthError(500, "Unknown event: %s" % event)
except AuthError as e: except AuthError as e:
@ -111,7 +119,14 @@ class Auth(object):
membership = event.content["membership"] membership = event.content["membership"]
join_rule = yield self.store.get_room_join_rule(event.room_id)
if not join_rule:
join_rule = JoinRules.INVITE
if Membership.INVITE == membership: if Membership.INVITE == membership:
# TODO (erikj): We should probably handle this more intelligently
# PRIVATE join rules.
# Invites are valid iff caller is in the room and target isn't. # Invites are valid iff caller is in the room and target isn't.
if not caller_in_room: # caller isn't joined if not caller_in_room: # caller isn't joined
raise AuthError(403, "You are not in room %s." % event.room_id) raise AuthError(403, "You are not in room %s." % event.room_id)
@ -124,11 +139,18 @@ class Auth(object):
# joined: It's a NOOP # joined: It's a NOOP
if event.user_id != target_user_id: if event.user_id != target_user_id:
raise AuthError(403, "Cannot force another user to join.") raise AuthError(403, "Cannot force another user to join.")
elif room.is_public: elif join_rule == JoinRules.PUBLIC or room.is_public:
pass # anyone can join public rooms. pass
elif (not caller or caller.membership not in elif join_rule == JoinRules.INVITE:
[Membership.INVITE, Membership.JOIN]): if (
raise AuthError(403, "You are not invited to this room.") not caller or caller.membership not in
[Membership.INVITE, Membership.JOIN]
):
raise AuthError(403, "You are not invited to this room.")
else:
# TODO (erikj): may_join list
# TODO (erikj): private rooms
raise AuthError(403, "You are not allowed to join this room")
elif Membership.LEAVE == membership: elif Membership.LEAVE == membership:
if not caller_in_room: # trying to leave a room you aren't joined if not caller_in_room: # trying to leave a room you aren't joined
raise AuthError(403, "You are not in room %s." % event.room_id) raise AuthError(403, "You are not in room %s." % event.room_id)
@ -176,3 +198,53 @@ class Auth(object):
except StoreError: except StoreError:
raise AuthError(403, "Unrecognised access token.", raise AuthError(403, "Unrecognised access token.",
errcode=Codes.UNKNOWN_TOKEN) errcode=Codes.UNKNOWN_TOKEN)
@defer.inlineCallbacks
@log_function
def _can_send_event(self, event):
send_level = yield self.store.get_send_event_level(event.room_id)
if send_level:
send_level = int(send_level)
else:
send_level = 0
user_level = yield self.store.get_power_level(
event.room_id,
event.user_id,
)
if user_level:
user_level = int(user_level)
else:
user_level = 0
if user_level < send_level:
raise AuthError(
403, "You don't have permission to post to the room"
)
defer.returnValue(True)
@defer.inlineCallbacks
def _can_add_state(self, event):
add_level = yield self.store.get_add_state_level(event.room_id)
if not add_level:
defer.returnValue(True)
add_level = int(add_level)
user_level = yield self.store.get_power_level(
event.room_id,
event.user_id,
)
user_level = int(user_level)
if user_level < add_level:
raise AuthError(
403, "You don't have permission to add state to the room"
)
defer.returnValue(True)

View File

@ -16,8 +16,8 @@
from synapse.api.events.room import ( from synapse.api.events.room import (
RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent, RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent, InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
RoomPowerLevelsEvent, RoomDefaultLevelEvent, RoomJoinRulesEvent, RoomPowerLevelsEvent, RoomJoinRulesEvent,
RoomCreateEvent, RoomCreateEvent, RoomAddStateLevelEvent, RoomSendEventLevelEvent
) )
from synapse.util.stringutils import random_string from synapse.util.stringutils import random_string
@ -34,9 +34,10 @@ class EventFactory(object):
InviteJoinEvent, InviteJoinEvent,
RoomConfigEvent, RoomConfigEvent,
RoomPowerLevelsEvent, RoomPowerLevelsEvent,
RoomDefaultLevelEvent,
RoomJoinRulesEvent, RoomJoinRulesEvent,
RoomCreateEvent, RoomCreateEvent,
RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
] ]
def __init__(self, hs): def __init__(self, hs):

View File

@ -155,8 +155,15 @@ class RoomPowerLevelsEvent(SynapseStateEvent):
return {} return {}
class RoomDefaultLevelEvent(SynapseStateEvent): class RoomAddStateLevelEvent(SynapseStateEvent):
TYPE = "m.room.default_level" TYPE = "m.room.add_state_level"
def get_content_template(self):
return {}
class RoomSendEventLevelEvent(SynapseStateEvent):
TYPE = "m.room.send_event_level"
def get_content_template(self): def get_content_template(self):
return {} return {}

View File

@ -21,7 +21,8 @@ from synapse.api.constants import Membership, JoinRules
from synapse.api.errors import StoreError, SynapseError from synapse.api.errors import StoreError, SynapseError
from synapse.api.events.room import ( from synapse.api.events.room import (
RoomMemberEvent, RoomCreateEvent, RoomPowerLevelsEvent, RoomMemberEvent, RoomCreateEvent, RoomPowerLevelsEvent,
RoomJoinRulesEvent, RoomDefaultLevelEvent, RoomJoinRulesEvent, RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
) )
from synapse.util import stringutils from synapse.util import stringutils
from ._base import BaseRoomHandler from ._base import BaseRoomHandler
@ -152,7 +153,7 @@ class RoomCreationHandler(BaseRoomHandler):
creation_event = self.event_factory.create_event( creation_event = self.event_factory.create_event(
etype=RoomCreateEvent.TYPE, etype=RoomCreateEvent.TYPE,
content={"creator": creator.to_string()}, content={"creator": creator.to_string(), "default": 0},
**event_keys **event_keys
) )
@ -162,12 +163,6 @@ class RoomCreationHandler(BaseRoomHandler):
**event_keys **event_keys
) )
default_level_event = self.event_factory.create_event(
etype=RoomDefaultLevelEvent.TYPE,
content={"default_level": 0},
**event_keys
)
join_rule = JoinRules.PUBLIC if is_public else JoinRules.INVITE join_rule = JoinRules.PUBLIC if is_public else JoinRules.INVITE
join_rules_event = self.event_factory.create_event( join_rules_event = self.event_factory.create_event(
etype=RoomJoinRulesEvent.TYPE, etype=RoomJoinRulesEvent.TYPE,
@ -175,7 +170,25 @@ class RoomCreationHandler(BaseRoomHandler):
**event_keys **event_keys
) )
return [creation_event, power_levels_event, default_level_event, join_rules_event] add_state_event = self.event_factory.create_event(
etype=RoomAddStateLevelEvent.TYPE,
content={"level": 10},
**event_keys
)
send_event = self.event_factory.create_event(
etype=RoomSendEventLevelEvent.TYPE,
content={"level": 0},
**event_keys
)
return [
creation_event,
power_levels_event,
join_rules_event,
add_state_event,
send_event,
]
class RoomMemberHandler(BaseRoomHandler): class RoomMemberHandler(BaseRoomHandler):

View File

@ -21,7 +21,8 @@ from synapse.api.events.room import (
RoomNameEvent, RoomNameEvent,
RoomJoinRulesEvent, RoomJoinRulesEvent,
RoomPowerLevelsEvent, RoomPowerLevelsEvent,
RoomDefaultLevelEvent, RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
) )
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
@ -125,7 +126,7 @@ class DataStore(RoomMemberStore, RoomStore,
if event.type == RoomMemberEvent.TYPE: if event.type == RoomMemberEvent.TYPE:
self._store_room_member_txn(txn, event) self._store_room_member_txn(txn, event)
elif event.type == FeedbackEvent.TYPE: elif event.type == FeedbackEvent.TYPE:
self._store_feedback_txn(txn,event) self._store_feedback_txn(txn, event)
# elif event.type == RoomConfigEvent.TYPE: # elif event.type == RoomConfigEvent.TYPE:
# self._store_room_config_txn(txn, event) # self._store_room_config_txn(txn, event)
elif event.type == RoomNameEvent.TYPE: elif event.type == RoomNameEvent.TYPE:
@ -136,8 +137,10 @@ class DataStore(RoomMemberStore, RoomStore,
self._store_join_rule(txn, event) self._store_join_rule(txn, event)
elif event.type == RoomPowerLevelsEvent.TYPE: elif event.type == RoomPowerLevelsEvent.TYPE:
self._store_power_levels(txn, event) self._store_power_levels(txn, event)
elif event.type == RoomDefaultLevelEvent.TYPE: elif event.type == RoomAddStateLevelEvent.TYPE:
self._store_default_level(txn, event) self._store_add_state_level(txn, event)
elif event.type == RoomSendEventLevelEvent.TYPE:
self._store_send_event_level(txn, event)
vals = { vals = {
"topological_ordering": event.depth, "topological_ordering": event.depth,
@ -231,7 +234,6 @@ class DataStore(RoomMemberStore, RoomStore,
defer.returnValue(self.min_token) defer.returnValue(self.min_token)
def snapshot_room(self, room_id, user_id, state_type=None, state_key=None): def snapshot_room(self, room_id, user_id, state_type=None, state_key=None):
"""Snapshot the room for an update by a user """Snapshot the room for an update by a user
Args: Args:

View File

@ -174,6 +174,28 @@ class RoomStore(SQLBaseStore):
else: else:
defer.returnValue(None) defer.returnValue(None)
def get_add_state_level(self, room_id):
return self._get_level_from_table("room_add_state_levels", room_id)
def get_send_event_level(self, room_id):
return self._get_level_from_table("room_send_event_levels", room_id)
@defer.inlineCallbacks
def _get_level_from_table(self, table, room_id):
sql = (
"SELECT level FROM %(table)s as r "
"INNER JOIN current_state_events as c "
"ON r.event_id = c.event_id "
"WHERE c.room_id = ? "
) % {"table": table}
rows = yield self._execute(None, sql, room_id)
if len(rows) == 1:
defer.returnValue(rows[0][0])
else:
defer.returnValue(None)
def _store_room_topic_txn(self, txn, event): def _store_room_topic_txn(self, txn, event):
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
@ -196,38 +218,71 @@ class RoomStore(SQLBaseStore):
} }
) )
def _store_join_rule(txn, event): def _store_join_rule(self, txn, event):
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
"room_join_rules", "room_join_rules",
{ {
"event_id": event.event_id, "event_id": event.event_id,
"room_id": event.room_id, "room_id": event.room_id,
"join_rule": event.join_rule, "join_rule": event.content["join_rule"],
}, },
) )
def _store_power_levels(txn, event): def _store_power_levels(self, txn, event):
for user_id, level in event.content: for user_id, level in event.content.items():
self._simple_insert_txn( if user_id == "default":
txn, self._simple_insert_txn(
"room_power_levels", txn,
{ "room_default_levels",
"event_id": event.event_id, {
"room_id": event.room_id, "event_id": event.event_id,
"user_id": user_id, "room_id": event.room_id,
"level": level "level": level,
}, },
) )
else:
self._simple_insert_txn(
txn,
"room_power_levels",
{
"event_id": event.event_id,
"room_id": event.room_id,
"user_id": user_id,
"level": level
},
)
def _store_default_level(txn, event): def _store_default_level(self, txn, event):
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
"room_default_levels", "room_default_levels",
{ {
"event_id": event.event_id, "event_id": event.event_id,
"room_id": event.room_id, "room_id": event.room_id,
"level": level "level": event.content["default_level"],
},
)
def _store_add_state_level(self, txn, event):
self._simple_insert_txn(
txn,
"room_add_state_levels",
{
"event_id": event.event_id,
"room_id": event.room_id,
"level": event.content["level"],
},
)
def _store_send_event_level(self, txn, event):
self._simple_insert_txn(
txn,
"room_send_event_levels",
{
"event_id": event.event_id,
"room_id": event.room_id,
"level": event.content["level"],
}, },
) )

View File

@ -126,6 +126,26 @@ CREATE INDEX IF NOT EXISTS room_default_levels_event_id ON room_default_levels(e
CREATE INDEX IF NOT EXISTS room_default_levels_room_id ON room_default_levels(room_id); CREATE INDEX IF NOT EXISTS room_default_levels_room_id ON room_default_levels(room_id);
CREATE TABLE IF NOT EXISTS room_add_state_levels(
event_id TEXT NOT NULL,
room_id TEXT NOT NULL,
level INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS room_add_state_levels_event_id ON room_add_state_levels(event_id);
CREATE INDEX IF NOT EXISTS room_add_state_levels_room_id ON room_add_state_levels(room_id);
CREATE TABLE IF NOT EXISTS room_send_event_levels(
event_id TEXT NOT NULL,
room_id TEXT NOT NULL,
level INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS room_send_event_levels_event_id ON room_send_event_levels(event_id);
CREATE INDEX IF NOT EXISTS room_send_event_levels_room_id ON room_send_event_levels(room_id);
CREATE TABLE IF NOT EXISTS room_hosts( CREATE TABLE IF NOT EXISTS room_hosts(
room_id TEXT NOT NULL, room_id TEXT NOT NULL,
host TEXT NOT NULL, host TEXT NOT NULL,