Add an EventValidator. Fix bugs in auth ++ storage
This commit is contained in:
parent
ec824927c1
commit
a8e565eca8
|
@ -70,6 +70,7 @@ class Auth(object):
|
||||||
logger.debug("Denying! %s", event)
|
logger.debug("Denying! %s", event)
|
||||||
return allowed
|
return allowed
|
||||||
|
|
||||||
|
self.check_event_sender_in_room(event)
|
||||||
self._can_send_event(event)
|
self._can_send_event(event)
|
||||||
|
|
||||||
if event.type == RoomPowerLevelsEvent.TYPE:
|
if event.type == RoomPowerLevelsEvent.TYPE:
|
||||||
|
@ -83,8 +84,10 @@ class Auth(object):
|
||||||
else:
|
else:
|
||||||
raise AuthError(500, "Unknown event: %s" % event)
|
raise AuthError(500, "Unknown event: %s" % event)
|
||||||
except AuthError as e:
|
except AuthError as e:
|
||||||
logger.info("Event auth check failed on event %s with msg: %s",
|
logger.info(
|
||||||
event, e.msg)
|
"Event auth check failed on event %s with msg: %s",
|
||||||
|
event, e.msg
|
||||||
|
)
|
||||||
logger.info("Denying! %s", event)
|
logger.info("Denying! %s", event)
|
||||||
if raises:
|
if raises:
|
||||||
raise e
|
raise e
|
||||||
|
@ -277,7 +280,7 @@ class Auth(object):
|
||||||
default=[""]
|
default=[""]
|
||||||
)[0]
|
)[0]
|
||||||
if user and access_token and ip_addr:
|
if user and access_token and ip_addr:
|
||||||
self.store.insert_client_ip(
|
yield self.store.insert_client_ip(
|
||||||
user=user,
|
user=user,
|
||||||
access_token=access_token,
|
access_token=access_token,
|
||||||
device_id=user_info["device_id"],
|
device_id=user_info["device_id"],
|
||||||
|
@ -349,7 +352,8 @@ class Auth(object):
|
||||||
if event.type == RoomMemberEvent.TYPE:
|
if event.type == RoomMemberEvent.TYPE:
|
||||||
e_type = event.content["membership"]
|
e_type = event.content["membership"]
|
||||||
if e_type in [Membership.JOIN, Membership.INVITE]:
|
if e_type in [Membership.JOIN, Membership.INVITE]:
|
||||||
auth_events.append(join_rule_event.event_id)
|
if join_rule_event:
|
||||||
|
auth_events.append(join_rule_event.event_id)
|
||||||
|
|
||||||
if member_event and not is_public:
|
if member_event and not is_public:
|
||||||
auth_events.append(member_event.event_id)
|
auth_events.append(member_event.event_id)
|
||||||
|
@ -405,7 +409,9 @@ class Auth(object):
|
||||||
|
|
||||||
if user_level < send_level:
|
if user_level < send_level:
|
||||||
raise AuthError(
|
raise AuthError(
|
||||||
403, "You don't have permission to post that to the room"
|
403,
|
||||||
|
"You don't have permission to post that to the room. " +
|
||||||
|
"user_level (%d) < send_level (%d)" % (user_level, send_level)
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from synapse.api.errors import SynapseError, Codes
|
|
||||||
from synapse.util.jsonobject import JsonEncodedObject
|
from synapse.util.jsonobject import JsonEncodedObject
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,66 +117,6 @@ class SynapseEvent(JsonEncodedObject):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("get_content_template not implemented.")
|
raise NotImplementedError("get_content_template not implemented.")
|
||||||
|
|
||||||
def check_json(self, content, raises=True):
|
|
||||||
"""Checks the given JSON content abides by the rules of the template.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
content : A JSON object to check.
|
|
||||||
raises: True to raise a SynapseError if the check fails.
|
|
||||||
Returns:
|
|
||||||
True if the content passes the template. Returns False if the check
|
|
||||||
fails and raises=False.
|
|
||||||
Raises:
|
|
||||||
SynapseError if the check fails and raises=True.
|
|
||||||
"""
|
|
||||||
# recursively call to inspect each layer
|
|
||||||
err_msg = self._check_json(content, self.get_content_template())
|
|
||||||
if err_msg:
|
|
||||||
if raises:
|
|
||||||
raise SynapseError(400, err_msg, Codes.BAD_JSON)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _check_json(self, content, template):
|
|
||||||
"""Check content and template matches.
|
|
||||||
|
|
||||||
If the template is a dict, each key in the dict will be validated with
|
|
||||||
the content, else it will just compare the types of content and
|
|
||||||
template. This basic type check is required because this function will
|
|
||||||
be recursively called and could be called with just strs or ints.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
content: The content to validate.
|
|
||||||
template: The validation template.
|
|
||||||
Returns:
|
|
||||||
str: An error message if the validation fails, else None.
|
|
||||||
"""
|
|
||||||
if type(content) != type(template):
|
|
||||||
return "Mismatched types: %s" % template
|
|
||||||
|
|
||||||
if type(template) == dict:
|
|
||||||
for key in template:
|
|
||||||
if key not in content:
|
|
||||||
return "Missing %s key" % key
|
|
||||||
|
|
||||||
if type(content[key]) != type(template[key]):
|
|
||||||
return "Key %s is of the wrong type (got %s, want %s)" % (
|
|
||||||
key, type(content[key]), type(template[key]))
|
|
||||||
|
|
||||||
if type(content[key]) == dict:
|
|
||||||
# we must go deeper
|
|
||||||
msg = self._check_json(content[key], template[key])
|
|
||||||
if msg:
|
|
||||||
return msg
|
|
||||||
elif type(content[key]) == list:
|
|
||||||
# make sure each item type in content matches the template
|
|
||||||
for entry in content[key]:
|
|
||||||
msg = self._check_json(entry, template[key][0])
|
|
||||||
if msg:
|
|
||||||
return msg
|
|
||||||
|
|
||||||
|
|
||||||
class SynapseStateEvent(SynapseEvent):
|
class SynapseStateEvent(SynapseEvent):
|
||||||
|
|
||||||
|
|
|
@ -152,10 +152,13 @@ class ProfileHandler(BaseHandler):
|
||||||
if not user.is_mine:
|
if not user.is_mine:
|
||||||
defer.returnValue(None)
|
defer.returnValue(None)
|
||||||
|
|
||||||
(displayname, avatar_url) = yield defer.gatherResults([
|
(displayname, avatar_url) = yield defer.gatherResults(
|
||||||
self.store.get_profile_displayname(user.localpart),
|
[
|
||||||
self.store.get_profile_avatar_url(user.localpart),
|
self.store.get_profile_displayname(user.localpart),
|
||||||
])
|
self.store.get_profile_avatar_url(user.localpart),
|
||||||
|
],
|
||||||
|
consumeErrors=True
|
||||||
|
)
|
||||||
|
|
||||||
state["displayname"] = displayname
|
state["displayname"] = displayname
|
||||||
state["avatar_url"] = avatar_url
|
state["avatar_url"] = avatar_url
|
||||||
|
|
|
@ -67,6 +67,8 @@ class RestServlet(object):
|
||||||
self.auth = hs.get_auth()
|
self.auth = hs.get_auth()
|
||||||
self.txns = HttpTransactionStore()
|
self.txns = HttpTransactionStore()
|
||||||
|
|
||||||
|
self.validator = hs.get_event_validator()
|
||||||
|
|
||||||
def register(self, http_server):
|
def register(self, http_server):
|
||||||
""" Register this servlet with the given HTTP server. """
|
""" Register this servlet with the given HTTP server. """
|
||||||
if hasattr(self, "PATTERN"):
|
if hasattr(self, "PATTERN"):
|
||||||
|
|
|
@ -154,6 +154,9 @@ class RoomStateEventRestServlet(RestServlet):
|
||||||
user_id=user.to_string(),
|
user_id=user.to_string(),
|
||||||
state_key=urllib.unquote(state_key)
|
state_key=urllib.unquote(state_key)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validator.validate(event)
|
||||||
|
|
||||||
if event_type == RoomMemberEvent.TYPE:
|
if event_type == RoomMemberEvent.TYPE:
|
||||||
# membership events are special
|
# membership events are special
|
||||||
handler = self.handlers.room_member_handler
|
handler = self.handlers.room_member_handler
|
||||||
|
@ -188,6 +191,8 @@ class RoomSendEventRestServlet(RestServlet):
|
||||||
content=content
|
content=content
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validator.validate(event)
|
||||||
|
|
||||||
msg_handler = self.handlers.message_handler
|
msg_handler = self.handlers.message_handler
|
||||||
yield msg_handler.send_message(event)
|
yield msg_handler.send_message(event)
|
||||||
|
|
||||||
|
@ -253,6 +258,9 @@ class JoinRoomAliasServlet(RestServlet):
|
||||||
user_id=user.to_string(),
|
user_id=user.to_string(),
|
||||||
state_key=user.to_string()
|
state_key=user.to_string()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validator.validate(event)
|
||||||
|
|
||||||
handler = self.handlers.room_member_handler
|
handler = self.handlers.room_member_handler
|
||||||
yield handler.change_membership(event)
|
yield handler.change_membership(event)
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
@ -424,6 +432,9 @@ class RoomMembershipRestServlet(RestServlet):
|
||||||
user_id=user.to_string(),
|
user_id=user.to_string(),
|
||||||
state_key=state_key
|
state_key=state_key
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validator.validate(event)
|
||||||
|
|
||||||
handler = self.handlers.room_member_handler
|
handler = self.handlers.room_member_handler
|
||||||
yield handler.change_membership(event)
|
yield handler.change_membership(event)
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
@ -461,6 +472,8 @@ class RoomRedactEventRestServlet(RestServlet):
|
||||||
redacts=urllib.unquote(event_id),
|
redacts=urllib.unquote(event_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validator.validate(event)
|
||||||
|
|
||||||
msg_handler = self.handlers.message_handler
|
msg_handler = self.handlers.message_handler
|
||||||
yield msg_handler.send_message(event)
|
yield msg_handler.send_message(event)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
from synapse.federation import initialize_http_replication
|
from synapse.federation import initialize_http_replication
|
||||||
from synapse.api.events import serialize_event
|
from synapse.api.events import serialize_event
|
||||||
from synapse.api.events.factory import EventFactory
|
from synapse.api.events.factory import EventFactory
|
||||||
|
from synapse.api.events.validator import EventValidator
|
||||||
from synapse.notifier import Notifier
|
from synapse.notifier import Notifier
|
||||||
from synapse.api.auth import Auth
|
from synapse.api.auth import Auth
|
||||||
from synapse.handlers import Handlers
|
from synapse.handlers import Handlers
|
||||||
|
@ -80,6 +81,7 @@ class BaseHomeServer(object):
|
||||||
'event_sources',
|
'event_sources',
|
||||||
'ratelimiter',
|
'ratelimiter',
|
||||||
'keyring',
|
'keyring',
|
||||||
|
'event_validator',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, hostname, **kwargs):
|
def __init__(self, hostname, **kwargs):
|
||||||
|
@ -223,6 +225,9 @@ class HomeServer(BaseHomeServer):
|
||||||
def build_keyring(self):
|
def build_keyring(self):
|
||||||
return Keyring(self)
|
return Keyring(self)
|
||||||
|
|
||||||
|
def build_event_validator(self):
|
||||||
|
return EventValidator(self)
|
||||||
|
|
||||||
def register_servlets(self):
|
def register_servlets(self):
|
||||||
""" Register all servlets associated with this HomeServer.
|
""" Register all servlets associated with this HomeServer.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -212,7 +212,7 @@ class SQLBaseStore(object):
|
||||||
retcol : string giving the name of the column to return
|
retcol : string giving the name of the column to return
|
||||||
"""
|
"""
|
||||||
return self.runInteraction(
|
return self.runInteraction(
|
||||||
"_simple_select_one_onecol_txn",
|
"_simple_select_one_onecol",
|
||||||
self._simple_select_one_onecol_txn,
|
self._simple_select_one_onecol_txn,
|
||||||
table, keyvalues, retcol, allow_none=allow_none,
|
table, keyvalues, retcol, allow_none=allow_none,
|
||||||
)
|
)
|
||||||
|
|
|
@ -107,13 +107,17 @@ class RegistrationStore(SQLBaseStore):
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
def is_server_admin(self, user):
|
def is_server_admin(self, user):
|
||||||
return self._simple_select_one_onecol(
|
res = yield self._simple_select_one_onecol(
|
||||||
table="users",
|
table="users",
|
||||||
keyvalues={"name": user.to_string()},
|
keyvalues={"name": user.to_string()},
|
||||||
retcol="admin",
|
retcol="admin",
|
||||||
|
allow_none=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defer.returnValue(res if res else False)
|
||||||
|
|
||||||
def _query_for_auth(self, txn, token):
|
def _query_for_auth(self, txn, token):
|
||||||
sql = (
|
sql = (
|
||||||
"SELECT users.name, users.admin, access_tokens.device_id "
|
"SELECT users.name, users.admin, access_tokens.device_id "
|
||||||
|
|
|
@ -133,26 +133,28 @@ class RoomStore(SQLBaseStore):
|
||||||
defer.returnValue(ret)
|
defer.returnValue(ret)
|
||||||
|
|
||||||
def _store_room_topic_txn(self, txn, event):
|
def _store_room_topic_txn(self, txn, event):
|
||||||
self._simple_insert_txn(
|
if hasattr(event, "topic"):
|
||||||
txn,
|
self._simple_insert_txn(
|
||||||
"topics",
|
txn,
|
||||||
{
|
"topics",
|
||||||
"event_id": event.event_id,
|
{
|
||||||
"room_id": event.room_id,
|
"event_id": event.event_id,
|
||||||
"topic": event.topic,
|
"room_id": event.room_id,
|
||||||
}
|
"topic": event.topic,
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def _store_room_name_txn(self, txn, event):
|
def _store_room_name_txn(self, txn, event):
|
||||||
self._simple_insert_txn(
|
if hasattr(event, "name"):
|
||||||
txn,
|
self._simple_insert_txn(
|
||||||
"room_names",
|
txn,
|
||||||
{
|
"room_names",
|
||||||
"event_id": event.event_id,
|
{
|
||||||
"room_id": event.room_id,
|
"event_id": event.event_id,
|
||||||
"name": event.name,
|
"room_id": event.room_id,
|
||||||
}
|
"name": event.name,
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RoomsTable(Table):
|
class RoomsTable(Table):
|
||||||
|
|
Loading…
Reference in New Issue