Beispiel #1
0
    def test_get_assertion_html_redirects_to_frontend(self):
        badgr_app = BadgrApp(cors='frontend.ui',
                             public_pages_redirect='http://frontend.ui/public')
        badgr_app.save()

        redirect_accepts = [
            {'HTTP_ACCEPT': 'application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5'},  # safari/chrome
            {'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'},  # firefox
            {'HTTP_ACCEPT': 'text/html, application/xhtml+xml, image/jxr, */*'},  # edge
        ]
        json_accepts = [
            {'HTTP_ACCEPT': '*/*'},  # curl
            {},  # no accept header
        ]

        with self.settings(BADGR_APP_ID=badgr_app.id):
            test_user = self.setup_user(authenticate=False)
            test_issuer = self.setup_issuer(owner=test_user)
            test_issuer.cached_badgrapp  # publish badgrapp to cache
            test_badgeclass = self.setup_badgeclass(issuer=test_issuer)
            assertion = test_badgeclass.issue(recipient_id='*****@*****.**')

            for headers in redirect_accepts:
                with self.assertNumQueries(0):
                    response = self.client.get('/public/assertions/{}'.format(assertion.entity_id), **headers)
                    self.assertEqual(response.status_code, 302)
                    self.assertEqual(response.get('Location'), 'http://frontend.ui/public/assertions/{}'.format(assertion.entity_id))

            for headers in json_accepts:
                with self.assertNumQueries(0):
                    response = self.client.get('/public/assertions/{}'.format(assertion.entity_id), **headers)
                    self.assertEqual(response.status_code, 200)
                    self.assertEqual(response.get('Content-Type'), "application/ld+json")
Beispiel #2
0
    def test_get_assertion_html_redirects_to_frontend(self):
        badgr_app = BadgrApp(cors='frontend.ui',
                             public_pages_redirect='http://frontend.ui/public')
        badgr_app.save()

        testcase_headers = [
            # browsers will send Accept: */* by default
            {
                'HTTP_ACCEPT': '*/*'
            },
        ]

        with self.settings(BADGR_APP_ID=badgr_app.id):
            test_user = self.setup_user(authenticate=False)
            test_issuer = self.setup_issuer(owner=test_user)
            test_issuer.cached_badgrapp  # publish badgrapp to cache
            test_badgeclass = self.setup_badgeclass(issuer=test_issuer)
            assertion = test_badgeclass.issue(
                recipient_id='*****@*****.**')

            for headers in testcase_headers:
                with self.assertNumQueries(0):
                    response = self.client.get(
                        '/public/assertions/{}'.format(assertion.entity_id),
                        **headers)
                    self.assertEqual(response.status_code, 302)
                    self.assertEqual(
                        response.get('Location'),
                        'http://frontend.ui/public/assertions/{}'.format(
                            assertion.entity_id))
Beispiel #3
0
class UserBadgeTests(BadgrTestCase):
    def setUp(self):
        super(UserBadgeTests, self).setUp()
        self.badgr_app = BadgrApp(cors='testserver',
                                  email_confirmation_redirect='http://testserver/login/',
                                  forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

    def create_badgeclass(self):
        with open(os.path.join(TOP_DIR, 'apps', 'issuer', 'testfiles', 'guinea_pig_testing_badge.png'), 'r') as fh:
            issuer = Issuer.objects.create(name='Issuer of Testing')
            badgeclass = BadgeClass.objects.create(
                issuer=issuer,
                name="Badge of Testing",
                image=SimpleUploadedFile(name='test_image.png', content=fh.read(), content_type='image/png')
            )
            return badgeclass

    #@unittest.skip('For debug speedup')
    def test_badge_awards_transferred_on_email_verification(self):
        first_user_email = '*****@*****.**'
        first_user = self.setup_user(email=first_user_email, authenticate=True)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        badgeclass = self.create_badgeclass()
        badgeclass.issue(recipient_id='*****@*****.**', allow_uppercase=True)
        badgeclass.issue(recipient_id='*****@*****.**', allow_uppercase=True)

        outbox_count = len(mail.outbox)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # Mark email as verified
            email = CachedEmailAddress.cached.get(email='*****@*****.**')
            self.assertEqual(len(mail.outbox), outbox_count+1)
            verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[-1].body).group("url")
            response = self.client.get(verify_url)
            self.assertEqual(response.status_code, 302)

        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertTrue(email.verified)

        self.assertTrue('*****@*****.**' in [e.email for e in email.cached_variants()])
        self.assertTrue('*****@*****.**' in [e.email for e in email.cached_variants()])
Beispiel #4
0
class UserBadgeTests(BadgrTestCase):
    def setUp(self):
        super(UserBadgeTests, self).setUp()
        self.badgr_app = BadgrApp(cors='testserver',
                                  email_confirmation_redirect='http://testserver/login/',
                                  forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

    def create_badgeclass(self):
        with open(os.path.join(TOP_DIR, 'apps', 'issuer', 'testfiles', 'guinea_pig_testing_badge.png'), 'r') as fh:
            issuer = Issuer.objects.create(name='Issuer of Testing')
            badgeclass = BadgeClass.objects.create(
                issuer=issuer,
                name="Badge of Testing",
                image=SimpleUploadedFile(name='test_image.png', content=fh.read(), content_type='image/png')
            )
            return badgeclass

    def test_badge_awards_transferred_on_email_verification(self):
        first_user_email = '*****@*****.**'
        first_user = self.setup_user(email=first_user_email, authenticate=True)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        badgeclass = self.create_badgeclass()
        badgeclass.issue(recipient_id='*****@*****.**', allow_uppercase=True)
        badgeclass.issue(recipient_id='*****@*****.**', allow_uppercase=True)

        outbox_count = len(mail.outbox)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # Mark email as verified
            email = CachedEmailAddress.cached.get(email='*****@*****.**')
            self.assertEqual(len(mail.outbox), outbox_count+1)
            verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[-1].body).group("url")
            response = self.client.get(verify_url)
            self.assertEqual(response.status_code, 302)

        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertTrue(email.verified)

        self.assertTrue('*****@*****.**' in [e.email for e in email.cached_variants()])
        self.assertTrue('*****@*****.**' in [e.email for e in email.cached_variants()])
Beispiel #5
0
    def test_user_signup_email_confirmation_redirect(self):
        from django.conf import settings
        http_origin = getattr(settings, 'HTTP_ORIGIN')
        badgr_app = BadgrApp(
            cors='frontend.ui',
            email_confirmation_redirect='http://frontend.ui/login/',
            forgot_password_redirect='http://frontend.ui/forgot-password/',
            is_default=True)
        badgr_app.save()

        post_data = {
            'first_name': 'Tester',
            'last_name': 'McSteve',
            'email': '*****@*****.**',
            'password': '******'
        }
        response = self.client.post('/v1/user/profile', post_data)
        self.assertEqual(response.status_code, 201)

        user = BadgeUser.objects.get(entity_id=response.data.get('slug'))

        self.assertEqual(len(mail.outbox), 1)
        url_match = re.search(
            r'{}(/v1/user/confirmemail.*)'.format(http_origin),
            mail.outbox[0].body)
        self.assertIsNotNone(url_match)
        confirm_url = url_match.group(1)

        expected_redirect_url = '{badgrapp_redirect}{first_name}?authToken={auth}&email={email}'.format(
            badgrapp_redirect=badgr_app.email_confirmation_redirect,
            first_name=post_data['first_name'],
            email=urllib.quote(post_data['email']),
            auth=user.auth_token)

        response = self.client.get(confirm_url, follow=False)
        self.assertEqual(response.status_code, 302)

        actual = urlparse.urlparse(response.get('location'))
        expected = urlparse.urlparse(expected_redirect_url)
        self.assertEqual(actual.netloc, expected.netloc)
        self.assertEqual(actual.scheme, expected.scheme)

        actual_query = urlparse.parse_qs(actual.query)
        expected_query = urlparse.parse_qs(expected.query)
        self.assertEqual(actual_query.get('email'),
                         expected_query.get('email'))
        self.assertIsNotNone(actual_query.get('authToken'))
    def test_user_signup_email_confirmation_redirect(self):
        from django.conf import settings
        http_origin = getattr(settings, 'HTTP_ORIGIN')
        badgr_app = BadgrApp(cors='frontend.ui',
                             email_confirmation_redirect='http://frontend.ui/login/',
                             forgot_password_redirect='http://frontend.ui/forgot-password/')
        badgr_app.save()

        with self.settings(BADGR_APP_ID=badgr_app.id):
            post_data = {
                'first_name': 'Tester',
                'last_name': 'McSteve',
                'email': '*****@*****.**',
                'password': '******'
            }
            response = self.client.post('/v1/user/profile', post_data)
            self.assertEqual(response.status_code, 201)

            user = BadgeUser.objects.get(entity_id=response.data.get('slug'))

            self.assertEqual(len(mail.outbox), 1)
            url_match = re.search(r'{}(/v1/user/confirmemail.*)'.format(http_origin), mail.outbox[0].body)
            self.assertIsNotNone(url_match)
            confirm_url = url_match.group(1)

            expected_redirect_url = '{badgrapp_redirect}{first_name}?authToken={auth}&email={email}'.format(
                badgrapp_redirect=badgr_app.email_confirmation_redirect,
                first_name=post_data['first_name'],
                email=urllib.quote(post_data['email']),
                auth=user.auth_token
            )

            response = self.client.get(confirm_url, follow=False)
            self.assertEqual(response.status_code, 302)

            actual = urlparse.urlparse(response.get('location'))
            expected = urlparse.urlparse(expected_redirect_url)
            self.assertEqual(actual.netloc, expected.netloc)
            self.assertEqual(actual.scheme, expected.scheme)

            actual_query = urlparse.parse_qs(actual.query)
            expected_query = urlparse.parse_qs(expected.query)
            self.assertEqual(actual_query.get('email'), expected_query.get('email'))
            self.assertIsNotNone(actual_query.get('authToken'))
Beispiel #7
0
class UserEmailTests(BadgrTestCase):
    def setUp(self):
        super(UserEmailTests, self).setUp()

        self.badgr_app = BadgrApp(
            cors='testserver',
            email_confirmation_redirect='http://testserver/login/',
            forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

        self.first_user_email = '*****@*****.**'
        self.first_user_email_secondary = '*****@*****.**'
        self.first_user = self.setup_user(email=self.first_user_email,
                                          authenticate=True)
        CachedEmailAddress.objects.create(
            user=self.first_user,
            email=self.first_user_email_secondary,
            verified=True)
        response = self.client.get('/v1/user/auth-token')
        self.assertEqual(response.status_code, 200)

    def test_user_register_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count + 1, len(response.data))

        # Mark email as verified
        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        email.verified = True
        email.save()

        # Can not add the same email twice
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)
        self.assertTrue("Could not register email address." in response.data)

    def test_user_can_verify_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count + 1, len(response.data))

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # Mark email as verified
            email = CachedEmailAddress.cached.get(
                email='*****@*****.**')
            self.assertEqual(len(mail.outbox), 1)
            verify_url = re.search("(?P<url>/v1/[^\s]+)",
                                   mail.outbox[0].body).group("url")
            response = self.client.get(verify_url)
            self.assertEqual(response.status_code, 302)

        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertTrue(email.verified)

    def test_user_cant_register_new_email_verified_by_other(self):
        second_user = self.setup_user(authenticate=False)
        existing_mail = CachedEmailAddress.objects.create(
            user=self.first_user,
            email='*****@*****.**',
            verified=True)

        response = self.client.get('/v1/user/emails')

        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        # Another user tries to add this email
        self.client.force_authenticate(user=second_user)
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)

        self.client.force_authenticate(user=self.first_user)
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count, len(response.data))

    def test_user_can_remove_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        not_primary = random.choice(
            filter(lambda e: e['verified'] and not e['primary'],
                   response.data))
        primary = random.choice(filter(lambda e: e['primary'], response.data))

        # cant remove primary email
        response = self.client.delete('/v1/user/emails/{}'.format(
            primary.get('id')))
        self.assertEqual(response.status_code, 400)
        response = self.client.get('/v1/user/emails/{}'.format(
            primary.get('id')))
        self.assertEqual(response.status_code, 200)

        # can remove a non-primary email
        response = self.client.delete('/v1/user/emails/{}'.format(
            not_primary.get('id')))
        self.assertEqual(response.status_code, 200)
        response = self.client.get('/v1/user/emails/{}'.format(
            not_primary.get('id')))
        self.assertEqual(response.status_code, 404)

    def test_user_can_make_email_primary(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        self.assertGreater(len(response.data), 0)

        not_primary = random.choice(
            filter(lambda e: e['verified'] and not e['primary'],
                   response.data))

        # set a non primary email to primary
        response = self.client.put(
            '/v1/user/emails/{}'.format(not_primary.get('id')),
            {'primary': True})
        self.assertEqual(response.status_code, 200)

        # confirm that the new email is primary and the others aren't
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        for email in response.data:
            if email['id'] == not_primary['id']:
                self.assertEqual(email['primary'], True)
            else:
                self.assertEqual(email['primary'], False)

    def test_user_can_resend_verification_email(self):
        # register a new un-verified email
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)
        self.assertEqual(len(mail.outbox), 1)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        not_verified = random.choice(
            filter(lambda e: not e['verified'], response.data))
        verified = random.choice(filter(lambda e: e['verified'],
                                        response.data))

        # dont resend verification email if already verified
        response = self.client.put(
            '/v1/user/emails/{}'.format(verified.get('id')), {'resend': True})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 1)

        # gets an email for an unverified email
        response = self.client.put(
            '/v1/user/emails/{}'.format(not_verified.get('id')),
            {'resend': True})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 2)

    def test_user_can_request_forgot_password(self):
        self.client.logout()

        # dont send recovery to unknown emails
        response = self.client.post('/v1/user/forgot-password', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 200,
                         "Does not leak information about account emails")
        self.assertEqual(len(mail.outbox), 0)

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # successfully send recovery email
            response = self.client.post('/v1/user/forgot-password',
                                        {'email': self.first_user_email})
            self.assertEqual(response.status_code, 200)
            # received email with recovery url
            self.assertEqual(len(mail.outbox), 1)
            matches = re.search(
                r'/v1/user/forgot-password\?token=([-0-9a-zA-Z]*)',
                mail.outbox[0].body)
            self.assertIsNotNone(matches)
            token = matches.group(1)
            new_password = '******'

            # able to use token received in email to reset password
            response = self.client.put('/v1/user/forgot-password', {
                'token': token,
                'password': new_password
            })
            self.assertEqual(response.status_code, 200)

            response = self.client.post('/api-auth/token', {
                'username': self.first_user.username,
                'password': new_password,
            })
            self.assertEqual(response.status_code, 200)

    def test_lower_variant_autocreated_on_new_email(self):
        first_email = CachedEmailAddress(email="*****@*****.**",
                                         user=BadgeUser.objects.first(),
                                         verified=True)
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        variants = EmailAddressVariant.objects.filter(
            canonical_email=first_email)

        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_new_variant_api(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(email="*****@*****.**",
                                         user=user,
                                         verified=True)
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        self.client.force_authenticate(user=user)
        response = self.client.post('/v1/user/emails',
                                    {'email': '*****@*****.**'})

        self.assertEqual(response.status_code, 400)
        self.assertTrue(
            'Matching address already exists. New case variant registered.' in
            response.data)

        variants = first_email.cached_variants()
        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_variants(self):
        user = self.setup_user(authenticate=False)
        first_email = CachedEmailAddress.objects.create(
            email="*****@*****.**", verified=True, user=user)
        self.assertIsNotNone(first_email.pk)

        first_variant_email = "*****@*****.**"
        second_variant_email = "*****@*****.**"

        first_variant = EmailAddressVariant(email=first_variant_email,
                                            canonical_email=first_email)
        first_variant.save()
        self.assertEqual(first_variant.canonical_email, first_email)

        second_variant = first_email.add_variant(second_variant_email)
        self.assertEqual(second_variant.canonical_email, first_email)

        self.assertEqual(len(first_email.emailaddressvariant_set.all()), 2)
        self.assertEqual(len(first_email.cached_variants()), 2)

    def test_user_can_create_variant_method(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(email="*****@*****.**",
                                         user=user,
                                         verified=True)
        first_email.save()
        first_email.add_variant("*****@*****.**")

        self.assertTrue(user.can_add_variant("*****@*****.**"))
        self.assertFalse(
            user.can_add_variant("*****@*****.**"))  # already exists
        self.assertFalse(
            user.can_add_variant("*****@*****.**"))  # is the original
        self.assertFalse(user.can_add_variant(
            "*****@*****.**"))  # not a match of original

    def test_can_create_variant_for_unconfirmed_email(self):
        user = BadgeUser.objects.first()
        new_email_address = "*****@*****.**"
        new_email = CachedEmailAddress.objects.create(email=new_email_address,
                                                      user=user)
        new_variant = EmailAddressVariant(email=new_email_address.upper(),
                                          canonical_email=new_email)

        new_variant.save()
        self.assertFalse(new_variant.verified)

        verified_emails = [e.email for e in user.emailaddress_set.filter(verified=True)] \
                          + [e.email for e in user.cached_email_variants() if e.verified]

        self.assertTrue(new_variant not in verified_emails)

    def cannot_link_variant_of_case_insensitive_nonmatch(self):
        first_email = CachedEmailAddress.objects.get(email="*****@*****.**")
        self.assertIsNotNone(first_email.pk)

        variant_email = "*****@*****.**"

        variant = EmailAddressVariant(email=variant_email,
                                      canonical_email=first_email)
        try:
            variant.save()
        except ValidationError as e:
            self.assertEqual(
                e.message,
                "New EmailAddressVariant does not match stored email address.")
        else:
            raise self.fail("ValidationError expected on nonmatch.")
Beispiel #8
0
class UserRecipientIdentifierTests(SetupIssuerHelper, BadgrTestCase):
    def setUp(self):
        super(UserRecipientIdentifierTests, self).setUp()

        self.badgr_app = BadgrApp(
            cors='testserver',
            email_confirmation_redirect='http://testserver/login/',
            forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

        self.first_user_email = '*****@*****.**'
        self.first_user_email_secondary = '*****@*****.**'
        self.first_user = self.setup_user(email=self.first_user_email,
                                          authenticate=True)
        CachedEmailAddress.objects.create(
            user=self.first_user,
            email=self.first_user_email_secondary,
            verified=True)
        response = self.client.get('/v1/user/auth-token')
        self.assertEqual(response.status_code, 200)

        self.issuer = self.setup_issuer(owner=self.first_user)
        self.badgeclass = self.setup_badgeclass(self.issuer)

    def test_two_users_can_have_same_identifier(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url)
        second_user_email = '*****@*****.**'
        second_user = self.setup_user(email=second_user_email,
                                      authenticate=True)
        second_user.userrecipientidentifier_set.create(identifier=url)

        self.assertGreater(
            UserRecipientIdentifier.objects.filter(identifier=url).count(), 1)

    def test_only_one_user_can_have_verified_identifier(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url,
                                                           verified=True)
        second_user_email = '*****@*****.**'
        second_user = self.setup_user(email=second_user_email,
                                      authenticate=True)
        second_identifier = second_user.userrecipientidentifier_set.create(
            identifier=url)

        with self.assertRaisesRegex(ValidationError,
                                    re.compile('identifier', re.I)):
            second_identifier.verified = True
            second_identifier.save()

    def test_url_format_validation(self):
        self.first_user.userrecipientidentifier_set.create(
            identifier='http://example.com')
        self.first_user.userrecipientidentifier_set.create(
            identifier='ftp://example.com')
        self.first_user.userrecipientidentifier_set.create(
            identifier='https://withpath.com/12345678')
        self.first_user.userrecipientidentifier_set.create(
            identifier='https://withhash.com/12345678/bar.html#fooey')

        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='http')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='(541) 342-8456')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(identifier='')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='12345678')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='*****@*****.**')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='customprotocol://example.com')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='http://singlepart')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                identifier='/relative/url')

    def test_phone_format_validation(self):
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier='3428456')
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier='5413428456')
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier='15413428456')
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier='+15413428456')

        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
                identifier='+1541342845612345')
        with self.assertRaisesRegex(ValidationError, 'valid'):
            self.first_user.userrecipientidentifier_set.create(
                type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
                identifier='(541) 342-8456')

    def test_verified_phone_included_in_all_recipient_identifiers(self):
        identifier = '3428456'
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier=identifier,
            verified=True)
        self.assertIn(identifier, self.first_user.all_recipient_identifiers)

    def test_verified_url_included_in_all_recipient_identifiers(self):
        identifier = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL,
            identifier=identifier,
            verified=True)
        self.assertIn(identifier, self.first_user.all_recipient_identifiers)

    def test_identifiers_serialized_to_correct_fields(self):
        url = 'http://example.com'
        phone = '+15413428456'
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL,
            identifier=url,
            verified=True)
        self.first_user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier=phone,
            verified=True)
        v1serialized = BadgeUserProfileSerializerV1(self.first_user).data
        v2serialized = BadgeUserSerializerV2(self.first_user).data['result'][0]

        self.assertIn(url, v1serialized['url'])
        self.assertIn(url, v2serialized['url'])
        self.assertIn(phone, v1serialized['telephone'])
        self.assertIn(phone, v2serialized['telephone'])

        self.assertNotIn(phone, v1serialized['url'])
        self.assertNotIn(phone, v2serialized['url'])
        self.assertNotIn(url, v1serialized['telephone'])
        self.assertNotIn(url, v2serialized['telephone'])

    def test_recipient_identity_serialized_to_correct_fields(self):
        user = self.setup_user(create_email_address=False)
        v2serialized = BadgeUserSerializerV2(user).data['result'][0]
        self.assertEqual(None, v2serialized['recipient'])

        url = 'http://example.com'
        phone = '+15413428456'
        user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL,
            identifier=url,
            verified=True)
        user.userrecipientidentifier_set.create(
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_TELEPHONE,
            identifier=phone,
            verified=True)
        v2serialized = BadgeUserSerializerV2(user).data['result'][0]
        self.assertIn(url, v2serialized['recipient']['identity'])
        self.assertIn('url', v2serialized['recipient']['type'])

        primary_email = '*****@*****.**'
        CachedEmailAddress.objects.create(user=user,
                                          email=primary_email,
                                          primary=True,
                                          verified=True)
        v2serialized = BadgeUserSerializerV2(user).data['result'][0]
        self.assertIn(primary_email, v2serialized['recipient']['identity'])
        self.assertIn('email', v2serialized['recipient']['type'])

    def test_verified_recipient_receives_assertion(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(
            identifier=url,
            verified=True,
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)
        self.assertEqual(len(self.first_user.cached_badgeinstances()), 1)

    def test_unverified_recipient_receives_no_assertion(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)
        self.assertEqual(len(self.first_user.cached_badgeinstances()), 0)

    def test_verified_recipient_v1_badges_endpoint(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(
            identifier=url,
            verified=True,
            type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)

        response = self.client.get('/v1/earner/badges')
        self.assertEqual(len(response.data), 1)

    def test_verified_recipient_v2_assertions_endpoint(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url,
                                                           verified=True)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)
        response = self.client.get('/v2/backpack/assertions')
        self.assertEqual(len(response.data['result']), 1)

    def test_unverified_recipient_v1_badges_endpoint(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)

        response = self.client.get('/v1/earner/badges')
        self.assertEqual(len(response.data), 0)

    def test_unverified_recipient_v2_assertions_endpoint(self):
        url = 'http://example.com'
        self.first_user.userrecipientidentifier_set.create(identifier=url)
        self.badgeclass.issue(
            recipient_id=url,
            recipient_type=UserRecipientIdentifier.IDENTIFIER_TYPE_URL)

        response = self.client.get('/v2/backpack/assertions')
        self.assertEqual(len(response.data['result']), 0)
Beispiel #9
0
class UserEmailTests(BadgrTestCase):
    def setUp(self):
        super(UserEmailTests, self).setUp()

        self.badgr_app = BadgrApp(cors='testserver',
                                  email_confirmation_redirect='http://testserver/login/',
                                  forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

        self.first_user_email = '*****@*****.**'
        self.first_user_email_secondary = '*****@*****.**'
        self.first_user = self.setup_user(email=self.first_user_email, authenticate=True)
        CachedEmailAddress.objects.create(user=self.first_user, email=self.first_user_email_secondary, verified=True)
        response = self.client.get('/v1/user/auth-token')
        self.assertEqual(response.status_code, 200)

    def test_user_register_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        # Mark email as verified
        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        email.verified = True
        email.save()

        # Can not add the same email twice
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)
        self.assertTrue("Could not register email address." in response.data)

    def test_user_can_verify_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # Mark email as verified
            email = CachedEmailAddress.cached.get(email='*****@*****.**')
            self.assertEqual(len(mail.outbox), 1)
            verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[0].body).group("url")
            response = self.client.get(verify_url)
            self.assertEqual(response.status_code, 302)

        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertTrue(email.verified)

    def test_user_cant_register_new_email_verified_by_other(self):
        second_user = self.setup_user(authenticate=False)
        existing_mail = CachedEmailAddress.objects.create(
            user=self.first_user, email='*****@*****.**', verified=True)

        response = self.client.get('/v1/user/emails')

        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        # Another user tries to add this email
        self.client.force_authenticate(user=second_user)
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)

        self.client.force_authenticate(user=self.first_user)
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count, len(response.data))

    def test_user_can_remove_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        not_primary = random.choice(filter(lambda e: e['verified'] and not e['primary'], response.data))
        primary = random.choice(filter(lambda e: e['primary'], response.data))

        # cant remove primary email
        response = self.client.delete('/v1/user/emails/{}'.format(primary.get('id')))
        self.assertEqual(response.status_code, 400)
        response = self.client.get('/v1/user/emails/{}'.format(primary.get('id')))
        self.assertEqual(response.status_code, 200)

        # can remove a non-primary email
        response = self.client.delete('/v1/user/emails/{}'.format(not_primary.get('id')))
        self.assertEqual(response.status_code, 200)
        response = self.client.get('/v1/user/emails/{}'.format(not_primary.get('id')))
        self.assertEqual(response.status_code, 404)

    def test_user_can_make_email_primary(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        self.assertGreater(len(response.data), 0)

        not_primary = random.choice(filter(lambda e: e['verified'] and not e['primary'], response.data))

        # set a non primary email to primary
        response = self.client.put('/v1/user/emails/{}'.format(not_primary.get('id')), {
            'primary': True
        })
        self.assertEqual(response.status_code, 200)

        # confirm that the new email is primary and the others aren't
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        for email in response.data:
            if email['id'] == not_primary['id']:
                self.assertEqual(email['primary'], True)
            else:
                self.assertEqual(email['primary'], False)

    def test_user_can_resend_verification_email(self):
        # register a new un-verified email
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)
        self.assertEqual(len(mail.outbox), 1)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        not_verified = random.choice(filter(lambda e: not e['verified'], response.data))
        verified = random.choice(filter(lambda e: e['verified'], response.data))

        # dont resend verification email if already verified
        response = self.client.put('/v1/user/emails/{}'.format(verified.get('id')), {
            'resend': True
        })
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 1)

        # gets an email for an unverified email
        response = self.client.put('/v1/user/emails/{}'.format(not_verified.get('id')), {
            'resend': True
        })
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 2)

    def test_user_can_request_forgot_password(self):
        self.client.logout()
        cache.clear()

        # dont send recovery to unknown emails
        response = self.client.post('/v1/user/forgot-password', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 200, "Does not leak information about account emails")
        self.assertEqual(len(mail.outbox), 0)

        with self.settings(BADGR_APP_ID=self.badgr_app.id):
            # successfully send recovery email
            response = self.client.post('/v1/user/forgot-password', {
                'email': self.first_user_email
            })

            backoff_key = backoff_cache_key(self.first_user_email, None)
            backoff_data = {'count': 6, 'until': timezone.now() + timezone.timedelta(seconds=60)}
            cache.set(backoff_key, backoff_data)
            self.assertEqual(cache.get(backoff_key), backoff_data)

            self.assertEqual(response.status_code, 200)
            # received email with recovery url
            self.assertEqual(len(mail.outbox), 1)
            matches = re.search(r'/v1/user/forgot-password\?token=([-0-9a-zA-Z]*)', mail.outbox[0].body)
            self.assertIsNotNone(matches)
            token = matches.group(1)
            new_password = '******'

            # able to use token received in email to reset password
            response = self.client.put('/v1/user/forgot-password', {
                'token': token,
                'password': new_password
            })
            self.assertEqual(response.status_code, 200)

            backoff_data = cache.get(backoff_key)
            self.assertIsNone(backoff_data)

            response = self.client.post('/api-auth/token', {
                'username': self.first_user.username,
                'password': new_password,
            })
            self.assertEqual(response.status_code, 200)

    def test_lower_variant_autocreated_on_new_email(self):
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=BadgeUser.objects.first(), verified=True
        )
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        variants = EmailAddressVariant.objects.filter(canonical_email=first_email)

        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_new_variant_api(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=user, verified=True
        )
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        self.client.force_authenticate(user=user)
        response = self.client.post('/v1/user/emails', {'email': '*****@*****.**'})

        self.assertEqual(response.status_code, 400)
        self.assertTrue('Matching address already exists. New case variant registered.' in response.data)

        variants = first_email.cached_variants()
        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_variants(self):
        user = self.setup_user(authenticate=False)
        first_email = CachedEmailAddress.objects.create(email="*****@*****.**", verified=True, user=user)
        self.assertIsNotNone(first_email.pk)

        first_variant_email = "*****@*****.**"
        second_variant_email = "*****@*****.**"

        first_variant = EmailAddressVariant(email=first_variant_email, canonical_email=first_email)
        first_variant.save()
        self.assertEqual(first_variant.canonical_email, first_email)

        second_variant = first_email.add_variant(second_variant_email)
        self.assertEqual(second_variant.canonical_email, first_email)

        self.assertEqual(len(first_email.emailaddressvariant_set.all()), 2)
        self.assertEqual(len(first_email.cached_variants()), 2)

    def test_user_can_create_variant_method(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=user, verified=True
        )
        first_email.save()
        first_email.add_variant("*****@*****.**")

        self.assertTrue(user.can_add_variant("*****@*****.**"))
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # already exists
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # is the original
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # not a match of original

    def test_can_create_variant_for_unconfirmed_email(self):
        user = BadgeUser.objects.first()
        new_email_address = "*****@*****.**"
        new_email = CachedEmailAddress.objects.create(email=new_email_address, user=user)
        new_variant = EmailAddressVariant(email=new_email_address.upper(), canonical_email=new_email)

        new_variant.save()
        self.assertFalse(new_variant.verified)

        verified_emails = [e.email for e in user.emailaddress_set.filter(verified=True)] \
                          + [e.email for e in user.cached_email_variants() if e.verified]

        self.assertTrue(new_variant not in verified_emails)

    def cannot_link_variant_of_case_insensitive_nonmatch(self):
        first_email = CachedEmailAddress.objects.get(email="*****@*****.**")
        self.assertIsNotNone(first_email.pk)

        variant_email = "*****@*****.**"

        variant = EmailAddressVariant(email=variant_email, canonical_email=first_email)
        try:
            variant.save()
        except ValidationError as e:
            self.assertEqual(e.message, "New EmailAddressVariant does not match stored email address.")
        else:
            raise self.fail("ValidationError expected on nonmatch.")
Beispiel #10
0
class UserEmailTests(BadgrTestCase):
    def setUp(self):
        super(UserEmailTests, self).setUp()

        self.badgr_app = BadgrApp(cors='testserver',
                                  email_confirmation_redirect='http://testserver/login/',
                                  forgot_password_redirect='http://testserver/reset-password/')
        self.badgr_app.save()

        self.first_user_email = '*****@*****.**'
        self.first_user_email_secondary = '*****@*****.**'
        self.first_user = self.setup_user(email=self.first_user_email, authenticate=True)
        CachedEmailAddress.objects.create(user=self.first_user, email=self.first_user_email_secondary, verified=True)
        response = self.client.get('/v1/user/auth-token')
        self.assertEqual(response.status_code, 200)

    def test_user_register_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        # Mark email as verified
        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        email.verified = True
        email.save()

        # Can not add the same email twice
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)
        self.assertTrue("Could not register email address." in response.data)

    def test_user_can_verify_new_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count+1, len(response.data))

        # Mark email as verified
        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertEqual(len(mail.outbox), 1)
        verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[0].body).group("url")
        response = self.client.get(verify_url)
        self.assertEqual(response.status_code, 302)

        email = CachedEmailAddress.cached.get(email='*****@*****.**')
        self.assertTrue(email.verified)

    def test_user_cant_register_new_email_verified_by_other(self):
        second_user = self.setup_user(authenticate=False)
        existing_mail = CachedEmailAddress.objects.create(
            user=self.first_user, email='*****@*****.**', verified=True)

        response = self.client.get('/v1/user/emails')

        self.assertEqual(response.status_code, 200)
        starting_count = len(response.data)

        # Another user tries to add this email
        self.client.force_authenticate(user=second_user)
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 400)

        self.client.force_authenticate(user=self.first_user)
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(starting_count, len(response.data))

    def test_user_can_remove_email(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        not_primary = random.choice([e for e in response.data if e['verified'] and not e['primary']])
        primary = random.choice([e for e in response.data if e['primary']])

        # cant remove primary email
        response = self.client.delete('/v1/user/emails/{}'.format(primary.get('id')))
        self.assertEqual(response.status_code, 400)
        response = self.client.get('/v1/user/emails/{}'.format(primary.get('id')))
        self.assertEqual(response.status_code, 200)

        # can remove a non-primary email
        response = self.client.delete('/v1/user/emails/{}'.format(not_primary.get('id')))
        self.assertEqual(response.status_code, 200)
        response = self.client.get('/v1/user/emails/{}'.format(not_primary.get('id')))
        self.assertEqual(response.status_code, 404)

    def test_user_can_make_email_primary(self):
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)

        self.assertGreater(len(response.data), 0)

        not_primary = random.choice([e for e in response.data if e['verified'] and not e['primary']])

        # set a non primary email to primary
        response = self.client.put('/v1/user/emails/{}'.format(not_primary.get('id')), {
            'primary': True
        })
        self.assertEqual(response.status_code, 200)

        # confirm that the new email is primary and the others aren't
        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        for email in response.data:
            if email['id'] == not_primary['id']:
                self.assertEqual(email['primary'], True)
            else:
                self.assertEqual(email['primary'], False)

    def test_user_can_resend_verification_email(self):
        # register a new un-verified email
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)
        self.assertEqual(len(mail.outbox), 1)

        response = self.client.get('/v1/user/emails')
        self.assertEqual(response.status_code, 200)
        not_verified = random.choice([e for e in response.data if not e['verified']])
        verified = random.choice([e for e in response.data if e['verified']])

        # dont resend verification email if already verified
        response = self.client.put('/v1/user/emails/{}'.format(verified.get('id')), {
            'resend': True
        })
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 1)

        # gets an email for an unverified email
        response = self.client.put('/v1/user/emails/{}'.format(not_verified.get('id')), {
            'resend': True
        })
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(mail.outbox), 2)

    def test_no_login_on_confirmation_of_verified_email(self):
        # register a new un-verified email
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        # receive verification email
        self.assertEqual(len(mail.outbox), 1)
        verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[0].body).group("url")

        # verify the email address
        email_address = CachedEmailAddress.objects.filter(verified=False).get()
        email_address.verified = True
        email_address.save()

        # verification attempt fails
        response = self.client.get(verify_url)
        self.assertEqual(response.status_code, 302)
        self.assertIn('authError', response['location'])
        self.assertNotIn('authToken', response['location'])

    def test_verification_cannot_be_reused(self):
        # register a new un-verified email
        response = self.client.post('/v1/user/emails', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 201)

        # receive verification email
        self.assertEqual(len(mail.outbox), 1)
        verify_url = re.search("(?P<url>/v1/[^\s]+)", mail.outbox[0].body).group("url")

        # verify the email address successfully
        response = self.client.get(verify_url)
        self.assertEqual(response.status_code, 302)
        self.assertIn('authToken', response['location'])
        self.assertNotIn('authError', response['location'])

        # second verification attempt fails
        response = self.client.get(verify_url)
        self.assertEqual(response.status_code, 302)
        self.assertIn('authError', response['location'])
        self.assertNotIn('authToken', response['location'])

    def test_user_can_request_forgot_password(self):
        self.client.logout()
        cache.clear()

        # dont send recovery to unknown emails
        response = self.client.post('/v1/user/forgot-password', {
            'email': '*****@*****.**',
        })
        self.assertEqual(response.status_code, 200, "Does not leak information about account emails")
        self.assertEqual(len(mail.outbox), 0)

        # successfully send recovery email
        response = self.client.post('/v1/user/forgot-password', {
            'email': self.first_user_email
        })

        backoff_key = backoff_cache_key(self.first_user_email)
        backoff_data = {'127.0.0.1': {'count': 6, 'until': timezone.now() + timezone.timedelta(seconds=60)}}
        cache.set(backoff_key, backoff_data)
        self.assertEqual(cache.get(backoff_key), backoff_data)

        self.assertEqual(response.status_code, 200)
        # received email with recovery url
        self.assertEqual(len(mail.outbox), 1)
        matches = re.search(r'/v1/user/forgot-password\?token=([-0-9a-zA-Z]*)', mail.outbox[0].body)
        self.assertIsNotNone(matches)
        token = matches.group(1)
        new_password = '******'

        # able to use token received in email to reset password
        response = self.client.put('/v1/user/forgot-password', {
            'token': token,
            'password': new_password
        })
        self.assertEqual(response.status_code, 200)

        backoff_data = cache.get(backoff_key)
        self.assertIsNone(backoff_data)

        application = Application.objects.create(
            client_id='public',
            client_secret='',
            user=None,
            authorization_grant_type=Application.GRANT_PASSWORD,
            name='public'
        )
        ApplicationInfo.objects.create(
            application=application,
            allowed_scopes='rw:issuer rw:backpack rw:profile'
        )

        response = self.client.post('/o/token', {
            'username': self.first_user.email,
            'password': new_password,
        })
        self.assertEqual(response.status_code, 200)

    @patch('mainsite.serializers.badgrlogger.event')
    def test_log_when_api_auth_token_endpoint_is_used(self, mocked_logger):
        response = self.client.post('/api-auth/token', {
            'username': self.first_user.username,
            'password': '******',
        })
        self.assertEqual(response.status_code, 200)
        self.assertIn('deprecated', response.data['warning'], 'There is a warning returned to the requester')
        mocked_logger.assert_called_once()
        self.assertTrue(mocked_logger.call_args[0][0].is_new_token)

    @patch('mainsite.authentication.badgrlogger.event')
    def test_log_when_legacy_auth_token_is_used(self, mocked_logger):
        # logout previous user
        self.client.logout()

        user_email = '*****@*****.**'
        user = self.setup_user(email=user_email, authenticate=False)
        token, created = Token.objects.get_or_create(user=user)
        response = self.client.get('/v2/users/self', HTTP_AUTHORIZATION='Token {}'.format(token.key))

        mocked_logger.assert_called_once()
        self.assertIsNotNone(mocked_logger.call_args[0][0].request.META.get("REMOTE_ADDR", None))
        self.assertEquals(mocked_logger.call_args[0][0].username, user.username)
        self.assertFalse(mocked_logger.call_args[0][0].is_new_token)

    def test_lower_variant_autocreated_on_new_email(self):
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=BadgeUser.objects.first(), verified=True
        )
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        variants = EmailAddressVariant.objects.filter(canonical_email=first_email)

        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_new_variant_api(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=user, verified=True
        )
        first_email.save()
        self.assertIsNotNone(first_email.pk)

        self.client.force_authenticate(user=user)
        response = self.client.post('/v1/user/emails', {'email': '*****@*****.**'})

        self.assertEqual(response.status_code, 400)
        self.assertTrue('Matching address already exists. New case variant registered.' in response.data)

        variants = first_email.cached_variants()
        self.assertEqual(len(variants), 1)
        self.assertEqual(variants[0].email, '*****@*****.**')

    def test_can_create_variants(self):
        user = self.setup_user(authenticate=False)
        first_email = CachedEmailAddress.objects.create(email="*****@*****.**", verified=True, user=user)
        self.assertIsNotNone(first_email.pk)

        first_variant_email = "*****@*****.**"
        second_variant_email = "*****@*****.**"

        first_variant = EmailAddressVariant(email=first_variant_email, canonical_email=first_email)
        first_variant.save()
        self.assertEqual(first_variant.canonical_email, first_email)

        second_variant = first_email.add_variant(second_variant_email)
        self.assertEqual(second_variant.canonical_email, first_email)

        self.assertEqual(len(first_email.emailaddressvariant_set.all()), 2)
        self.assertEqual(len(first_email.cached_variants()), 2)

    def test_user_can_create_variant_method(self):
        user = BadgeUser.objects.first()
        first_email = CachedEmailAddress(
            email="*****@*****.**", user=user, verified=True
        )
        first_email.save()
        first_email.add_variant("*****@*****.**")

        self.assertTrue(user.can_add_variant("*****@*****.**"))
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # already exists
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # is the original
        self.assertFalse(user.can_add_variant("*****@*****.**"))  # not a match of original

    def test_can_create_variant_for_unconfirmed_email(self):
        user = BadgeUser.objects.first()
        new_email_address = "*****@*****.**"
        new_email = CachedEmailAddress.objects.create(email=new_email_address, user=user)
        new_variant = EmailAddressVariant(email=new_email_address.upper(), canonical_email=new_email)

        new_variant.save()
        self.assertFalse(new_variant.verified)

        verified_emails = [e.email for e in user.emailaddress_set.filter(verified=True)] \
                          + [e.email for e in user.cached_email_variants() if e.verified]

        self.assertTrue(new_variant not in verified_emails)

    def cannot_link_variant_of_case_insensitive_nonmatch(self):
        first_email = CachedEmailAddress.objects.get(email="*****@*****.**")
        self.assertIsNotNone(first_email.pk)

        variant_email = "*****@*****.**"

        variant = EmailAddressVariant(email=variant_email, canonical_email=first_email)
        try:
            variant.save()
        except ValidationError as e:
            self.assertEqual(e.message, "New EmailAddressVariant does not match stored email address.")
        else:
            raise self.fail("ValidationError expected on nonmatch.")