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 synapse.api.constants import Membership
from synapse.api.constants import Membership, JoinRules
from synapse.api.errors import AuthError, StoreError, Codes
from synapse.api.events.room import RoomMemberEvent
from synapse.util.logutils import log_function
import logging
@ -47,12 +48,19 @@ class Auth(object):
if event.type == RoomMemberEvent.TYPE:
allowed = yield self.is_membership_change_allowed(event)
defer.returnValue(allowed)
else:
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:
yield self._can_send_event(event)
defer.returnValue(True)
else:
raise AuthError(500, "Unknown event: %s" % event)
@ -111,7 +119,14 @@ class Auth(object):
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:
# 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.
if not caller_in_room: # caller isn't joined
raise AuthError(403, "You are not in room %s." % event.room_id)
@ -124,11 +139,18 @@ class Auth(object):
# joined: It's a NOOP
if event.user_id != target_user_id:
raise AuthError(403, "Cannot force another user to join.")
elif room.is_public:
pass # anyone can join public rooms.
elif (not caller or caller.membership not in
[Membership.INVITE, Membership.JOIN]):
elif join_rule == JoinRules.PUBLIC or room.is_public:
pass
elif join_rule == JoinRules.INVITE:
if (
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:
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)
@ -176,3 +198,53 @@ class Auth(object):
except StoreError:
raise AuthError(403, "Unrecognised access 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 (
RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
RoomPowerLevelsEvent, RoomDefaultLevelEvent, RoomJoinRulesEvent,
RoomCreateEvent,
RoomPowerLevelsEvent, RoomJoinRulesEvent,
RoomCreateEvent, RoomAddStateLevelEvent, RoomSendEventLevelEvent
)
from synapse.util.stringutils import random_string
@ -34,9 +34,10 @@ class EventFactory(object):
InviteJoinEvent,
RoomConfigEvent,
RoomPowerLevelsEvent,
RoomDefaultLevelEvent,
RoomJoinRulesEvent,
RoomCreateEvent,
RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
]
def __init__(self, hs):

View file

@ -155,8 +155,15 @@ class RoomPowerLevelsEvent(SynapseStateEvent):
return {}
class RoomDefaultLevelEvent(SynapseStateEvent):
TYPE = "m.room.default_level"
class RoomAddStateLevelEvent(SynapseStateEvent):
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):
return {}

View file

@ -21,7 +21,8 @@ from synapse.api.constants import Membership, JoinRules
from synapse.api.errors import StoreError, SynapseError
from synapse.api.events.room import (
RoomMemberEvent, RoomCreateEvent, RoomPowerLevelsEvent,
RoomJoinRulesEvent, RoomDefaultLevelEvent,
RoomJoinRulesEvent, RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
)
from synapse.util import stringutils
from ._base import BaseRoomHandler
@ -152,7 +153,7 @@ class RoomCreationHandler(BaseRoomHandler):
creation_event = self.event_factory.create_event(
etype=RoomCreateEvent.TYPE,
content={"creator": creator.to_string()},
content={"creator": creator.to_string(), "default": 0},
**event_keys
)
@ -162,12 +163,6 @@ class RoomCreationHandler(BaseRoomHandler):
**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_rules_event = self.event_factory.create_event(
etype=RoomJoinRulesEvent.TYPE,
@ -175,7 +170,25 @@ class RoomCreationHandler(BaseRoomHandler):
**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):

View file

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

View file

@ -174,6 +174,28 @@ class RoomStore(SQLBaseStore):
else:
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):
self._simple_insert_txn(
txn,
@ -196,19 +218,30 @@ class RoomStore(SQLBaseStore):
}
)
def _store_join_rule(txn, event):
def _store_join_rule(self, txn, event):
self._simple_insert_txn(
txn,
"room_join_rules",
{
"event_id": event.event_id,
"room_id": event.room_id,
"join_rule": event.join_rule,
"join_rule": event.content["join_rule"],
},
)
def _store_power_levels(txn, event):
for user_id, level in event.content:
def _store_power_levels(self, txn, event):
for user_id, level in event.content.items():
if user_id == "default":
self._simple_insert_txn(
txn,
"room_default_levels",
{
"event_id": event.event_id,
"room_id": event.room_id,
"level": level,
},
)
else:
self._simple_insert_txn(
txn,
"room_power_levels",
@ -220,14 +253,36 @@ class RoomStore(SQLBaseStore):
},
)
def _store_default_level(txn, event):
def _store_default_level(self, txn, event):
self._simple_insert_txn(
txn,
"room_default_levels",
{
"event_id": event.event_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 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(
room_id TEXT NOT NULL,
host TEXT NOT NULL,