forked from MirrorHub/synapse
Fix email push in pusher worker
This was broken when device list updates were implemented, as Mailer could no longer instantiate an AuthHandler due to a dependency on federation sending.
This commit is contained in:
parent
14d5e22700
commit
51adaac953
8 changed files with 70 additions and 57 deletions
|
@ -65,6 +65,7 @@ class AuthHandler(BaseHandler):
|
||||||
|
|
||||||
self.hs = hs # FIXME better possibility to access registrationHandler later?
|
self.hs = hs # FIXME better possibility to access registrationHandler later?
|
||||||
self.device_handler = hs.get_device_handler()
|
self.device_handler = hs.get_device_handler()
|
||||||
|
self.macaroon_gen = hs.get_macaroon_generator()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_auth(self, flows, clientdict, clientip):
|
def check_auth(self, flows, clientdict, clientip):
|
||||||
|
@ -529,37 +530,11 @@ class AuthHandler(BaseHandler):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def issue_access_token(self, user_id, device_id=None):
|
def issue_access_token(self, user_id, device_id=None):
|
||||||
access_token = self.generate_access_token(user_id)
|
access_token = self.macaroon_gen.generate_access_token(user_id)
|
||||||
yield self.store.add_access_token_to_user(user_id, access_token,
|
yield self.store.add_access_token_to_user(user_id, access_token,
|
||||||
device_id)
|
device_id)
|
||||||
defer.returnValue(access_token)
|
defer.returnValue(access_token)
|
||||||
|
|
||||||
def generate_access_token(self, user_id, extra_caveats=None):
|
|
||||||
extra_caveats = extra_caveats or []
|
|
||||||
macaroon = self._generate_base_macaroon(user_id)
|
|
||||||
macaroon.add_first_party_caveat("type = access")
|
|
||||||
# Include a nonce, to make sure that each login gets a different
|
|
||||||
# access token.
|
|
||||||
macaroon.add_first_party_caveat("nonce = %s" % (
|
|
||||||
stringutils.random_string_with_symbols(16),
|
|
||||||
))
|
|
||||||
for caveat in extra_caveats:
|
|
||||||
macaroon.add_first_party_caveat(caveat)
|
|
||||||
return macaroon.serialize()
|
|
||||||
|
|
||||||
def generate_short_term_login_token(self, user_id, duration_in_ms=(2 * 60 * 1000)):
|
|
||||||
macaroon = self._generate_base_macaroon(user_id)
|
|
||||||
macaroon.add_first_party_caveat("type = login")
|
|
||||||
now = self.hs.get_clock().time_msec()
|
|
||||||
expiry = now + duration_in_ms
|
|
||||||
macaroon.add_first_party_caveat("time < %d" % (expiry,))
|
|
||||||
return macaroon.serialize()
|
|
||||||
|
|
||||||
def generate_delete_pusher_token(self, user_id):
|
|
||||||
macaroon = self._generate_base_macaroon(user_id)
|
|
||||||
macaroon.add_first_party_caveat("type = delete_pusher")
|
|
||||||
return macaroon.serialize()
|
|
||||||
|
|
||||||
def validate_short_term_login_token_and_get_user_id(self, login_token):
|
def validate_short_term_login_token_and_get_user_id(self, login_token):
|
||||||
auth_api = self.hs.get_auth()
|
auth_api = self.hs.get_auth()
|
||||||
try:
|
try:
|
||||||
|
@ -570,15 +545,6 @@ class AuthHandler(BaseHandler):
|
||||||
except Exception:
|
except Exception:
|
||||||
raise AuthError(403, "Invalid token", errcode=Codes.FORBIDDEN)
|
raise AuthError(403, "Invalid token", errcode=Codes.FORBIDDEN)
|
||||||
|
|
||||||
def _generate_base_macaroon(self, user_id):
|
|
||||||
macaroon = pymacaroons.Macaroon(
|
|
||||||
location=self.hs.config.server_name,
|
|
||||||
identifier="key",
|
|
||||||
key=self.hs.config.macaroon_secret_key)
|
|
||||||
macaroon.add_first_party_caveat("gen = 1")
|
|
||||||
macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
|
|
||||||
return macaroon
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def set_password(self, user_id, newpassword, requester=None):
|
def set_password(self, user_id, newpassword, requester=None):
|
||||||
password_hash = self.hash(newpassword)
|
password_hash = self.hash(newpassword)
|
||||||
|
@ -673,6 +639,48 @@ class AuthHandler(BaseHandler):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class MacaroonGeneartor(object):
|
||||||
|
def __init__(self, hs):
|
||||||
|
self.clock = hs.get_clock()
|
||||||
|
self.server_name = hs.config.server_name
|
||||||
|
self.macaroon_secret_key = hs.config.macaroon_secret_key
|
||||||
|
|
||||||
|
def generate_access_token(self, user_id, extra_caveats=None):
|
||||||
|
extra_caveats = extra_caveats or []
|
||||||
|
macaroon = self._generate_base_macaroon(user_id)
|
||||||
|
macaroon.add_first_party_caveat("type = access")
|
||||||
|
# Include a nonce, to make sure that each login gets a different
|
||||||
|
# access token.
|
||||||
|
macaroon.add_first_party_caveat("nonce = %s" % (
|
||||||
|
stringutils.random_string_with_symbols(16),
|
||||||
|
))
|
||||||
|
for caveat in extra_caveats:
|
||||||
|
macaroon.add_first_party_caveat(caveat)
|
||||||
|
return macaroon.serialize()
|
||||||
|
|
||||||
|
def generate_short_term_login_token(self, user_id, duration_in_ms=(2 * 60 * 1000)):
|
||||||
|
macaroon = self._generate_base_macaroon(user_id)
|
||||||
|
macaroon.add_first_party_caveat("type = login")
|
||||||
|
now = self.clock.time_msec()
|
||||||
|
expiry = now + duration_in_ms
|
||||||
|
macaroon.add_first_party_caveat("time < %d" % (expiry,))
|
||||||
|
return macaroon.serialize()
|
||||||
|
|
||||||
|
def generate_delete_pusher_token(self, user_id):
|
||||||
|
macaroon = self._generate_base_macaroon(user_id)
|
||||||
|
macaroon.add_first_party_caveat("type = delete_pusher")
|
||||||
|
return macaroon.serialize()
|
||||||
|
|
||||||
|
def _generate_base_macaroon(self, user_id):
|
||||||
|
macaroon = pymacaroons.Macaroon(
|
||||||
|
location=self.server_name,
|
||||||
|
identifier="key",
|
||||||
|
key=self.macaroon_secret_key)
|
||||||
|
macaroon.add_first_party_caveat("gen = 1")
|
||||||
|
macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
|
||||||
|
return macaroon
|
||||||
|
|
||||||
|
|
||||||
class _AccountHandler(object):
|
class _AccountHandler(object):
|
||||||
"""A proxy object that gets passed to password auth providers so they
|
"""A proxy object that gets passed to password auth providers so they
|
||||||
can register new users etc if necessary.
|
can register new users etc if necessary.
|
||||||
|
|
|
@ -40,6 +40,8 @@ class RegistrationHandler(BaseHandler):
|
||||||
|
|
||||||
self._next_generated_user_id = None
|
self._next_generated_user_id = None
|
||||||
|
|
||||||
|
self.macaroon_gen = hs.get_macaroon_generator()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_username(self, localpart, guest_access_token=None,
|
def check_username(self, localpart, guest_access_token=None,
|
||||||
assigned_user_id=None):
|
assigned_user_id=None):
|
||||||
|
@ -143,7 +145,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
|
|
||||||
token = None
|
token = None
|
||||||
if generate_token:
|
if generate_token:
|
||||||
token = self.auth_handler().generate_access_token(user_id)
|
token = self.macaroon_gen.generate_access_token(user_id)
|
||||||
yield self.store.register(
|
yield self.store.register(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
token=token,
|
token=token,
|
||||||
|
@ -167,7 +169,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
user_id = user.to_string()
|
user_id = user.to_string()
|
||||||
yield self.check_user_id_not_appservice_exclusive(user_id)
|
yield self.check_user_id_not_appservice_exclusive(user_id)
|
||||||
if generate_token:
|
if generate_token:
|
||||||
token = self.auth_handler().generate_access_token(user_id)
|
token = self.macaroon_gen.generate_access_token(user_id)
|
||||||
try:
|
try:
|
||||||
yield self.store.register(
|
yield self.store.register(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
@ -254,7 +256,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
user_id = user.to_string()
|
user_id = user.to_string()
|
||||||
|
|
||||||
yield self.check_user_id_not_appservice_exclusive(user_id)
|
yield self.check_user_id_not_appservice_exclusive(user_id)
|
||||||
token = self.auth_handler().generate_access_token(user_id)
|
token = self.macaroon_gen.generate_access_token(user_id)
|
||||||
try:
|
try:
|
||||||
yield self.store.register(
|
yield self.store.register(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
@ -399,7 +401,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
|
|
||||||
user = UserID(localpart, self.hs.hostname)
|
user = UserID(localpart, self.hs.hostname)
|
||||||
user_id = user.to_string()
|
user_id = user.to_string()
|
||||||
token = self.auth_handler().generate_access_token(user_id)
|
token = self.macaroon_gen.generate_access_token(user_id)
|
||||||
|
|
||||||
if need_register:
|
if need_register:
|
||||||
yield self.store.register(
|
yield self.store.register(
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Mailer(object):
|
||||||
def __init__(self, hs, app_name):
|
def __init__(self, hs, app_name):
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
self.store = self.hs.get_datastore()
|
self.store = self.hs.get_datastore()
|
||||||
self.auth_handler = self.hs.get_auth_handler()
|
self.macaroon_gen = self.hs.get_macaroon_generator()
|
||||||
self.state_handler = self.hs.get_state_handler()
|
self.state_handler = self.hs.get_state_handler()
|
||||||
loader = jinja2.FileSystemLoader(self.hs.config.email_template_dir)
|
loader = jinja2.FileSystemLoader(self.hs.config.email_template_dir)
|
||||||
self.app_name = app_name
|
self.app_name = app_name
|
||||||
|
@ -466,7 +466,7 @@ class Mailer(object):
|
||||||
|
|
||||||
def make_unsubscribe_link(self, user_id, app_id, email_address):
|
def make_unsubscribe_link(self, user_id, app_id, email_address):
|
||||||
params = {
|
params = {
|
||||||
"access_token": self.auth_handler.generate_delete_pusher_token(user_id),
|
"access_token": self.macaroon_gen.generate_delete_pusher_token(user_id),
|
||||||
"app_id": app_id,
|
"app_id": app_id,
|
||||||
"pushkey": email_address,
|
"pushkey": email_address,
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,6 +330,7 @@ class CasTicketServlet(ClientV1RestServlet):
|
||||||
self.cas_required_attributes = hs.config.cas_required_attributes
|
self.cas_required_attributes = hs.config.cas_required_attributes
|
||||||
self.auth_handler = hs.get_auth_handler()
|
self.auth_handler = hs.get_auth_handler()
|
||||||
self.handlers = hs.get_handlers()
|
self.handlers = hs.get_handlers()
|
||||||
|
self.macaroon_gen = hs.get_macaroon_generator()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_GET(self, request):
|
def on_GET(self, request):
|
||||||
|
@ -368,7 +369,9 @@ class CasTicketServlet(ClientV1RestServlet):
|
||||||
yield self.handlers.registration_handler.register(localpart=user)
|
yield self.handlers.registration_handler.register(localpart=user)
|
||||||
)
|
)
|
||||||
|
|
||||||
login_token = auth_handler.generate_short_term_login_token(registered_user_id)
|
login_token = self.macaroon_gen.generate_short_term_login_token(
|
||||||
|
registered_user_id
|
||||||
|
)
|
||||||
redirect_url = self.add_login_token_to_redirect_url(client_redirect_url,
|
redirect_url = self.add_login_token_to_redirect_url(client_redirect_url,
|
||||||
login_token)
|
login_token)
|
||||||
request.redirect(redirect_url)
|
request.redirect(redirect_url)
|
||||||
|
|
|
@ -96,6 +96,7 @@ class RegisterRestServlet(RestServlet):
|
||||||
self.registration_handler = hs.get_handlers().registration_handler
|
self.registration_handler = hs.get_handlers().registration_handler
|
||||||
self.identity_handler = hs.get_handlers().identity_handler
|
self.identity_handler = hs.get_handlers().identity_handler
|
||||||
self.device_handler = hs.get_device_handler()
|
self.device_handler = hs.get_device_handler()
|
||||||
|
self.macaroon_gen = hs.get_macaroon_generator()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, request):
|
def on_POST(self, request):
|
||||||
|
@ -436,7 +437,7 @@ class RegisterRestServlet(RestServlet):
|
||||||
user_id, device_id, initial_display_name
|
user_id, device_id, initial_display_name
|
||||||
)
|
)
|
||||||
|
|
||||||
access_token = self.auth_handler.generate_access_token(
|
access_token = self.macaroon_gen.generate_access_token(
|
||||||
user_id, ["guest = true"]
|
user_id, ["guest = true"]
|
||||||
)
|
)
|
||||||
defer.returnValue((200, {
|
defer.returnValue((200, {
|
||||||
|
|
|
@ -37,7 +37,7 @@ from synapse.federation.transport.client import TransportLayerClient
|
||||||
from synapse.federation.transaction_queue import TransactionQueue
|
from synapse.federation.transaction_queue import TransactionQueue
|
||||||
from synapse.handlers import Handlers
|
from synapse.handlers import Handlers
|
||||||
from synapse.handlers.appservice import ApplicationServicesHandler
|
from synapse.handlers.appservice import ApplicationServicesHandler
|
||||||
from synapse.handlers.auth import AuthHandler
|
from synapse.handlers.auth import AuthHandler, MacaroonGeneartor
|
||||||
from synapse.handlers.devicemessage import DeviceMessageHandler
|
from synapse.handlers.devicemessage import DeviceMessageHandler
|
||||||
from synapse.handlers.device import DeviceHandler
|
from synapse.handlers.device import DeviceHandler
|
||||||
from synapse.handlers.e2e_keys import E2eKeysHandler
|
from synapse.handlers.e2e_keys import E2eKeysHandler
|
||||||
|
@ -131,6 +131,7 @@ class HomeServer(object):
|
||||||
'federation_transport_client',
|
'federation_transport_client',
|
||||||
'federation_sender',
|
'federation_sender',
|
||||||
'receipts_handler',
|
'receipts_handler',
|
||||||
|
'macaroon_generator',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, hostname, **kwargs):
|
def __init__(self, hostname, **kwargs):
|
||||||
|
@ -213,6 +214,9 @@ class HomeServer(object):
|
||||||
def build_auth_handler(self):
|
def build_auth_handler(self):
|
||||||
return AuthHandler(self)
|
return AuthHandler(self)
|
||||||
|
|
||||||
|
def build_macaroon_generator(self):
|
||||||
|
return MacaroonGeneartor(self)
|
||||||
|
|
||||||
def build_device_handler(self):
|
def build_device_handler(self):
|
||||||
return DeviceHandler(self)
|
return DeviceHandler(self)
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,10 @@ class AuthTestCase(unittest.TestCase):
|
||||||
self.hs = yield setup_test_homeserver(handlers=None)
|
self.hs = yield setup_test_homeserver(handlers=None)
|
||||||
self.hs.handlers = AuthHandlers(self.hs)
|
self.hs.handlers = AuthHandlers(self.hs)
|
||||||
self.auth_handler = self.hs.handlers.auth_handler
|
self.auth_handler = self.hs.handlers.auth_handler
|
||||||
|
self.macaroon_generator = self.hs.get_macaroon_generator()
|
||||||
|
|
||||||
def test_token_is_a_macaroon(self):
|
def test_token_is_a_macaroon(self):
|
||||||
self.hs.config.macaroon_secret_key = "this key is a huge secret"
|
token = self.macaroon_generator.generate_access_token("some_user")
|
||||||
|
|
||||||
token = self.auth_handler.generate_access_token("some_user")
|
|
||||||
# Check that we can parse the thing with pymacaroons
|
# Check that we can parse the thing with pymacaroons
|
||||||
macaroon = pymacaroons.Macaroon.deserialize(token)
|
macaroon = pymacaroons.Macaroon.deserialize(token)
|
||||||
# The most basic of sanity checks
|
# The most basic of sanity checks
|
||||||
|
@ -46,10 +45,9 @@ class AuthTestCase(unittest.TestCase):
|
||||||
self.fail("some_user was not in %s" % macaroon.inspect())
|
self.fail("some_user was not in %s" % macaroon.inspect())
|
||||||
|
|
||||||
def test_macaroon_caveats(self):
|
def test_macaroon_caveats(self):
|
||||||
self.hs.config.macaroon_secret_key = "this key is a massive secret"
|
|
||||||
self.hs.clock.now = 5000
|
self.hs.clock.now = 5000
|
||||||
|
|
||||||
token = self.auth_handler.generate_access_token("a_user")
|
token = self.macaroon_generator.generate_access_token("a_user")
|
||||||
macaroon = pymacaroons.Macaroon.deserialize(token)
|
macaroon = pymacaroons.Macaroon.deserialize(token)
|
||||||
|
|
||||||
def verify_gen(caveat):
|
def verify_gen(caveat):
|
||||||
|
@ -74,7 +72,7 @@ class AuthTestCase(unittest.TestCase):
|
||||||
def test_short_term_login_token_gives_user_id(self):
|
def test_short_term_login_token_gives_user_id(self):
|
||||||
self.hs.clock.now = 1000
|
self.hs.clock.now = 1000
|
||||||
|
|
||||||
token = self.auth_handler.generate_short_term_login_token(
|
token = self.macaroon_generator.generate_short_term_login_token(
|
||||||
"a_user", 5000
|
"a_user", 5000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,7 +91,7 @@ class AuthTestCase(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_short_term_login_token_cannot_replace_user_id(self):
|
def test_short_term_login_token_cannot_replace_user_id(self):
|
||||||
token = self.auth_handler.generate_short_term_login_token(
|
token = self.macaroon_generator.generate_short_term_login_token(
|
||||||
"a_user", 5000
|
"a_user", 5000
|
||||||
)
|
)
|
||||||
macaroon = pymacaroons.Macaroon.deserialize(token)
|
macaroon = pymacaroons.Macaroon.deserialize(token)
|
||||||
|
|
|
@ -41,15 +41,12 @@ class RegistrationTestCase(unittest.TestCase):
|
||||||
handlers=None,
|
handlers=None,
|
||||||
http_client=None,
|
http_client=None,
|
||||||
expire_access_token=True)
|
expire_access_token=True)
|
||||||
self.auth_handler = Mock(
|
self.macaroon_generator = Mock(
|
||||||
generate_access_token=Mock(return_value='secret'))
|
generate_access_token=Mock(return_value='secret'))
|
||||||
|
self.hs.get_macaroon_generator = Mock(return_value=self.macaroon_generator)
|
||||||
self.hs.handlers = RegistrationHandlers(self.hs)
|
self.hs.handlers = RegistrationHandlers(self.hs)
|
||||||
self.handler = self.hs.get_handlers().registration_handler
|
self.handler = self.hs.get_handlers().registration_handler
|
||||||
self.hs.get_handlers().profile_handler = Mock()
|
self.hs.get_handlers().profile_handler = Mock()
|
||||||
self.mock_handler = Mock(spec=[
|
|
||||||
"generate_access_token",
|
|
||||||
])
|
|
||||||
self.hs.get_auth_handler = Mock(return_value=self.auth_handler)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_user_is_created_and_logged_in_if_doesnt_exist(self):
|
def test_user_is_created_and_logged_in_if_doesnt_exist(self):
|
||||||
|
|
Loading…
Reference in a new issue