def test_browserid_exception(self, requests): """ If requests.post raises an exception, wrap it in a BrowserIDException. """ requests.post.side_effect = RequestException requests.exceptions.RequestException = RequestException with self.assertRaises(BrowserIDException): verify('asdf', 'http://testserver/')
def test_verify_post_uses_custom_settings(post): post.return_value.content = '{"status": "okay"}' verify(assertion, audience) post.assert_called_with('https://custom.org/verify', verify=True, proxies=ANY, data=ANY, timeout=1, headers=ANY)
def test_custom_url(self): # If a custom URL is passed into verify, send the verification request # to that URL. verify('asdf', 'http://testserver/', url='https://example.com') self.post.assert_called_with('https://example.com', data=ANY, proxies=ANY, verify=ANY, headers=ANY, timeout=ANY)
def test_verify_with_custom_url(post): post.return_value.content = '{"status": "okay"}' url = 'https://custom-service.org/verify' verify(assertion, audience, url=url) post.assert_called_with(url, verify=ANY, proxies=ANY, data=ANY, timeout=ANY, headers=ANY)
def test_cacert_file(self): # If certificate verification is enabled and BROWSERID_CACERT_FILE is # set, the path to that file should be passed to requests in the # 'verify' kwarg. verify('asdf', 'http://testserver/') self.post.assert_called_with(ANY, data=ANY, proxies=ANY, verify='http://testserver/path/to/file', headers=ANY, timeout=ANY)
def test_custom_settings(self): verify('asdf', 'http://testserver/') self.post.assert_called_with( 'http://example.org', data={ 'assertion': 'asdf', 'audience': 'http://testserver/' }, proxies={'http': 'http://blah.example.com'}, verify=False, headers=ANY, timeout=10)
def test_authenticate_unverified_user(_verify_http_request): """ Test that extra parameters are passed through to _verify_http_request correctly. """ # In real life, BROWSERID_VERIFICATION_URL would point to the # BID Unverified Email verifier. (Yes, that makes my head hurt too.) args = dict(authenticate_kwargs) args['extra_params'] = {'issuer': 'a.b.c', 'allow_unverified': True} verify(**args) _verify_http_request.assert_called_once_with( 'https://unverifier.persona.org/verify', ANY) assert 'allow_unverified=True' in _verify_http_request.call_args[0][1]
def test_extra_params(self): # If extra params are passed into verify, they should be included with # the other POST arguments in the verification tests. verify('asdf', 'http://testserver/', extra_params={'a': 'b', 'c': 'd'}) expected_data = { 'assertion': 'asdf', 'audience': 'http://testserver/', 'a': 'b', 'c': 'd' } self.post.assert_called_with(ANY, data=expected_data, proxies=ANY, verify=ANY, headers=ANY, timeout=ANY)
def authenticate(self, assertion=None, audience=None): """``django.contrib.auth`` compatible authentication method. Given a BrowserID assertion and an audience, it attempts to verify them and then extract the email address for the authenticated user. An audience should be in the form ``https://example.com`` or ``http://localhost:8001``. See django_browserid.base.get_audience() """ result = verify(assertion, audience) if not result: return email = result['email'] # in the rare case that two user accounts have the same email address, # log and bail. randomly selecting one seems really wrong. users = self.filter_users_by_email(email=email) if len(users) > 1: log.warn('%d users with email address %s.' % (len(users), email)) return if len(users) == 1: return users[0] create_user = getattr(settings, 'BROWSERID_CREATE_USER', True) if not create_user: return elif create_user == True: return self.create_user(email) else: # Find the function to call, call it and throw in the email. return self._load_module(create_user)(email)
def authenticate(self, assertion=None, audience=None): """``django.contrib.auth`` compatible authentication method. Given a BrowserID assertion and an audience, it attempts to verify them and then extract the email address for the authenticated user. An audience should be in the form ``https://example.com`` or ``http://localhost:8001``. See django_browserid.base.get_audience() """ result = verify(assertion, audience) if result is None: return None email = result['email'] # in the rare case that two user accounts have the same email address, # log and bail. randomly selecting one seems really wrong. users = self.filter_users_by_email(email=email) if len(users) > 1: log.warn('%d users with email address %s.' % (len(users), email)) return None if len(users) == 1: return users[0] create_user = getattr(settings, 'BROWSERID_CREATE_USER', False) if not create_user: return None elif create_user == True: return self.create_user(email) else: # Find the function to call, call it and throw in the email. return self._load_module(create_user)(email)
def authenticate(self, assertion=None, audience=None): """``django.contrib.auth`` compatible authentication method. Given a BrowserID assertion and an audience, it attempts to verify them and then extract the email address for the authenticated user. An audience should be in the form ``https://example.com`` or ``http://localhost:8001``. See django_browserid.base.get_audience() """ result = verify(assertion, audience) if result is None: return None email = result['email'] # in the rare case that two user accounts have the same email address, # log and bail. randomly selecting one seems really wrong. users = self.filter_users_by_email(email=email) if len(users) > 1: log.warn('%d users with email address %s.' % (len(users), email)) return None if len(users) == 1: return users[0] create_user = getattr(settings, 'BROWSERID_CREATE_USER', False) if not create_user: return None username_algo = getattr(settings, 'BROWSERID_USERNAME_ALGO', default_username_algo) user = User.objects.create_user(username_algo(email), email) user.is_active = True user.save() return user
def form_valid(self, form): """Custom form validation to support email changing. If user is already authenticated and reaches this point, it's an email changing procedure. Validate that email is good and save it in the database. Otherwise continue with the default django-browserid verification. """ if not self.request.user.is_authenticated(): return super(BrowserIDVerify, self).form_valid(form) failure_url = urlparams(reverse('phonebook:profile_edit'), bid_login_failed=1) self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) result = verify(self.assertion, self.audience) if not result: messages.error(self.request, _('Authentication failed.')) return redirect(failure_url) email = result['email'] if User.objects.filter(email=email).exists(): messages.error(self.request, _('Email already exists in the database.')) return redirect('phonebook:logout') user = self.request.user user.email = email user.save() return redirect('phonebook:profile_view', user.username)
def authenticate(self, browserid_assertion=None, browserid_audience=None, request=None): result = browserid_base.verify(browserid_assertion, browserid_audience) if not result: return None email = result['email'] try: user = self.user_class.objects.get(browserid_profile_data__email=email) # TODO: What is we get more than one user? except self.user_class.DoesNotExist: # TODO: Based on user preference, we might create a new user here, not just link with existing, if existing user is lazy user # We reload to make sure user object is recent request.user.reload() user = request.user # TODO: Is it OK to override BrowserID link if it already exist with some other BrowserID user? user.browserid_profile_data = result if user.lazyuser_username: # Best username guess we can get from BrowserID user.username = email.rsplit('@', 1)[0] user.lazyuser_username = False if user.email is None: user.email = email or None # BrowserID takes care of email confirmation user.email_confirmed = True user.save() return user
def authenticate(self, browserid_assertion=None, browserid_audience=None, request=None): result = browserid_base.verify(browserid_assertion, browserid_audience) if not result: return None email = result['email'] try: user = self.user_class.objects.get( browserid_profile_data__email=email) # TODO: What is we get more than one user? except self.user_class.DoesNotExist: # TODO: Based on user preference, we might create a new user here, not just link with existing, if existing user is lazy user # We reload to make sure user object is recent request.user.reload() user = request.user # TODO: Is it OK to override BrowserID link if it already exist with some other BrowserID user? user.browserid_profile_data = result if user.lazyuser_username: # Best username guess we can get from BrowserID user.username = email.rsplit('@', 1)[0] user.lazyuser_username = False if user.email is None: user.email = email or None # BrowserID takes care of email confirmation user.email_confirmed = True user.save() return user
def authenticate(self, browserid_assertion=None, browserid_audience=None, request=None): browserid_profile_data = browserid_base.verify(browserid_assertion, browserid_audience) if not browserid_profile_data or 'email' not in browserid_profile_data: return None try: user = self.user_class.objects.get( browserid_profile_data__email=browserid_profile_data['email']) # TODO: What is we get more than one user? except self.user_class.DoesNotExist: # TODO: Based on user preference, we might create a new user here, not just link with existing, if existing user is lazy user # We reload to make sure user object is recent request.user.reload() user = request.user # TODO: Is it OK to override BrowserID link if it already exist with some other BrowserID user? user.browserid_profile_data = browserid_profile_data user.authenticate_browserid(request) user.save() return user
def form_valid(self, form): """Custom form validation to support email changing. If user is already authenticated and reaches this points, it's an email changing procedure. Validate that email is good and save it in the database. Otherwise continue with the default django-browserid verification. """ if not self.request.user.is_authenticated(): return super(BrowserIDVerify, self).form_valid(form) failure_url = urlparams(reverse('phonebook:profile_edit'), bid_login_failed=1) self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) result = verify(self.assertion, self.audience) if not result: messages.error(self.request, _('Authentication failed.')) return redirect(failure_url) email = result['email'] if User.objects.filter(email=email).exists(): messages.error(self.request, _('Email already exists in the database.')) return redirect('phonebook:logout') user = self.request.user user.email = email user.save() return redirect('phonebook:profile_view', user.username)
def authenticate(self, assertion): """Verifies BrowserID assertion Returns: Object that represents a user with an email verified by the assertion. None if user with such email does not exist. Raises: AssertionVerificationException: verification failed. """ result = verify(assertion=assertion, audience=models.SITE_URL) if not result: raise AssertionVerificationException( 'BrowserID assertion verification failed.') return self.users_collection.find_item_by_email(result['email'])
def test_basic_success(self): self.post.return_value.content = """ {"status": "okay", "email": "*****@*****.**"} """ result = verify('asdf', 'http://testserver/') self.assertEqual(result, {'status': 'okay', 'email': '*****@*****.**'}) self.post.assert_called_with('https://example.com', data={ 'assertion': 'asdf', 'audience': 'http://testserver/' }, proxies=None, verify=True, headers={'Foo': 'Bar'}, timeout=5)
def authenticate(self, assertion=None, audience=None, browserid_extra=None, **kw): """``django.contrib.auth`` compatible authentication method. Given a BrowserID assertion and an audience, it attempts to verify them and then extract the email address for the authenticated user. An audience should be in the form ``https://example.com`` or ``http://localhost:8001``. See django_browserid.base.get_audience() """ result = verify(assertion, audience, extra_params=browserid_extra) if not result: return None email = result['email'] # In the rare case that two user accounts have the same email address, # log and bail. Randomly selecting one seems really wrong. users = self.filter_users_by_email(email=email) if len(users) > 1: logger.warn('%s users with email address %s.', len(users), email) return None if len(users) == 1: return users[0] create_user = getattr(settings, 'BROWSERID_CREATE_USER', True) if not create_user: logger.debug( 'Login failed: No user with email %s found, and ' 'BROWSERID_CREATE_USER is False', email) return None else: if create_user is True: create_function = self.create_user else: # Find the function to call. create_function = self._load_module(create_user) user = create_function(email) user_created.send(create_function, user=user) return user
def authenticate(self, assertion=None, audience=None, browserid_extra=None, **kw): """``django.contrib.auth`` compatible authentication method. Given a BrowserID assertion and an audience, it attempts to verify them and then extract the email address for the authenticated user. An audience should be in the form ``https://example.com`` or ``http://localhost:8001``. See django_browserid.base.get_audience() """ result = verify(assertion, audience, extra_params=browserid_extra) if not result: return None email = result['email'] if not self.is_valid_email(email): return None # In the rare case that two user accounts have the same email address, # log and bail. Randomly selecting one seems really wrong. users = self.filter_users_by_email(email=email) if len(users) > 1: logger.warn('%s users with email address %s.', len(users), email) return None if len(users) == 1: return users[0] create_user = getattr(settings, 'BROWSERID_CREATE_USER', True) if not create_user: logger.debug('Login failed: No user with email %s found, and ' 'BROWSERID_CREATE_USER is False', email) return None else: if create_user is True: create_function = self.create_user else: # Find the function to call. create_function = import_function_from_setting('BROWSERID_CREATE_USER') user = create_function(email) user_created.send(create_function, user=user) return user
def authenticate(self, browserid_assertion=None, browserid_audience=None, request=None): browserid_profile_data = browserid_base.verify(browserid_assertion, browserid_audience) if not browserid_profile_data or 'email' not in browserid_profile_data: return None try: user = self.user_class.objects.get(browserid_profile_data__email=browserid_profile_data['email']) # TODO: What is we get more than one user? except self.user_class.DoesNotExist: # TODO: Based on user preference, we might create a new user here, not just link with existing, if existing user is lazy user # We reload to make sure user object is recent request.user.reload() user = request.user # TODO: Is it OK to override BrowserID link if it already exist with some other BrowserID user? user.browserid_profile_data = browserid_profile_data user.authenticate_browserid(request) user.save() return user
def authenticate(self, site, site_url, assertion): """Verifies BrowserID assertion Returns: Object that represents a user with an email verified by the assertion. If a user with such email does not exists, but there are open locations that require login, the user object is created. In other cases, None is returned. Raises: AssertionVerificationException: verification failed. """ result = verify(assertion=assertion, audience=site_url) if not result: # TODO: different error if Persona is down. raise AssertionVerificationException( 'BrowserID assertion verification failed.') email = result['email'] user = site.users.find_item_by_email(result['email']) if user is not None: return user try: # The site has open locations that require login, every # user needs to be allowed. # # TODO: user objects created in such way should probably # be marked and automatically deleted on logout or after # some time of inactivity. if site.locations.has_open_location_with_login(): return site.users.create_item(email) else: return None except ValidationError as ex: raise AssertionVerificationException(', '.join(ex.messages)) except LimitExceeded as ex: raise AssertionVerificationException(str(ex))
def test_invalid_json(self): # If the JSON returned by the verification server is invalid, return # False. self.post.return_value.content = '{invalid-json}' self.assertEqual(verify('asdf', 'http://testserver/'), False)
def test_verify_correct_credentials(fake): """Test that verify() returns assertion details when assertion is valid.""" with positive_assertion(fake): verification = verify(assertion, audience) assert verification['status'] == 'okay' assert verification['email'] == '*****@*****.**'
def verify(self, *args): warn('Deprecated, please use the standalone function ' 'django_browserid.verify instead.', DeprecationWarning) return verify(*args)
def test_verify_correct_credentials(): """Test that verify() returns assertion details when assertion is valid.""" verification = verify(assertion, audience) assert verification["status"] == "okay" assert verification["email"] == "*****@*****.**"
def test_verify_correct_credentials(): # Test that verify() returns assertion details when assertion is valid. verification = verify(assertion, audience) assert verification['status'] == 'okay' assert verification['email'] == '*****@*****.**'
def test_valid_json_failure(self): # If the verification request returns valid json with a status that # isn't 'okay', return False. self.post.return_value.content = '{"status": "failure"}' self.assertEqual(verify('asdf', 'http://testserver/'), False)
def verify(self, *args): warn( 'Deprecated, please use the standalone function ' 'django_browserid.verify instead.', DeprecationWarning) return verify(*args)
def test_browserid_exception(self): # If requests.post raises an exception, wrap it in BrowserIDException. self.post.side_effect = RequestException with self.assertRaises(BrowserIDException): verify('asdf', 'http://testserver/')