Implement power level lists, default power levels and send_evnet_level/add_state_level events.
This commit is contained in:
parent
10efca1a74
commit
865469f233
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue