Add an EventValidator. Fix bugs in auth ++ storage

This commit is contained in:
Erik Johnston 2014-11-10 18:24:43 +00:00
parent ec824927c1
commit a8e565eca8
9 changed files with 64 additions and 90 deletions

View file

@ -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,6 +352,7 @@ 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]:
if join_rule_event:
auth_events.append(join_rule_event.event_id) auth_events.append(join_rule_event.event_id)
if member_event and not is_public: if member_event and not is_public:
@ -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

View file

@ -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):

View file

@ -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_displayname(user.localpart),
self.store.get_profile_avatar_url(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

View file

@ -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"):

View file

@ -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)

View file

@ -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.
""" """

View file

@ -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,
) )

View file

@ -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 "

View file

@ -133,6 +133,7 @@ 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):
if hasattr(event, "topic"):
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
"topics", "topics",
@ -144,6 +145,7 @@ class RoomStore(SQLBaseStore):
) )
def _store_room_name_txn(self, txn, event): def _store_room_name_txn(self, txn, event):
if hasattr(event, "name"):
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
"room_names", "room_names",