diff --git a/emails/signin_link.spt b/emails/signin_link.spt new file mode 100644 index 0000000000..dc4b23fbc7 --- /dev/null +++ b/emails/signin_link.spt @@ -0,0 +1,15 @@ +{{ _("Sign in to Gratipay") }} + +[---] text/html +{{ _( "Click the button below to sign in to Gratipay. " + "This link will expire in 1 hour and can only be used once.") }} +
+
+{{ _("Sign in to Gratipay") }} + +[---] text/plain + +{{ _( "Click the link below to sign in to Gratipay. " + "This link will expire in 1 hour and can only be used once.") }} + +{{ signin_link }} diff --git a/tests/py/test_www_email_auth.py b/tests/py/test_www_email_auth.py new file mode 100644 index 0000000000..ff5802cada --- /dev/null +++ b/tests/py/test_www_email_auth.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function, unicode_literals + +import json + +from gratipay.testing import Harness +from gratipay.testing.email import QueuedEmailHarness + + +class TestSendLink(Harness): + def test_returns_json(self): + self.make_participant('alice', email_address='alice@gratipay.com') + response = self.client.POST('/auth/email/send_link.json', {'email_address': 'alice@gratipay.com'}) + + message = json.loads(response.body)['message'] + assert message == "We've sent you a link to sign in. Please check your inbox." + + def test_only_allows_post(self): + response = self.client.GxT('/auth/email/send_link.json') + + assert response.code == 405 + + def test_400_for_no_email_address_parameter(self): + response = self.client.PxST('/auth/email/send_link.json') + + assert response.code == 400 + + def test_400_for_invalid_email(self): + response = self.client.PxST('/auth/email/send_link.json', {'email_address': 'dummy@gratipay.com'}) + + # TODO: Change this when signup links are supported + + assert response.code == 400 + +class TestSendLinkEmail(QueuedEmailHarness): + def test_sends_email(self): + self.make_participant('alice', email_address='alice@gratipay.com') + self.client.POST('/auth/email/send_link.json', {'email_address': 'alice@gratipay.com'}) + + assert self.get_last_email()['to'] == 'alice ' + assert 'Click the link below to sign in to Gratipay' in self.get_last_email()['body_text'] + assert 'Click the button below to sign in to Gratipay' in self.get_last_email()['body_html'] diff --git a/www/auth/email/send_link.json.spt b/www/auth/email/send_link.json.spt new file mode 100644 index 0000000000..8ea1740a11 --- /dev/null +++ b/www/auth/email/send_link.json.spt @@ -0,0 +1,39 @@ +from aspen import Response + +from gratipay.models.participant import Participant +from gratipay.security.authentication.email import create_signin_nonce +from gratipay.utils import encode_for_querystring + +[---] + +request.allow("POST") + +if "email_address" not in request.body: + raise Response(400, "no 'email_address' in body") + +email_address = request.body["email_address"] + +participant = Participant.from_email(email_address) + +if participant: + nonce = create_signin_nonce(website.db, email_address) + + # TODO: Catch throttled! + + encoded_email = encode_for_querystring(email_address) + + signin_link = "%s/auth/email/verify.html?nonce=%s&email=%s" % (website.base_url, nonce, encoded_email) + website.app.email_queue.put( participant + , "signin_link" + , _user_initiated=True + , include_unsubscribe=False + , email=email_address + , signin_link=signin_link + ) + message = _("We've sent you a link to sign in. Please check your inbox.") +else: + # TODO: Create sign-up link! + raise Response(400, "no participant exists by this address") + +[---] application/json via json_dump +{"message": message}