def test_email_login_bad_email(self): data = {'log-in.id': '*****@*****.**'} r = self.client.POST('/sign-in', data, raise_immediately=False) assert r.code != 302 assert SESSION not in r.headers.cookie Participant.dequeue_emails() assert not self.get_emails()
def test_emails_are_not_sent_went_database_is_read_only(self): larry = self.make_participant('larry') self.queue_email(larry, 'team_invite', team='team', team_url='fake_url', inviter='bob') with postgres_readonly(self.db): Participant.dequeue_emails() assert self.mailer.call_count == 0 assert self.db.one("SELECT email_status FROM notifications") == 'queued'
def test_dequeueing_an_email_without_address_just_skips_it(self): larry = self.make_participant('larry') self.queue_email(larry, 'team_invite', team='team', team_url='fake_url', inviter='bob') Participant.dequeue_emails() assert self.mailer.call_count == 0 assert self.db.one("SELECT email_sent FROM notifications") is False
def test_participant_with_long_email_address_can_receive_messages(self): email = 'a' * 200 + '@example.org' fred = self.make_participant(None, email=email) fred.insert_identity({'name': "You don't need to know my legal name"}) fred.notify('team_invite', team='team', team_url='fake_url', inviter='bob') Participant.dequeue_emails() assert self.db.one("SELECT email_status FROM notifications") == 'sent'
def test_sign_in(self): r = self.client.PxST('/sign-in', good_data) assert r.code == 302, r.text assert SESSION in r.headers.cookie Participant.dequeue_emails() assert self.get_last_email() p = Participant.from_username(good_data['sign-in.username']) assert p.avatar_url
def test_dequeueing_an_email_without_address_just_skips_it(self): larry = self.make_participant('larry') larry.queue_email("verification") assert self.db.one("SELECT spt_name FROM email_queue") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 0 assert self.db.one("SELECT spt_name FROM email_queue") is None
def test_email_login_team_account(self): email = '*****@*****.**' self.make_participant('team', email=email, kind='group') data = {'log-in.id': email} r = self.client.POST('/log-in', data, raise_immediately=False) assert SESSION not in r.headers.cookie Participant.dequeue_emails() assert not self.get_emails()
def test_dequeueing_an_email_without_address_just_skips_it(self): larry = self.make_participant('larry') self.queue_email(larry, "verification") assert self.db.one("SELECT event FROM notifications") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 0 assert self.db.one("SELECT event FROM notifications") is None
def test_can_dequeue_an_email(self): larry = self.make_participant('larry', email='*****@*****.**') self.queue_email(larry, 'team_invite', team='team', team_url='fake_url', inviter='bob') Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'larry <*****@*****.**>' assert self.db.one("SELECT email_sent FROM notifications") is True
def test_participant_with_long_nonascii_name_can_receive_emails(self): fred = self.make_participant(None, email='*****@*****.**') fred.insert_identity( {'name': "Frédéric d'Arundel d'Esquincourt de Condé"}) fred.notify('team_invite', team='team', team_url='fake_url', inviter='bob') Participant.dequeue_emails() assert self.db.one("SELECT email_status FROM notifications") == 'sent'
def test_can_dequeue_an_email(self): larry = self.make_participant('larry', email='*****@*****.**') larry.queue_email("verification", link='https://example.com/larry') assert self.db.one("SELECT spt_name FROM email_queue") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'larry <*****@*****.**>' expected = "connect larry" assert expected in last_email['text'] assert self.db.one("SELECT spt_name FROM email_queue") is None
def test_sign_in(self): r = self.sign_in(HTTP_ACCEPT_LANGUAGE='fr') assert r.code == 302, r.text assert SESSION in r.headers.cookie # Check that an email was sent, in the user's preferred language Participant.dequeue_emails() last_email = self.get_last_email() username = good_data['sign-in.username'] expected_subject = 'Lier à %s sur Liberapay ?' % username assert last_email['subject'] == expected_subject # Check that the new user has an avatar p = Participant.from_username(username) assert p.avatar_url
def test_can_dequeue_an_email(self): larry = self.make_participant('larry', email='*****@*****.**') larry.queue_email("verification", link='https://example.com/larry') assert self.db.one( "SELECT spt_name FROM email_queue") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'larry <*****@*****.**>' assert last_email[ 'subject'] == "Email address verification - Liberapay" assert self.db.one("SELECT spt_name FROM email_queue") is None
def test_sign_in(self): fake_msg = Message('Email address verification - Liberapay', 'Vous avez du pain ?') LOCALES['fr'].catalog[fake_msg.id].string = fake_msg.string r = self.sign_in(HTTP_ACCEPT_LANGUAGE='fr') assert r.code == 302, r.text assert SESSION in r.headers.cookie # Check that an email was sent, in the user's preferred language Participant.dequeue_emails() last_email = self.get_last_email() username = good_data['sign-in.username'] assert last_email['subject'] == fake_msg.string # Check that the new user has an avatar p = Participant.from_username(username) assert p.avatar_url
def test_take_over_sends_notifications_to_patrons(self): dan_twitter = self.make_elsewhere('twitter', 1, 'dan') self.alice.set_tip_to(self.dan, '100') # Alice shouldn't receive an email. self.bob.set_tip_to(dan_twitter, '100') # Bob should receive an email. self.dan.take_over(dan_twitter, have_confirmation=True) Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'bob <*****@*****.**>' assert "to dan" in last_email['text'] assert "Change your email settings" in last_email['text']
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice') alice.add_email(email) alice.close(None) data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one( "SELECT * FROM user_secrets WHERE participant = %s", (alice.id, )) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s' % ( alice.id, session.id, session.secret) assert qs in last_email['text'] r = self.client.GxT('/alice/?foo=bar&' + qs) assert r.code == 302 assert r.headers[b'Location'] == b'http://localhost/alice/?foo=bar' # ↑ checks that original path and query are preserved old_secret = self.db.one( """ SELECT secret FROM user_secrets WHERE participant = %s AND id = %s AND secret = %s """, (alice.id, session.id, session.secret)) assert old_secret is None # ↑ this means that the link is only valid once # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=r.headers.cookie, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate(alice.id, 0, password) assert alice2 and alice2 == alice
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice') alice.add_email(email) alice.close(None) data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one("SELECT * FROM user_secrets WHERE participant = %s", (alice.id,)) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s' % ( alice.id, session.id, session.secret ) assert qs in last_email['text'] r = self.client.GxT('/alice/?foo=bar&' + qs) assert r.code == 302 assert r.headers[b'Location'] == b'http://localhost/alice/?foo=bar' # ↑ checks that original path and query are preserved old_secret = self.db.one(""" SELECT secret FROM user_secrets WHERE participant = %s AND id = %s AND secret = %s """, (alice.id, session.id, session.secret)) assert old_secret is None # ↑ this means that the link is only valid once # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=r.headers.cookie, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate(alice.id, 0, password) assert alice2 and alice2 == alice
def test_patrons_are_notified_after_pledgee_joins(self): self.bob = self.make_participant('bob', email='*****@*****.**') self.dan = self.make_participant('dan', email='*****@*****.**') self.alice = self.make_participant('alice', email='*****@*****.**') dan_twitter = self.make_elsewhere('twitter', 1, 'dan') self.alice.set_tip_to(self.dan, EUR('100')) # Alice shouldn't receive an email. self.bob.set_tip_to(dan_twitter, EUR('100')) # Bob should receive an email. self.dan.take_over(dan_twitter, have_confirmation=True) # dan hasn't connected any payment account yet, so there shouldn't be a notification Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 0 # add a payment account and check again self.add_payment_account(self.dan, 'stripe') Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'bob <*****@*****.**>' assert "to dan" in last_email['text'] assert "Change your email settings" in last_email['text'] # check that the notification isn't sent again self.mailer.reset_mock() Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 0
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice') alice.add_email(email) alice.close(None) data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) alice = alice.refetch() assert alice.session_token not in r.headers.raw.decode('ascii') assert alice.session_token not in r.body.decode('utf8') Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' assert 'log-in.token='+alice.session_token in last_email['text'] url = '/alice/?foo=bar&log-in.id=%s&log-in.token=%s' r = self.client.GxT(url % (alice.id, alice.session_token)) alice2 = alice.refetch() assert alice2.session_token != alice.session_token # ↑ this means that the link is only valid once assert r.code == 302 assert r.headers[b'Location'] == b'http://localhost/alice/?foo=bar' # ↑ checks that original path and query are preserved # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=r.headers.cookie, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate('id', 'password', alice.id, password) assert alice2 and alice2 == alice
def test_email_login_with_old_unverified_address(self): email = '*****@*****.**' alice = self.make_participant('alice', email=None) alice.add_email(email) Participant.dequeue_emails() self.db.run("UPDATE emails SET nonce = null") # Initiate email log-in data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one( "SELECT * FROM user_secrets WHERE participant = %s", (alice.id, )) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') # Check the email message Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' email_row = alice.get_email(email) assert email_row.verified is None assert email_row.nonce qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s&email.id=%s&email.nonce=%s' % ( alice.id, session.id, session.secret, email_row.id, email_row.nonce) assert qs in last_email['text'] # Log in r = self.client.GxT('/alice/?' + qs) assert r.code == 302 assert r.headers[b'Location'].startswith(b'http://localhost/alice/') # Check that the email address is now verified email_row = alice.get_email(email) assert email_row.verified alice = alice.refetch() assert alice.email == email
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice') alice.add_email(email) alice.close(None) data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) alice = alice.refetch() assert alice.session_token not in r.headers.raw.decode('ascii') assert alice.session_token not in r.body.decode('utf8') Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' assert 'log-in.token=' + alice.session_token in last_email['text'] url = '/alice/?foo=bar&log-in.id=%s&log-in.token=%s' r = self.client.GxT(url % (alice.id, alice.session_token)) alice2 = alice.refetch() assert alice2.session_token != alice.session_token # ↑ this means that the link is only valid once assert r.code == 302 assert r.headers[b'Location'] == b'http://localhost/alice/?foo=bar' # ↑ checks that original path and query are preserved # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=r.headers.cookie, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate('id', 'password', alice.id, password) assert alice2 and alice2 == alice
def test_patrons_are_charged_after_pledgee_joins(self): bob = self.make_participant('bob', email='*****@*****.**') dan = self.make_participant('dan', email='*****@*****.**') alice = self.make_participant('alice', email='*****@*****.**') dan_twitter = self.make_elsewhere('twitter', 1, 'dan') alice.set_tip_to(dan, EUR('100'), renewal_mode=2) bob.set_tip_to(dan_twitter, EUR('100'), renewal_mode=2) dan.take_over(dan_twitter, have_confirmation=True) # dan hasn't connected any payment account yet, so nothing should happen Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 0 # add a payment account and check again, but it's still too early self.add_payment_account(dan, 'stripe') Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 0 # simulate skipping one day ahead, now there should be a notification # and a scheduled payin self.db.run("UPDATE events SET ts = ts - interval '24 hours'") Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0] == 'bob <*****@*****.**>' assert last_email[ 'subject'] == "dan from Twitter has joined Liberapay!" scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins WHERE payer = %s", (bob.id, )) assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('400.00') assert scheduled_payins[0].automatic is True # check that the notification isn't sent again self.mailer.reset_mock() Participant.notify_patrons() Participant.dequeue_emails() assert self.mailer.call_count == 0
def test_sign_in(self): r = self.client.PxST('/sign-in', good_data) assert r.code == 302, r.text assert SESSION in r.headers.cookie Participant.dequeue_emails() assert self.get_last_email()
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice') alice.add_email(email) alice.close(None) # Sanity checks email_row = alice.get_email(email) assert email_row.verified is None assert alice.email is None # Initiate email log-in data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one( "SELECT * FROM user_secrets WHERE participant = %s", (alice.id, )) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') # Check the email message Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s&email.id=%s&email.nonce=%s' % ( alice.id, session.id, session.secret, email_row.id, email_row.nonce) assert qs in last_email['text'] # Log in r = self.client.GxT('/alice/?foo=bar&' + qs) assert r.code == 302 assert r.headers[b'Location'] == b'http://localhost/alice/?foo=bar' # ↑ checks that original path and query are preserved old_secret = self.db.one( """ SELECT secret FROM user_secrets WHERE participant = %s AND id = %s AND secret = %s """, (alice.id, session.id, session.secret)) assert old_secret is None # ↑ this means that the link is only valid once # Check that the email address is now verified email_row = alice.get_email(email) assert email_row.verified alice = alice.refetch() assert alice.email == email # Check what happens if the user clicks the login link a second time cookies = r.headers.cookie r = self.client.GxT('/alice/?foo=bar&' + qs, cookies=cookies) assert r.code == 400 assert " already logged in," in r.text, r.text # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=cookies, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate(alice.id, 0, password) assert alice2 and alice2 == alice
def get_emails(self): Participant.dequeue_emails() return [a[1] for a in self.mailer.call_args_list]
def get_emails(self): Participant.dequeue_emails() emails = [a[1] for a in self.mailer.call_args_list] self.mailer.reset_mock() return emails
def test_email_login(self): email = '*****@*****.**' alice = self.make_participant('alice', email=None) alice.add_email(email) alice.close(None) # Sanity checks email_row = alice.get_email(email) assert email_row.verified is None assert alice.email is None # Initiate email log-in data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one( "SELECT * FROM user_secrets WHERE participant = %s", (alice.id, )) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') # Check the email message Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s&email.id=%s&email.nonce=%s' % ( alice.id, session.id, session.secret, email_row.id, email_row.nonce) assert qs in last_email['text'] # Attempt to use the URL in a new browser session (no anti-CSRF cookie yet) r = self.client.GxT('/alice/?foo=bar&' + qs, csrf_token=None) assert r.code == 200 refresh_qs = '?foo=bar&' + qs + '&cookie_sent=true' assert r.headers[b'Refresh'] == b"0;url=" + refresh_qs.encode() assert CSRF_TOKEN in r.headers.cookie # Follow the redirect, still without cookies r = self.client.GxT('/alice/' + refresh_qs, csrf_token=None) assert r.code == 403, r.text assert "Please make sure your browser is configured to allow cookies" in r.text # Log in csrf_token = '_ThisIsAThirtyTwoBytesLongToken_' confirmation_token = b64encode_s( blake2b( session.secret.encode(), key=csrf_token.encode(), digest_size=48, ).digest()) r = self.client.GxT('/alice/' + refresh_qs, csrf_token=csrf_token) assert r.code == 200 assert SESSION not in r.headers.cookie assert confirmation_token in r.text r = self.client.GxT( '/alice/' + refresh_qs + '&log-in.confirmation=' + confirmation_token, csrf_token=csrf_token, ) assert r.code == 302 assert SESSION in r.headers.cookie assert r.headers[b'Location'].startswith( b'http://localhost/alice/?foo=bar&success=') # ↑ checks that original path and query are preserved old_secret = self.db.one( """ SELECT secret FROM user_secrets WHERE participant = %s AND id = %s AND secret = %s """, (alice.id, session.id, session.secret)) assert old_secret is None # ↑ this means that the link is only valid once # Check that the email address is now verified email_row = alice.get_email(email) assert email_row.verified alice = alice.refetch() assert alice.email == email # Check what happens if the user clicks the login link a second time cookies = r.headers.cookie r = self.client.GxT('/alice/?foo=bar&' + qs, cookies=cookies) assert r.code == 400 assert " already logged in," in r.text, r.text # Check that we can change our password password = '******' r = self.client.POST( '/alice/settings/edit', {'new-password': password}, cookies=cookies, raise_immediately=False, ) assert r.code == 302 alice2 = Participant.authenticate(alice.id, 0, password) assert alice2 and alice2 == alice
def test_email_login_with_old_unverified_address(self): email = '*****@*****.**' alice = self.make_participant('alice', email=None) alice.add_email(email) Participant.dequeue_emails() self.db.run("UPDATE emails SET nonce = null") # Initiate email log-in data = {'log-in.id': email.upper()} r = self.client.POST('/', data, raise_immediately=False) session = self.db.one( "SELECT * FROM user_secrets WHERE participant = %s", (alice.id, )) assert session.secret not in r.headers.raw.decode('ascii') assert session.secret not in r.body.decode('utf8') # Check the email message Participant.dequeue_emails() last_email = self.get_last_email() assert last_email and last_email['subject'] == 'Log in to Liberapay' email_row = alice.get_email(email) assert email_row.verified is None assert email_row.nonce qs = 'log-in.id=%i&log-in.key=%i&log-in.token=%s&email.id=%s&email.nonce=%s' % ( alice.id, session.id, session.secret, email_row.id, email_row.nonce) assert qs in last_email['text'] # Try to log in without a confirmation code csrf_token = '_ThisIsAThirtyTwoBytesLongToken_' confirmation_token = b64encode_s( blake2b( session.secret.encode(), key=csrf_token.encode(), digest_size=48, ).digest()) r = self.client.GxT('/alice/?' + qs, csrf_token=csrf_token) assert r.code == 200 assert SESSION not in r.headers.cookie assert confirmation_token in r.text # Try to log in with an incorrect confirmation code r = self.client.GxT( '/alice/?' + qs + '&log-in.confirmation=' + ('~' * 64), csrf_token=csrf_token, ) assert r.code == 400 assert SESSION not in r.headers.cookie assert confirmation_token not in r.text # Log in with the correct confirmation code r = self.client.GxT( '/alice/?' + qs + '&log-in.confirmation=' + confirmation_token, csrf_token=csrf_token, ) assert r.code == 302 assert SESSION in r.headers.cookie assert r.headers[b'Location'].startswith(b'http://localhost/alice/') # Check that the email address is now verified email_row = alice.get_email(email) assert email_row.verified alice = alice.refetch() assert alice.email == email