Пример #1
0
    def test_disavow_email(self):
        self.client.PxST(
            '/sign-up', {
                'sign-in.email': '*****@*****.**',
                'sign-in.username': '******',
                'sign-in.currency': 'USD',
            })
        bob = Participant.from_username('bob')
        email = bob.get_email('*****@*****.**')
        qs = '?email.id=%s&email.nonce=%s' % (email.id, email.nonce)
        url = '/bob/emails/disavow' + qs
        verification_email = self.get_last_email()
        assert url in verification_email['text']

        # Test the disavowal URL without cookies
        r = self.client.GET(url, csrf_token=None, raise_immediately=False)
        assert r.code == 200
        refresh_qs = qs + '&cookie_sent=true'
        assert r.headers[b"Refresh"] == b'0;url=' + refresh_qs.encode()
        assert CSRF_TOKEN in r.headers.cookie
        email = bob.get_email(email.address)
        assert email.disavowed is None
        assert email.disavowed_time is None
        assert email.verified is None
        assert email.verified_time is None

        # Test the disavowal URL with cookies
        r = self.client.GET(url)
        assert r.code == 200
        email = bob.get_email(email.address)
        assert email.disavowed is True
        assert email.disavowed_time is not None
        assert email.verified is None
        assert email.verified_time is None
        assert email.nonce

        # Check idempotency
        r = self.client.GET(url)
        assert r.code == 200

        # Check that resending the verification email isn't allowed
        r = self.client.POST(
            '/bob/emails/',
            {'resend': email.address},
            auth_as=bob,
            raise_immediately=False,
        )
        assert r.code == 400, r.text

        # Test adding the address to the blacklist
        r = self.client.POST(url, {'action': 'add_to_blacklist'})
        assert r.code == 200, r.text
        with self.assertRaises(EmailAddressIsBlacklisted):
            check_email_blacklist(email.address)

        # and removing it
        r = self.client.POST(url, {'action': 'remove_from_blacklist'})
        assert r.code == 200, r.text
        assert check_email_blacklist(email.address) is None
Пример #2
0
 def test_blacklisting_email_domain(self):
     admin = self.make_participant('admin', privileges=1)
     check_email_blacklist('*****@*****.**')
     r = self.client.GET('/admin/email-domains?domain=example.com', auth_as=admin)
     assert r.code == 200, r.text
     r = self.client.PxST(
         '/admin/email-domains?domain=example.com',
         {'action': 'add_to_blacklist', 'reason': 'bounce'},
         auth_as=admin,
     )
     assert r.code == 302, r.text
     r = self.client.GET('/admin/email-domains?domain=example.com', auth_as=admin)
     assert r.code == 200, r.text
     with self.assertRaises(EmailDomainIsBlacklisted):
         check_email_blacklist('*****@*****.**')
Пример #3
0
    def test_disavow_email(self):
        self.client.PxST(
            '/sign-up', {
                'sign-in.email': '*****@*****.**',
                'sign-in.username': '******',
                'sign-in.currency': 'USD',
            })
        bob = Participant.from_username('bob')
        email = bob.get_email('*****@*****.**')
        url = '/bob/emails/disavow?email.id=%s&email.nonce=%s' % (email.id,
                                                                  email.nonce)
        verification_email = self.get_last_email()
        assert url in verification_email['text']
        r = self.client.GET(url)
        assert r.code == 200
        email = bob.get_email(email.address)
        assert email.disavowed is True
        assert email.disavowed_time is not None
        assert email.verified is None
        assert email.verified_time is None
        assert email.nonce

        # Check idempotency
        r = self.client.GET(url)
        assert r.code == 200

        # Check that resending the verification email isn't allowed
        r = self.client.POST(
            '/bob/emails/modify.json',
            {'resend': email.address},
            auth_as=bob,
            raise_immediately=False,
        )
        assert r.code == 400, r.text

        # Test adding the address to the blacklist
        r = self.client.POST(url, {'action': 'add_to_blacklist'})
        assert r.code == 200, r.text
        with self.assertRaises(EmailAddressIsBlacklisted):
            check_email_blacklist(email.address)

        # and removing it
        r = self.client.POST(url, {'action': 'remove_from_blacklist'})
        assert r.code == 200, r.text
        assert check_email_blacklist(email.address) is None
Пример #4
0
def sign_in_with_form_data(body, state):
    p = None
    _, website = state['_'], state['website']

    if body.get('log-in.id'):
        request = state['request']
        src_addr, src_country = request.source, request.country
        website.db.hit_rate_limit('log-in.ip-addr', str(src_addr), TooManyLogInAttempts)
        website.db.hit_rate_limit('log-in.country', src_country, TooManyLogInAttempts)
        id = body['log-in.id'].strip()
        password = body.pop('log-in.password', None)
        k = 'email' if '@' in id else 'username'
        if password:
            id = Participant.get_id_for(k, id)
            p = Participant.authenticate(id, 0, password)
            if not p:
                state['log-in.error'] = _("Bad username or password.")
            else:
                try:
                    p.check_password(password, context='login')
                except Exception as e:
                    website.tell_sentry(e, state)
        elif k == 'username':
            state['log-in.error'] = _("\"{0}\" is not a valid email address.", id)
            return
        else:
            email = id
            p = Participant.from_email(email)
            if p and p.kind == 'group':
                state['log-in.error'] = _(
                    "{0} is linked to a team account. It's not possible to log in as a team.",
                    email
                )
            elif p:
                if not p.get_email(email).verified:
                    website.db.hit_rate_limit('log-in.email.not-verified', email, TooManyLoginEmails)
                website.db.hit_rate_limit('log-in.email', p.id, TooManyLoginEmails)
                email_row = p.get_email(email)
                p.send_email('login_link', email_row, link_validity=SESSION_TIMEOUT)
                state['log-in.email-sent-to'] = email
                raise LoginRequired
            else:
                state['log-in.error'] = _(
                    "We didn't find any account whose primary email address is {0}.",
                    email
                )
            p = None

    elif 'sign-in.email' in body:
        response = state['response']
        # Check the submitted data
        kind = body.pop('sign-in.kind', 'individual')
        if kind not in ('individual', 'organization'):
            raise response.invalid_input(kind, 'sign-in.kind', 'body')
        email = body.pop('sign-in.email')
        if not email:
            raise response.error(400, 'email is required')
        email = normalize_email_address(email)
        check_email_blacklist(email)
        currency = body.pop('sign-in.currency', 'EUR')
        if currency not in CURRENCIES:
            raise response.invalid_input(currency, 'sign-in.currency', 'body')
        password = body.pop('sign-in.password', None)
        if password:
            l = len(password)
            if l < PASSWORD_MIN_SIZE or l > PASSWORD_MAX_SIZE:
                raise BadPasswordSize
        username = body.pop('sign-in.username', None)
        if username:
            username = username.strip()
            Participant.check_username(username)
        session_token = body.pop('sign-in.token', '')
        if session_token:
            Participant.check_session_token(session_token)
        # Check for an existing account
        existing_account = website.db.one("""
            SELECT p
              FROM emails e
              JOIN participants p ON p.id = e.participant
             WHERE lower(e.address) = lower(%s)
               AND ( e.verified IS TRUE OR
                     e.added_time > (current_timestamp - interval '1 day') OR
                     p.email IS NULL )
          ORDER BY p.join_time DESC
             LIMIT 1
        """, (email,))
        if existing_account:
            session = website.db.one("""
                SELECT id, secret, mtime
                  FROM user_secrets
                 WHERE participant = %s
                   AND id = 1
                   AND mtime < (%s + interval '6 hours')
                   AND mtime > (current_timestamp - interval '6 hours')
            """, (existing_account.id, existing_account.join_time))
            if session and constant_time_compare(session_token, session.secret):
                p = existing_account
                p.authenticated = True
                p.sign_in(response.headers.cookie, session=session)
                return p
            else:
                raise EmailAlreadyTaken(email)
        username_taken = website.db.one("""
            SELECT count(*)
              FROM participants p
             WHERE p.username = %s
        """, (username,))
        if username_taken:
            raise UsernameAlreadyTaken(username)
        # Rate limit
        request = state['request']
        src_addr, src_country = request.source, request.country
        website.db.hit_rate_limit('sign-up.ip-addr', str(src_addr), TooManySignUps)
        website.db.hit_rate_limit('sign-up.ip-net', get_ip_net(src_addr), TooManySignUps)
        website.db.hit_rate_limit('sign-up.country', src_country, TooManySignUps)
        website.db.hit_rate_limit('sign-up.ip-version', src_addr.version, TooManySignUps)
        # Okay, create the account
        with website.db.get_cursor() as c:
            p = Participant.make_active(kind, currency, username, cursor=c)
            p.set_email_lang(state['locale'].language, cursor=c)
            p.add_email(email, cursor=c)
        if password:
            p.update_password(password)
            p.check_password(password, context='login')
        p.authenticated = True
        p.sign_in(response.headers.cookie, token=session_token)

    return p
Пример #5
0
def sign_in_with_form_data(body, state):
    p = None
    _, website = state['_'], state['website']

    if body.get('log-in.id'):
        request = state['request']
        src_addr, src_country = request.source, request.country
        website.db.hit_rate_limit('log-in.ip-addr', str(src_addr), TooManyLogInAttempts)
        website.db.hit_rate_limit('log-in.country', src_country, TooManyLogInAttempts)
        id = body.pop('log-in.id').strip()
        password = body.pop('log-in.password', None)
        k = 'email' if '@' in id else 'username'
        if password:
            id = Participant.get_id_for(k, id)
            p = Participant.authenticate(id, 0, password)
            if not p:
                state['log-in.error'] = _("Bad username or password.")
            else:
                try:
                    p.check_password(password, context='login')
                except Exception as e:
                    website.tell_sentry(e, state)

        elif k == 'username':
            state['log-in.error'] = _("\"{0}\" is not a valid email address.", id)
            return
        else:
            email = id
            p = Participant.from_email(email)
            if p and p.kind == 'group':
                state['log-in.error'] = _(
                    "{0} is linked to a team account. It's not possible to log in as a team.",
                    email
                )
            elif p:
                if not p.get_email(email).verified:
                    website.db.hit_rate_limit('log-in.email.not-verified', email, TooManyLoginEmails)
                website.db.hit_rate_limit('log-in.email', p.id, TooManyLoginEmails)
                p.start_session()
                qs = [
                    ('log-in.id', p.id),
                    ('log-in.key', p.session.id),
                    ('log-in.token', p.session.secret)
                ]
                p.send_email(
                    'login_link',
                    email,
                    link=p.url('settings/', qs),
                    link_validity=SESSION_TIMEOUT,
                )
                state['log-in.email-sent-to'] = email
                raise LoginRequired
            else:
                state['log-in.error'] = _(
                    "We didn't find any account whose primary email address is {0}.",
                    email
                )
            p = None

    elif 'sign-in.email' in body:
        response = state['response']
        # Check the submitted data
        kind = body.pop('sign-in.kind', 'individual')
        if kind not in ('individual', 'organization'):
            raise response.invalid_input(kind, 'sign-in.kind', 'body')
        email = body.pop('sign-in.email')
        if not email:
            raise response.error(400, 'email is required')
        email = normalize_email_address(email)
        check_email_blacklist(email)
        currency = body.pop('sign-in.currency', 'EUR')
        if currency not in CURRENCIES:
            raise response.invalid_input(currency, 'sign-in.currency', 'body')
        password = body.pop('sign-in.password', None)
        if password:
            l = len(password)
            if l < PASSWORD_MIN_SIZE or l > PASSWORD_MAX_SIZE:
                raise BadPasswordSize
        username = body.pop('sign-in.username', None)
        if username:
            username = username.strip()
            Participant.check_username(username)
        session_token = body.pop('sign-in.token', '')
        if session_token:
            Participant.check_session_token(session_token)
        # Check for an existing account
        existing_account = website.db.one("""
            SELECT p, s.secret
              FROM emails e
              JOIN participants p ON p.id = e.participant
         LEFT JOIN user_secrets s ON s.participant = p.id
                                 AND s.id = 1
                                 AND s.mtime < (p.join_time + interval '6 hours')
                                 AND s.mtime > (current_timestamp - interval '6 hours')
             WHERE lower(e.address) = lower(%s)
               AND ( e.verified IS TRUE OR
                     e.added_time > (current_timestamp - interval '1 day') OR
                     s.secret IS NOT NULL OR
                     p.email IS NULL )
          ORDER BY p.join_time DESC
             LIMIT 1
        """, (email,))
        if existing_account:
            p, secret = existing_account
            if secret and constant_time_compare(session_token, secret):
                p.authenticated = True
                p.sign_in(response.headers.cookie, token=session_token)
                return p
            else:
                raise EmailAlreadyTaken(email)
        username_taken = website.db.one("""
            SELECT count(*)
              FROM participants p
             WHERE p.username = %s
        """, (username,))
        if username_taken:
            raise UsernameAlreadyTaken(username)
        # Rate limit
        request = state['request']
        src_addr, src_country = request.source, request.country
        website.db.hit_rate_limit('sign-up.ip-addr', str(src_addr), TooManySignUps)
        website.db.hit_rate_limit('sign-up.ip-net', get_ip_net(src_addr), TooManySignUps)
        website.db.hit_rate_limit('sign-up.country', src_country, TooManySignUps)
        website.db.hit_rate_limit('sign-up.ip-version', src_addr.version, TooManySignUps)
        # Okay, create the account
        with website.db.get_cursor() as c:
            p = Participant.make_active(kind, currency, username, cursor=c)
            p.set_email_lang(state['locale'].language, cursor=c)
            p.add_email(email, cursor=c)
        if password:
            p.update_password(password)
            p.check_password(password, context='login')
        p.authenticated = True
        p.sign_in(response.headers.cookie, token=session_token)

    return p