1
0

auth.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014, 2015 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """This module contains classes for authenticating the user."""
  16. from signedjson.key import decode_verify_key_bytes
  17. from signedjson.sign import verify_signed_json, SignatureVerifyException
  18. from twisted.internet import defer
  19. from synapse.api.constants import EventTypes, Membership, JoinRules
  20. from synapse.api.errors import AuthError, Codes, SynapseError
  21. from synapse.types import RoomID, UserID, EventID
  22. from synapse.util.logutils import log_function
  23. from synapse.util import third_party_invites
  24. from unpaddedbase64 import decode_base64
  25. import logging
  26. import pymacaroons
  27. logger = logging.getLogger(__name__)
  28. AuthEventTypes = (
  29. EventTypes.Create, EventTypes.Member, EventTypes.PowerLevels,
  30. EventTypes.JoinRules, EventTypes.RoomHistoryVisibility,
  31. EventTypes.ThirdPartyInvite,
  32. )
  33. class Auth(object):
  34. def __init__(self, hs):
  35. self.hs = hs
  36. self.store = hs.get_datastore()
  37. self.state = hs.get_state_handler()
  38. self.TOKEN_NOT_FOUND_HTTP_STATUS = 401
  39. self._KNOWN_CAVEAT_PREFIXES = set([
  40. "gen = ",
  41. "type = ",
  42. "time < ",
  43. "user_id = ",
  44. ])
  45. def check(self, event, auth_events):
  46. """ Checks if this event is correctly authed.
  47. Args:
  48. event: the event being checked.
  49. auth_events (dict: event-key -> event): the existing room state.
  50. Returns:
  51. True if the auth checks pass.
  52. """
  53. try:
  54. if not hasattr(event, "room_id"):
  55. raise AuthError(500, "Event has no room_id: %s" % event)
  56. if auth_events is None:
  57. # Oh, we don't know what the state of the room was, so we
  58. # are trusting that this is allowed (at least for now)
  59. logger.warn("Trusting event: %s", event.event_id)
  60. return True
  61. if event.type == EventTypes.Create:
  62. # FIXME
  63. return True
  64. creation_event = auth_events.get((EventTypes.Create, ""), None)
  65. if not creation_event:
  66. raise SynapseError(
  67. 403,
  68. "Room %r does not exist" % (event.room_id,)
  69. )
  70. creating_domain = RoomID.from_string(event.room_id).domain
  71. originating_domain = UserID.from_string(event.sender).domain
  72. if creating_domain != originating_domain:
  73. if not self.can_federate(event, auth_events):
  74. raise AuthError(
  75. 403,
  76. "This room has been marked as unfederatable."
  77. )
  78. # FIXME: Temp hack
  79. if event.type == EventTypes.Aliases:
  80. return True
  81. logger.debug(
  82. "Auth events: %s",
  83. [a.event_id for a in auth_events.values()]
  84. )
  85. if event.type == EventTypes.Member:
  86. allowed = self.is_membership_change_allowed(
  87. event, auth_events
  88. )
  89. if allowed:
  90. logger.debug("Allowing! %s", event)
  91. else:
  92. logger.debug("Denying! %s", event)
  93. return allowed
  94. self.check_event_sender_in_room(event, auth_events)
  95. self._can_send_event(event, auth_events)
  96. if event.type == EventTypes.PowerLevels:
  97. self._check_power_levels(event, auth_events)
  98. if event.type == EventTypes.Redaction:
  99. self.check_redaction(event, auth_events)
  100. logger.debug("Allowing! %s", event)
  101. except AuthError as e:
  102. logger.info(
  103. "Event auth check failed on event %s with msg: %s",
  104. event, e.msg
  105. )
  106. logger.info("Denying! %s", event)
  107. raise
  108. @defer.inlineCallbacks
  109. def check_joined_room(self, room_id, user_id, current_state=None):
  110. """Check if the user is currently joined in the room
  111. Args:
  112. room_id(str): The room to check.
  113. user_id(str): The user to check.
  114. current_state(dict): Optional map of the current state of the room.
  115. If provided then that map is used to check whether they are a
  116. member of the room. Otherwise the current membership is
  117. loaded from the database.
  118. Raises:
  119. AuthError if the user is not in the room.
  120. Returns:
  121. A deferred membership event for the user if the user is in
  122. the room.
  123. """
  124. if current_state:
  125. member = current_state.get(
  126. (EventTypes.Member, user_id),
  127. None
  128. )
  129. else:
  130. member = yield self.state.get_current_state(
  131. room_id=room_id,
  132. event_type=EventTypes.Member,
  133. state_key=user_id
  134. )
  135. self._check_joined_room(member, user_id, room_id)
  136. defer.returnValue(member)
  137. @defer.inlineCallbacks
  138. def check_user_was_in_room(self, room_id, user_id, current_state=None):
  139. """Check if the user was in the room at some point.
  140. Args:
  141. room_id(str): The room to check.
  142. user_id(str): The user to check.
  143. current_state(dict): Optional map of the current state of the room.
  144. If provided then that map is used to check whether they are a
  145. member of the room. Otherwise the current membership is
  146. loaded from the database.
  147. Raises:
  148. AuthError if the user was never in the room.
  149. Returns:
  150. A deferred membership event for the user if the user was in the
  151. room. This will be the join event if they are currently joined to
  152. the room. This will be the leave event if they have left the room.
  153. """
  154. if current_state:
  155. member = current_state.get(
  156. (EventTypes.Member, user_id),
  157. None
  158. )
  159. else:
  160. member = yield self.state.get_current_state(
  161. room_id=room_id,
  162. event_type=EventTypes.Member,
  163. state_key=user_id
  164. )
  165. membership = member.membership if member else None
  166. if membership not in (Membership.JOIN, Membership.LEAVE):
  167. raise AuthError(403, "User %s not in room %s" % (
  168. user_id, room_id
  169. ))
  170. defer.returnValue(member)
  171. @defer.inlineCallbacks
  172. def check_host_in_room(self, room_id, host):
  173. curr_state = yield self.state.get_current_state(room_id)
  174. for event in curr_state.values():
  175. if event.type == EventTypes.Member:
  176. try:
  177. if UserID.from_string(event.state_key).domain != host:
  178. continue
  179. except:
  180. logger.warn("state_key not user_id: %s", event.state_key)
  181. continue
  182. if event.content["membership"] == Membership.JOIN:
  183. defer.returnValue(True)
  184. defer.returnValue(False)
  185. def check_event_sender_in_room(self, event, auth_events):
  186. key = (EventTypes.Member, event.user_id, )
  187. member_event = auth_events.get(key)
  188. return self._check_joined_room(
  189. member_event,
  190. event.user_id,
  191. event.room_id
  192. )
  193. def _check_joined_room(self, member, user_id, room_id):
  194. if not member or member.membership != Membership.JOIN:
  195. raise AuthError(403, "User %s not in room %s (%s)" % (
  196. user_id, room_id, repr(member)
  197. ))
  198. def can_federate(self, event, auth_events):
  199. creation_event = auth_events.get((EventTypes.Create, ""))
  200. return creation_event.content.get("m.federate", True) is True
  201. @log_function
  202. def is_membership_change_allowed(self, event, auth_events):
  203. membership = event.content["membership"]
  204. # Check if this is the room creator joining:
  205. if len(event.prev_events) == 1 and Membership.JOIN == membership:
  206. # Get room creation event:
  207. key = (EventTypes.Create, "", )
  208. create = auth_events.get(key)
  209. if create and event.prev_events[0][0] == create.event_id:
  210. if create.content["creator"] == event.state_key:
  211. return True
  212. target_user_id = event.state_key
  213. creating_domain = RoomID.from_string(event.room_id).domain
  214. target_domain = UserID.from_string(target_user_id).domain
  215. if creating_domain != target_domain:
  216. if not self.can_federate(event, auth_events):
  217. raise AuthError(
  218. 403,
  219. "This room has been marked as unfederatable."
  220. )
  221. # get info about the caller
  222. key = (EventTypes.Member, event.user_id, )
  223. caller = auth_events.get(key)
  224. caller_in_room = caller and caller.membership == Membership.JOIN
  225. caller_invited = caller and caller.membership == Membership.INVITE
  226. # get info about the target
  227. key = (EventTypes.Member, target_user_id, )
  228. target = auth_events.get(key)
  229. target_in_room = target and target.membership == Membership.JOIN
  230. target_banned = target and target.membership == Membership.BAN
  231. key = (EventTypes.JoinRules, "", )
  232. join_rule_event = auth_events.get(key)
  233. if join_rule_event:
  234. join_rule = join_rule_event.content.get(
  235. "join_rule", JoinRules.INVITE
  236. )
  237. else:
  238. join_rule = JoinRules.INVITE
  239. user_level = self._get_user_power_level(event.user_id, auth_events)
  240. target_level = self._get_user_power_level(
  241. target_user_id, auth_events
  242. )
  243. # FIXME (erikj): What should we do here as the default?
  244. ban_level = self._get_named_level(auth_events, "ban", 50)
  245. logger.debug(
  246. "is_membership_change_allowed: %s",
  247. {
  248. "caller_in_room": caller_in_room,
  249. "caller_invited": caller_invited,
  250. "target_banned": target_banned,
  251. "target_in_room": target_in_room,
  252. "membership": membership,
  253. "join_rule": join_rule,
  254. "target_user_id": target_user_id,
  255. "event.user_id": event.user_id,
  256. }
  257. )
  258. if Membership.JOIN != membership:
  259. if (caller_invited
  260. and Membership.LEAVE == membership
  261. and target_user_id == event.user_id):
  262. return True
  263. if not caller_in_room: # caller isn't joined
  264. raise AuthError(
  265. 403,
  266. "%s not in room %s." % (event.user_id, event.room_id,)
  267. )
  268. if Membership.INVITE == membership:
  269. # TODO (erikj): We should probably handle this more intelligently
  270. # PRIVATE join rules.
  271. # Invites are valid iff caller is in the room and target isn't.
  272. if target_banned:
  273. raise AuthError(
  274. 403, "%s is banned from the room" % (target_user_id,)
  275. )
  276. elif target_in_room: # the target is already in the room.
  277. raise AuthError(403, "%s is already in the room." %
  278. target_user_id)
  279. else:
  280. invite_level = self._get_named_level(auth_events, "invite", 0)
  281. if user_level < invite_level:
  282. raise AuthError(
  283. 403, "You cannot invite user %s." % target_user_id
  284. )
  285. elif Membership.JOIN == membership:
  286. # Joins are valid iff caller == target and they were:
  287. # invited: They are accepting the invitation
  288. # joined: It's a NOOP
  289. if event.user_id != target_user_id:
  290. raise AuthError(403, "Cannot force another user to join.")
  291. elif target_banned:
  292. raise AuthError(403, "You are banned from this room")
  293. elif join_rule == JoinRules.PUBLIC:
  294. pass
  295. elif join_rule == JoinRules.INVITE:
  296. if not caller_in_room and not caller_invited:
  297. if not self._verify_third_party_invite(event, auth_events):
  298. raise AuthError(403, "You are not invited to this room.")
  299. else:
  300. # TODO (erikj): may_join list
  301. # TODO (erikj): private rooms
  302. raise AuthError(403, "You are not allowed to join this room")
  303. elif Membership.LEAVE == membership:
  304. # TODO (erikj): Implement kicks.
  305. if target_banned and user_level < ban_level:
  306. raise AuthError(
  307. 403, "You cannot unban user &s." % (target_user_id,)
  308. )
  309. elif target_user_id != event.user_id:
  310. kick_level = self._get_named_level(auth_events, "kick", 50)
  311. if user_level < kick_level or user_level <= target_level:
  312. raise AuthError(
  313. 403, "You cannot kick user %s." % target_user_id
  314. )
  315. elif Membership.BAN == membership:
  316. if user_level < ban_level or user_level <= target_level:
  317. raise AuthError(403, "You don't have permission to ban")
  318. else:
  319. raise AuthError(500, "Unknown membership %s" % membership)
  320. return True
  321. def _verify_third_party_invite(self, event, auth_events):
  322. """
  323. Validates that the join event is authorized by a previous third-party invite.
  324. Checks that the public key, and keyserver, match those in the invite,
  325. and that the join event has a signature issued using that public key.
  326. Args:
  327. event: The m.room.member join event being validated.
  328. auth_events: All relevant previous context events which may be used
  329. for authorization decisions.
  330. Return:
  331. True if the event fulfills the expectations of a previous third party
  332. invite event.
  333. """
  334. if not third_party_invites.join_has_third_party_invite(event.content):
  335. return False
  336. join_third_party_invite = event.content["third_party_invite"]
  337. token = join_third_party_invite["token"]
  338. invite_event = auth_events.get(
  339. (EventTypes.ThirdPartyInvite, token,)
  340. )
  341. if not invite_event:
  342. logger.info("Failing 3pid invite because no invite found for token %s", token)
  343. return False
  344. try:
  345. public_key = join_third_party_invite["public_key"]
  346. key_validity_url = join_third_party_invite["key_validity_url"]
  347. if invite_event.content["public_key"] != public_key:
  348. logger.info(
  349. "Failing 3pid invite because public key invite: %s != join: %s",
  350. invite_event.content["public_key"],
  351. public_key
  352. )
  353. return False
  354. if invite_event.content["key_validity_url"] != key_validity_url:
  355. logger.info(
  356. "Failing 3pid invite because key_validity_url invite: %s != join: %s",
  357. invite_event.content["key_validity_url"],
  358. key_validity_url
  359. )
  360. return False
  361. signed = join_third_party_invite["signed"]
  362. if signed["mxid"] != event.user_id:
  363. return False
  364. if signed["token"] != token:
  365. return False
  366. for server, signature_block in signed["signatures"].items():
  367. for key_name, encoded_signature in signature_block.items():
  368. if not key_name.startswith("ed25519:"):
  369. return False
  370. verify_key = decode_verify_key_bytes(
  371. key_name,
  372. decode_base64(public_key)
  373. )
  374. verify_signed_json(signed, server, verify_key)
  375. return True
  376. return False
  377. except (KeyError, SignatureVerifyException,):
  378. return False
  379. def _get_power_level_event(self, auth_events):
  380. key = (EventTypes.PowerLevels, "", )
  381. return auth_events.get(key)
  382. def _get_user_power_level(self, user_id, auth_events):
  383. power_level_event = self._get_power_level_event(auth_events)
  384. if power_level_event:
  385. level = power_level_event.content.get("users", {}).get(user_id)
  386. if not level:
  387. level = power_level_event.content.get("users_default", 0)
  388. if level is None:
  389. return 0
  390. else:
  391. return int(level)
  392. else:
  393. key = (EventTypes.Create, "", )
  394. create_event = auth_events.get(key)
  395. if (create_event is not None and
  396. create_event.content["creator"] == user_id):
  397. return 100
  398. else:
  399. return 0
  400. def _get_named_level(self, auth_events, name, default):
  401. power_level_event = self._get_power_level_event(auth_events)
  402. if not power_level_event:
  403. return default
  404. level = power_level_event.content.get(name, None)
  405. if level is not None:
  406. return int(level)
  407. else:
  408. return default
  409. @defer.inlineCallbacks
  410. def get_user_by_req(self, request):
  411. """ Get a registered user's ID.
  412. Args:
  413. request - An HTTP request with an access_token query parameter.
  414. Returns:
  415. tuple of:
  416. UserID (str)
  417. Access token ID (str)
  418. Raises:
  419. AuthError if no user by that token exists or the token is invalid.
  420. """
  421. # Can optionally look elsewhere in the request (e.g. headers)
  422. try:
  423. access_token = request.args["access_token"][0]
  424. # Check for application service tokens with a user_id override
  425. try:
  426. app_service = yield self.store.get_app_service_by_token(
  427. access_token
  428. )
  429. if not app_service:
  430. raise KeyError
  431. user_id = app_service.sender
  432. if "user_id" in request.args:
  433. user_id = request.args["user_id"][0]
  434. if not app_service.is_interested_in_user(user_id):
  435. raise AuthError(
  436. 403,
  437. "Application service cannot masquerade as this user."
  438. )
  439. if not user_id:
  440. raise KeyError
  441. request.authenticated_entity = user_id
  442. defer.returnValue((UserID.from_string(user_id), ""))
  443. return
  444. except KeyError:
  445. pass # normal users won't have the user_id query parameter set.
  446. user_info = yield self._get_user_by_access_token(access_token)
  447. user = user_info["user"]
  448. token_id = user_info["token_id"]
  449. ip_addr = self.hs.get_ip_from_request(request)
  450. user_agent = request.requestHeaders.getRawHeaders(
  451. "User-Agent",
  452. default=[""]
  453. )[0]
  454. if user and access_token and ip_addr:
  455. self.store.insert_client_ip(
  456. user=user,
  457. access_token=access_token,
  458. ip=ip_addr,
  459. user_agent=user_agent
  460. )
  461. request.authenticated_entity = user.to_string()
  462. defer.returnValue((user, token_id,))
  463. except KeyError:
  464. raise AuthError(
  465. self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token.",
  466. errcode=Codes.MISSING_TOKEN
  467. )
  468. @defer.inlineCallbacks
  469. def _get_user_by_access_token(self, token):
  470. """ Get a registered user's ID.
  471. Args:
  472. token (str): The access token to get the user by.
  473. Returns:
  474. dict : dict that includes the user and the ID of their access token.
  475. Raises:
  476. AuthError if no user by that token exists or the token is invalid.
  477. """
  478. try:
  479. ret = yield self._get_user_from_macaroon(token)
  480. except AuthError:
  481. # TODO(daniel): Remove this fallback when all existing access tokens
  482. # have been re-issued as macaroons.
  483. ret = yield self._look_up_user_by_access_token(token)
  484. defer.returnValue(ret)
  485. @defer.inlineCallbacks
  486. def _get_user_from_macaroon(self, macaroon_str):
  487. try:
  488. macaroon = pymacaroons.Macaroon.deserialize(macaroon_str)
  489. self._validate_macaroon(macaroon)
  490. user_prefix = "user_id = "
  491. for caveat in macaroon.caveats:
  492. if caveat.caveat_id.startswith(user_prefix):
  493. user = UserID.from_string(caveat.caveat_id[len(user_prefix):])
  494. # This codepath exists so that we can actually return a
  495. # token ID, because we use token IDs in place of device
  496. # identifiers throughout the codebase.
  497. # TODO(daniel): Remove this fallback when device IDs are
  498. # properly implemented.
  499. ret = yield self._look_up_user_by_access_token(macaroon_str)
  500. if ret["user"] != user:
  501. logger.error(
  502. "Macaroon user (%s) != DB user (%s)",
  503. user,
  504. ret["user"]
  505. )
  506. raise AuthError(
  507. self.TOKEN_NOT_FOUND_HTTP_STATUS,
  508. "User mismatch in macaroon",
  509. errcode=Codes.UNKNOWN_TOKEN
  510. )
  511. defer.returnValue(ret)
  512. raise AuthError(
  513. self.TOKEN_NOT_FOUND_HTTP_STATUS, "No user caveat in macaroon",
  514. errcode=Codes.UNKNOWN_TOKEN
  515. )
  516. except (pymacaroons.exceptions.MacaroonException, TypeError, ValueError):
  517. raise AuthError(
  518. self.TOKEN_NOT_FOUND_HTTP_STATUS, "Invalid macaroon passed.",
  519. errcode=Codes.UNKNOWN_TOKEN
  520. )
  521. def _validate_macaroon(self, macaroon):
  522. v = pymacaroons.Verifier()
  523. v.satisfy_exact("gen = 1")
  524. v.satisfy_exact("type = access")
  525. v.satisfy_general(lambda c: c.startswith("user_id = "))
  526. v.satisfy_general(self._verify_expiry)
  527. v.verify(macaroon, self.hs.config.macaroon_secret_key)
  528. v = pymacaroons.Verifier()
  529. v.satisfy_general(self._verify_recognizes_caveats)
  530. v.verify(macaroon, self.hs.config.macaroon_secret_key)
  531. def _verify_expiry(self, caveat):
  532. prefix = "time < "
  533. if not caveat.startswith(prefix):
  534. return False
  535. # TODO(daniel): Enable expiry check when clients actually know how to
  536. # refresh tokens. (And remember to enable the tests)
  537. return True
  538. expiry = int(caveat[len(prefix):])
  539. now = self.hs.get_clock().time_msec()
  540. return now < expiry
  541. def _verify_recognizes_caveats(self, caveat):
  542. first_space = caveat.find(" ")
  543. if first_space < 0:
  544. return False
  545. second_space = caveat.find(" ", first_space + 1)
  546. if second_space < 0:
  547. return False
  548. return caveat[:second_space + 1] in self._KNOWN_CAVEAT_PREFIXES
  549. @defer.inlineCallbacks
  550. def _look_up_user_by_access_token(self, token):
  551. ret = yield self.store.get_user_by_access_token(token)
  552. if not ret:
  553. raise AuthError(
  554. self.TOKEN_NOT_FOUND_HTTP_STATUS, "Unrecognised access token.",
  555. errcode=Codes.UNKNOWN_TOKEN
  556. )
  557. user_info = {
  558. "user": UserID.from_string(ret.get("name")),
  559. "token_id": ret.get("token_id", None),
  560. }
  561. defer.returnValue(user_info)
  562. @defer.inlineCallbacks
  563. def get_appservice_by_req(self, request):
  564. try:
  565. token = request.args["access_token"][0]
  566. service = yield self.store.get_app_service_by_token(token)
  567. if not service:
  568. raise AuthError(
  569. self.TOKEN_NOT_FOUND_HTTP_STATUS,
  570. "Unrecognised access token.",
  571. errcode=Codes.UNKNOWN_TOKEN
  572. )
  573. request.authenticated_entity = service.sender
  574. defer.returnValue(service)
  575. except KeyError:
  576. raise AuthError(
  577. self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token."
  578. )
  579. def is_server_admin(self, user):
  580. return self.store.is_server_admin(user)
  581. @defer.inlineCallbacks
  582. def add_auth_events(self, builder, context):
  583. auth_ids = self.compute_auth_events(builder, context.current_state)
  584. auth_events_entries = yield self.store.add_event_hashes(
  585. auth_ids
  586. )
  587. builder.auth_events = auth_events_entries
  588. def compute_auth_events(self, event, current_state):
  589. if event.type == EventTypes.Create:
  590. return []
  591. auth_ids = []
  592. key = (EventTypes.PowerLevels, "", )
  593. power_level_event = current_state.get(key)
  594. if power_level_event:
  595. auth_ids.append(power_level_event.event_id)
  596. key = (EventTypes.JoinRules, "", )
  597. join_rule_event = current_state.get(key)
  598. key = (EventTypes.Member, event.user_id, )
  599. member_event = current_state.get(key)
  600. key = (EventTypes.Create, "", )
  601. create_event = current_state.get(key)
  602. if create_event:
  603. auth_ids.append(create_event.event_id)
  604. if join_rule_event:
  605. join_rule = join_rule_event.content.get("join_rule")
  606. is_public = join_rule == JoinRules.PUBLIC if join_rule else False
  607. else:
  608. is_public = False
  609. if event.type == EventTypes.Member:
  610. e_type = event.content["membership"]
  611. if e_type in [Membership.JOIN, Membership.INVITE]:
  612. if join_rule_event:
  613. auth_ids.append(join_rule_event.event_id)
  614. if e_type == Membership.JOIN:
  615. if member_event and not is_public:
  616. auth_ids.append(member_event.event_id)
  617. if third_party_invites.join_has_third_party_invite(event.content):
  618. key = (
  619. EventTypes.ThirdPartyInvite,
  620. event.content["third_party_invite"]["token"]
  621. )
  622. invite = current_state.get(key)
  623. if invite:
  624. auth_ids.append(invite.event_id)
  625. else:
  626. if member_event:
  627. auth_ids.append(member_event.event_id)
  628. elif member_event:
  629. if member_event.content["membership"] == Membership.JOIN:
  630. auth_ids.append(member_event.event_id)
  631. return auth_ids
  632. @log_function
  633. def _can_send_event(self, event, auth_events):
  634. key = (EventTypes.PowerLevels, "", )
  635. send_level_event = auth_events.get(key)
  636. send_level = None
  637. if send_level_event:
  638. send_level = send_level_event.content.get("events", {}).get(
  639. event.type
  640. )
  641. if send_level is None:
  642. if hasattr(event, "state_key"):
  643. send_level = send_level_event.content.get(
  644. "state_default", 50
  645. )
  646. else:
  647. send_level = send_level_event.content.get(
  648. "events_default", 0
  649. )
  650. if send_level:
  651. send_level = int(send_level)
  652. else:
  653. send_level = 0
  654. user_level = self._get_user_power_level(event.user_id, auth_events)
  655. if user_level < send_level:
  656. raise AuthError(
  657. 403,
  658. "You don't have permission to post that to the room. " +
  659. "user_level (%d) < send_level (%d)" % (user_level, send_level)
  660. )
  661. # Check state_key
  662. if hasattr(event, "state_key"):
  663. if event.state_key.startswith("@"):
  664. if event.state_key != event.user_id:
  665. raise AuthError(
  666. 403,
  667. "You are not allowed to set others state"
  668. )
  669. else:
  670. sender_domain = UserID.from_string(
  671. event.user_id
  672. ).domain
  673. if sender_domain != event.state_key:
  674. raise AuthError(
  675. 403,
  676. "You are not allowed to set others state"
  677. )
  678. return True
  679. def check_redaction(self, event, auth_events):
  680. """Check whether the event sender is allowed to redact the target event.
  681. Returns:
  682. True if the the sender is allowed to redact the target event if the
  683. target event was created by them.
  684. False if the sender is allowed to redact the target event with no
  685. further checks.
  686. Raises:
  687. AuthError if the event sender is definitely not allowed to redact
  688. the target event.
  689. """
  690. user_level = self._get_user_power_level(event.user_id, auth_events)
  691. redact_level = self._get_named_level(auth_events, "redact", 50)
  692. if user_level > redact_level:
  693. return False
  694. redacter_domain = EventID.from_string(event.event_id).domain
  695. redactee_domain = EventID.from_string(event.redacts).domain
  696. if redacter_domain == redactee_domain:
  697. return True
  698. raise AuthError(
  699. 403,
  700. "You don't have permission to redact events"
  701. )
  702. def _check_power_levels(self, event, auth_events):
  703. user_list = event.content.get("users", {})
  704. # Validate users
  705. for k, v in user_list.items():
  706. try:
  707. UserID.from_string(k)
  708. except:
  709. raise SynapseError(400, "Not a valid user_id: %s" % (k,))
  710. try:
  711. int(v)
  712. except:
  713. raise SynapseError(400, "Not a valid power level: %s" % (v,))
  714. key = (event.type, event.state_key, )
  715. current_state = auth_events.get(key)
  716. if not current_state:
  717. return
  718. user_level = self._get_user_power_level(event.user_id, auth_events)
  719. # Check other levels:
  720. levels_to_check = [
  721. ("users_default", None),
  722. ("events_default", None),
  723. ("state_default", None),
  724. ("ban", None),
  725. ("redact", None),
  726. ("kick", None),
  727. ("invite", None),
  728. ]
  729. old_list = current_state.content.get("users")
  730. for user in set(old_list.keys() + user_list.keys()):
  731. levels_to_check.append(
  732. (user, "users")
  733. )
  734. old_list = current_state.content.get("events")
  735. new_list = event.content.get("events")
  736. for ev_id in set(old_list.keys() + new_list.keys()):
  737. levels_to_check.append(
  738. (ev_id, "events")
  739. )
  740. old_state = current_state.content
  741. new_state = event.content
  742. for level_to_check, dir in levels_to_check:
  743. old_loc = old_state
  744. new_loc = new_state
  745. if dir:
  746. old_loc = old_loc.get(dir, {})
  747. new_loc = new_loc.get(dir, {})
  748. if level_to_check in old_loc:
  749. old_level = int(old_loc[level_to_check])
  750. else:
  751. old_level = None
  752. if level_to_check in new_loc:
  753. new_level = int(new_loc[level_to_check])
  754. else:
  755. new_level = None
  756. if new_level is not None and old_level is not None:
  757. if new_level == old_level:
  758. continue
  759. if dir == "users" and level_to_check != event.user_id:
  760. if old_level == user_level:
  761. raise AuthError(
  762. 403,
  763. "You don't have permission to remove ops level equal "
  764. "to your own"
  765. )
  766. if old_level > user_level or new_level > user_level:
  767. raise AuthError(
  768. 403,
  769. "You don't have permission to add ops level greater "
  770. "than your own"
  771. )