test_auth.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015, 2016 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. from mock import Mock
  16. import pymacaroons
  17. from twisted.internet import defer
  18. import synapse
  19. import synapse.api.errors
  20. from synapse.api.errors import AuthError
  21. from synapse.handlers.auth import AuthHandler
  22. from tests import unittest
  23. from tests.utils import setup_test_homeserver
  24. class AuthHandlers(object):
  25. def __init__(self, hs):
  26. self.auth_handler = AuthHandler(hs)
  27. class AuthTestCase(unittest.TestCase):
  28. @defer.inlineCallbacks
  29. def setUp(self):
  30. self.hs = yield setup_test_homeserver(handlers=None)
  31. self.hs.handlers = AuthHandlers(self.hs)
  32. self.auth_handler = self.hs.handlers.auth_handler
  33. self.macaroon_generator = self.hs.get_macaroon_generator()
  34. # MAU tests
  35. self.hs.config.max_mau_value = 50
  36. self.small_number_of_users = 1
  37. self.large_number_of_users = 100
  38. def test_token_is_a_macaroon(self):
  39. token = self.macaroon_generator.generate_access_token("some_user")
  40. # Check that we can parse the thing with pymacaroons
  41. macaroon = pymacaroons.Macaroon.deserialize(token)
  42. # The most basic of sanity checks
  43. if "some_user" not in macaroon.inspect():
  44. self.fail("some_user was not in %s" % macaroon.inspect())
  45. def test_macaroon_caveats(self):
  46. self.hs.clock.now = 5000
  47. token = self.macaroon_generator.generate_access_token("a_user")
  48. macaroon = pymacaroons.Macaroon.deserialize(token)
  49. def verify_gen(caveat):
  50. return caveat == "gen = 1"
  51. def verify_user(caveat):
  52. return caveat == "user_id = a_user"
  53. def verify_type(caveat):
  54. return caveat == "type = access"
  55. def verify_nonce(caveat):
  56. return caveat.startswith("nonce =")
  57. v = pymacaroons.Verifier()
  58. v.satisfy_general(verify_gen)
  59. v.satisfy_general(verify_user)
  60. v.satisfy_general(verify_type)
  61. v.satisfy_general(verify_nonce)
  62. v.verify(macaroon, self.hs.config.macaroon_secret_key)
  63. @defer.inlineCallbacks
  64. def test_short_term_login_token_gives_user_id(self):
  65. self.hs.clock.now = 1000
  66. token = self.macaroon_generator.generate_short_term_login_token(
  67. "a_user", 5000
  68. )
  69. user_id = yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  70. token
  71. )
  72. self.assertEqual("a_user", user_id)
  73. # when we advance the clock, the token should be rejected
  74. self.hs.clock.now = 6000
  75. with self.assertRaises(synapse.api.errors.AuthError):
  76. yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  77. token
  78. )
  79. @defer.inlineCallbacks
  80. def test_short_term_login_token_cannot_replace_user_id(self):
  81. token = self.macaroon_generator.generate_short_term_login_token(
  82. "a_user", 5000
  83. )
  84. macaroon = pymacaroons.Macaroon.deserialize(token)
  85. user_id = yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  86. macaroon.serialize()
  87. )
  88. self.assertEqual(
  89. "a_user", user_id
  90. )
  91. # add another "user_id" caveat, which might allow us to override the
  92. # user_id.
  93. macaroon.add_first_party_caveat("user_id = b_user")
  94. with self.assertRaises(synapse.api.errors.AuthError):
  95. yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  96. macaroon.serialize()
  97. )
  98. @defer.inlineCallbacks
  99. def test_mau_limits_disabled(self):
  100. self.hs.config.limit_usage_by_mau = False
  101. # Ensure does not throw exception
  102. yield self.auth_handler.get_access_token_for_user_id('user_a')
  103. yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  104. self._get_macaroon().serialize()
  105. )
  106. @defer.inlineCallbacks
  107. def test_mau_limits_exceeded(self):
  108. self.hs.config.limit_usage_by_mau = True
  109. self.hs.get_datastore().count_monthly_users = Mock(
  110. return_value=defer.succeed(self.large_number_of_users)
  111. )
  112. with self.assertRaises(AuthError):
  113. yield self.auth_handler.get_access_token_for_user_id('user_a')
  114. self.hs.get_datastore().count_monthly_users = Mock(
  115. return_value=defer.succeed(self.large_number_of_users)
  116. )
  117. with self.assertRaises(AuthError):
  118. yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  119. self._get_macaroon().serialize()
  120. )
  121. @defer.inlineCallbacks
  122. def test_mau_limits_not_exceeded(self):
  123. self.hs.config.limit_usage_by_mau = True
  124. self.hs.get_datastore().count_monthly_users = Mock(
  125. return_value=defer.succeed(self.small_number_of_users)
  126. )
  127. # Ensure does not raise exception
  128. yield self.auth_handler.get_access_token_for_user_id('user_a')
  129. self.hs.get_datastore().count_monthly_users = Mock(
  130. return_value=defer.succeed(self.small_number_of_users)
  131. )
  132. yield self.auth_handler.validate_short_term_login_token_and_get_user_id(
  133. self._get_macaroon().serialize()
  134. )
  135. def _get_macaroon(self):
  136. token = self.macaroon_generator.generate_short_term_login_token(
  137. "user_a", 5000
  138. )
  139. return pymacaroons.Macaroon.deserialize(token)