def test_no_audience(self): # If no matching audiences is found in BROWSERID_AUDIENCES, raise ImproperlyConfigured. request = self.factory.get('http://testserver') with self.settings(BROWSERID_AUDIENCES=['https://example.com']): with self.assertRaises(ImproperlyConfigured): get_audience(request)
def test_setting_missing(self): # If BROWSERID_AUDIENCES isn't defined, raise # ImproperlyConfigured. request = self.factory.get('/') with patch('django_browserid.base.settings') as settings: del settings.BROWSERID_AUDIENCES settings.DEBUG = False with self.assertRaises(ImproperlyConfigured): base.get_audience(request)
def test_setting_missing(self): # If BROWSERID_AUDIENCES isn't defined, raise ImproperlyConfigured. request = self.factory.get('/') # Simulate missing attribute with a mock property that raises AttributeError. with patch('django_browserid.base.settings') as settings: mock_browserid_audiences = PropertyMock(side_effect=AttributeError) type(settings).BROWSERID_AUDIENCES = mock_browserid_audiences with self.assertRaises(ImproperlyConfigured): get_audience(request)
def post(self, *args, **kwargs): self.change_email = False if not self.request.user.is_authenticated(): return super(BrowserIDVerify, self).post(*args, **kwargs) self.change_email = True assertion = self.request.POST.get('assertion') if not assertion: return self.login_failure() verifier = RemoteVerifier() audience = get_audience(self.request) result = verifier.verify(assertion, audience) if not result: messages.error(self.request, _('Authentication failed.')) return self.login_failure() email = result.email if User.objects.filter(email=email).exists(): error_msg = "Email '{0}' already exists in the database.".format(email) messages.error(self.request, _(error_msg)) return self.login_failure() self.user = self.request.user self.user.email = email self.user.save() return self.login_success()
def mozilla_browserid_verify(request): """Custom BrowserID verifier for mozilla addresses.""" home_url = reverse('crashstats.home', args=(settings.DEFAULT_PRODUCT,)) form = BrowserIDForm(request.POST) if form.is_valid(): assertion = form.cleaned_data['assertion'] audience = get_audience(request) result = verify(assertion, audience) if not settings.ALLOWED_PERSONA_EMAILS: # pragma: no cover raise ValueError( "No emails set up in `settings.ALLOWED_PERSONA_EMAILS`" ) if result: if result['email'] in settings.ALLOWED_PERSONA_EMAILS: user = auth.authenticate(assertion=assertion, audience=audience) auth.login(request, user) messages.success( request, 'You have successfully logged in.' ) else: messages.error( request, "You logged in as %s but you don't have sufficient " "privileges." % result['email'] ) else: messages.error( request, "Login failed" ) return redirect(home_url)
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 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 test_same_origin_found(self): # If an audience is found in BROWSERID_AUDIENCES with the same # origin as the request URI, return it. request = self.factory.get('http://testserver') audiences = ['https://example.com', 'http://testserver'] with self.settings(BROWSERID_AUDIENCES=audiences): eq_(base.get_audience(request), 'http://testserver')
def test_same_origin_found(self): # If an audience is found in BROWSERID_AUDIENCES with the same origin as the request URI, # return it. request = self.factory.get('http://testserver') audiences = ['https://example.com', 'http://testserver'] with self.settings(BROWSERID_AUDIENCES=audiences): eq_(get_audience(request), 'http://testserver')
def test_no_audience_but_in_debug(self): # If no BROWSERID_AUDIENCES is set but in DEBUG just use the # current protocal and host request = self.factory.get('/') # Simulate that no BROWSERID_AUDIENCES has been set with self.settings(BROWSERID_AUDIENCES=[], DEBUG=True): eq_(base.get_audience(request), 'http://testserver')
def mozilla_browserid_verify(request): """Custom BrowserID verifier for mozilla addresses.""" form = BrowserIDForm(request.POST) if form.is_valid(): assertion = form.cleaned_data['assertion'] audience = get_audience(request) result = verify(assertion, audience) try: _ok_assertion = False _is_contributor = False if result: _domain = result['email'].split('@')[-1] if _domain in settings.ALLOWED_BID: _ok_assertion = True elif is_vouched(result['email']): _ok_assertion = True _is_contributor = True if _ok_assertion: user = auth.authenticate( assertion=assertion, audience=audience ) auth.login(request, user) # if _is_contributor: # try: # profile = user.get_profile() # if not profile.contributor: # profile.contributor = True # profile.save() # except UserProfile.DoesNotExist: # profile = UserProfile.objects.create( # user=user, # contributor=True # ) if auth.models.User.objects.count() == 1: auth.models.User.objects.update(is_superuser=True, is_staff=True); return redirect(request.POST.get('next', settings.LOGIN_REDIRECT_URL)) elif result: messages.error( request, 'Hmmm. Are you sure you\'re a vouched mozillian?' ) except BadStatusCodeError: logging.error('Unable to call out to mozillians', exc_info=True) messages.error( request, 'Email (%s) authenticated but unable to connect to ' 'Mozillians to see if are vouched. ' % result['email'] ) return redirect(settings.LOGIN_REDIRECT_URL_FAILURE)
def test_same_origin_found(self): """ If an audience is found in BROWSERID_AUDIENCES with the same origin as the request URI, return it. """ request = self.factory.get('http://testserver') audiences = ['https://example.com', 'http://testserver'] with self.settings(BROWSERID_AUDIENCES=audiences, DEBUG=False): self.assertEqual(base.get_audience(request), 'http://testserver')
def test_missing_setting_but_in_debug(self): # If no BROWSERID_AUDIENCES is set but in DEBUG just use the # current protocal and host request = self.factory.get('/') # Simulate that no BROWSERID_AUDIENCES has been set with patch('django_browserid.base.settings') as settings: del settings.BROWSERID_AUDIENCES settings.DEBUG = True eq_(base.get_audience(request), 'http://testserver')
def mozilla_browserid_verify(request): """Custom BrowserID verifier for mozilla addresses.""" home_url = reverse('crashstats.home', args=(settings.DEFAULT_PRODUCT,)) goto_url = request.POST.get('goto', None) or home_url form = BrowserIDForm(request.POST) if form.is_valid(): assertion = form.cleaned_data['assertion'] audience = get_audience(request) result = verify(assertion, audience) for name in ('LDAP_BIND_DN', 'LDAP_BIND_PASSWORD', 'LDAP_GROUP_NAMES'): if not getattr(settings, name, None): # pragma: no cover raise ValueError( "Not configured `settings.%s`" % name ) if result: allowed = in_allowed_group(result['email']) debug_email_addresses = getattr( settings, 'DEBUG_LDAP_EMAIL_ADDRESSES', [] ) if debug_email_addresses and not settings.DEBUG: raise SuspiciousOperation( "Can't debug login when NOT in DEBUG mode" ) if allowed or result['email'] in debug_email_addresses: if allowed: logging.info('%r is in an allowed group', result['email']) else: logging.info('%r allowed for debugging', result['email']) user = auth.authenticate(assertion=assertion, audience=audience) auth.login(request, user) messages.success( request, 'You have successfully logged in.' ) else: if not allowed: logging.info('%r NOT in an allowed group', result['email']) messages.error( request, "You logged in as %s but you don't have sufficient " "privileges." % result['email'] ) else: messages.error( request, "Login failed" ) return redirect(goto_url)
def form_valid(self, form): """Handles the return post request from the browserID form and puts interesting variables into the class. If everything checks out, then we call handle_user to decide how to handle a valid user """ self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) self.user = auth.authenticate(assertion=self.assertion, audience=self.audience) if self.user and self.user.is_active: return self.login_success() return self.login_failure()
def mozilla_browserid_verify(request): """Custom BrowserID verifier for mozilla addresses.""" form = BrowserIDForm(request.POST) if form.is_valid(): assertion = form.cleaned_data['assertion'] audience = get_audience(request) result = verify(assertion, audience) try: _ok_assertion = False _is_contributor = False if result: _domain = result['email'].split('@')[-1] if _domain in settings.ALLOWED_BID: _ok_assertion = True elif is_vouched(result['email']): _ok_assertion = True _is_contributor = True if _ok_assertion: user = auth.authenticate(assertion=assertion, audience=audience) auth.login(request, user) # if _is_contributor: # try: # profile = user.get_profile() # if not profile.contributor: # profile.contributor = True # profile.save() # except UserProfile.DoesNotExist: # profile = UserProfile.objects.create( # user=user, # contributor=True # ) if auth.models.User.objects.count() == 1: auth.models.User.objects.update(is_superuser=True, is_staff=True) return redirect( request.POST.get('next', settings.LOGIN_REDIRECT_URL)) elif result: messages.error( request, 'Hmmm. Are you sure you\'re a vouched mozillian?') except BadStatusCodeError: logging.error('Unable to call out to mozillians', exc_info=True) messages.error( request, 'Email (%s) authenticated but unable to connect to ' 'Mozillians to see if are vouched. ' % result['email']) return redirect(settings.LOGIN_REDIRECT_URL_FAILURE)
def form_valid(self, form): """Handles the return post request from the browserID form and puts interesting variables into the class. If everything checks out, then we call handle_user to decide how to handle a valid user """ self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) self.user = auth.authenticate( assertion=self.assertion, audience=self.audience) if self.user and self.user.is_active: return self.login_success() return self.login_failure()
def browserid_verify(request): next = request.REQUEST.get('next') redirect_to = next or getattr(settings, 'LOGIN_REDIRECT_URL', '/') redirect_to_failure = getattr(settings, 'LOGIN_REDIRECT_URL_FAILURE', '/') form = BrowserIDForm(data=request.POST) if form.is_valid(): verifier = BrowserIDBackend().get_verifier() result = verifier.verify(form.cleaned_data['assertion'], get_audience(request)) if result: if (request.user.is_authenticated() and request.user.email != result.email): # User is already signed and wants to change their email. request.user.email = result.email request.user.save() return redirect(reverse('users.edit_profile')) else: # Verified so log in email = result.email user = User.objects.filter(email=email) contributor = 'contributor' in request.POST if len(user) == 0: # Add the email to the session and redirect to signup request.session['browserid-email'] = email signup_url = reverse('users.browserid_signup') return redirect('%s?%s' % (signup_url, urlencode({'next': next}))) else: user = user[0] user.backend = 'django_browserid.auth.BrowserIDBackend' if contributor: add_to_contributors(request, user) auth.login(request, user) return redirect(redirect_to) return redirect(redirect_to_failure)
def form_valid(self, form): """ Send the given assertion to the remote verification service and, depending on the result, trigger login success or failure. :param form: Instance of BrowserIDForm that was submitted by the user. """ self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) try: self.user = auth.authenticate(assertion=self.assertion, audience=self.audience) except BrowserIDException as e: return self.login_failure(e) if self.user and self.user.is_active: return self.login_success() return self.login_failure()
def post(self, *args, **kwargs): self.add_email = False if not self.request.user.is_authenticated(): return super(BrowserIDVerify, self).post(*args, **kwargs) self.add_email = True assertion = self.request.POST.get('assertion') if not assertion: return self.login_failure() verifier = RemoteVerifier() audience = get_audience(self.request) result = verifier.verify(assertion, audience) if not result: messages.error(self.request, _('Authentication failed.')) return self.login_failure() email = result.email kwargs = { 'type': ExternalAccount.TYPE_EMAIL, 'user': self.request.user.userprofile, 'identifier': email } email_exists = User.objects.filter(email=email).exists() alternate_email_exists = ExternalAccount.objects.filter( **kwargs).exists() if email_exists or alternate_email_exists: error_msg = "Email '{0}' already exists in the database.".format( email) messages.error(self.request, _(error_msg)) return self.login_failure() ExternalAccount.objects.create(**kwargs) return self.login_success()
def mozilla_browserid_verify(request): """Custom BrowserID verifier for mozilla addresses.""" home_url = reverse('crashstats.home', args=(settings.DEFAULT_PRODUCT, )) form = BrowserIDForm(request.POST) if form.is_valid(): assertion = form.cleaned_data['assertion'] audience = get_audience(request) result = verify(assertion, audience) for name in ('LDAP_BIND_DN', 'LDAP_BIND_PASSWORD', 'LDAP_GROUP_NAMES'): if not getattr(settings, name, None): # pragma: no cover raise ValueError("Not configured `settings.%s`" % name) if result: allowed = in_allowed_group(result['email']) debug_email_addresses = getattr(settings, 'DEBUG_LDAP_EMAIL_ADDRESSES', []) if debug_email_addresses and not settings.DEBUG: raise SuspiciousOperation( "Can't debug login when NOT in DEBUG mode") if allowed or result['email'] in debug_email_addresses: if allowed: logging.info('%r is in an allowed group', result['email']) else: logging.info('%r allowed for debugging', result['email']) user = auth.authenticate(assertion=assertion, audience=audience) auth.login(request, user) messages.success(request, 'You have successfully logged in.') else: if not allowed: logging.info('%r NOT in an allowed group', result['email']) messages.error( request, "You logged in as %s but you don't have sufficient " "privileges." % result['email']) else: messages.error(request, "Login failed") return redirect(home_url)
def form_valid(self, form): """ Send the given assertion to the remote verification service and, depending on the result, trigger login success or failure. :param form: Instance of BrowserIDForm that was submitted by the user. """ self.assertion = form.cleaned_data['assertion'] self.audience = get_audience(self.request) try: self.user = auth.authenticate( assertion=self.assertion, audience=self.audience ) except BrowserIDException as e: return self.login_failure(e) if self.user and self.user.is_active: return self.login_success() return self.login_failure()
def post(self, *args, **kwargs): self.add_email = False if not self.request.user.is_authenticated(): return super(BrowserIDVerify, self).post(*args, **kwargs) self.add_email = True assertion = self.request.POST.get('assertion') if not assertion: return self.login_failure() verifier = RemoteVerifier() audience = get_audience(self.request) result = verifier.verify(assertion, audience) if not result: messages.error(self.request, _('Authentication failed.')) return self.login_failure() email = result.email kwargs = { 'type': ExternalAccount.TYPE_EMAIL, 'user': self.request.user.userprofile, 'identifier': email } email_exists = User.objects.filter(email=email).exists() alternate_email_exists = ExternalAccount.objects.filter(**kwargs).exists() if email_exists or alternate_email_exists: error_msg = "Email '{0}' already exists in the database.".format(email) messages.error(self.request, _(error_msg)) return self.login_failure() ExternalAccount.objects.create(**kwargs) return self.login_success()
def verify(self, assertion=None, audience=None, request=None, **kwargs): """ Verify the given assertion and audience. See ``authenticate`` for accepted arguments. """ if audience is None and request: audience = get_audience(request) if audience is None or assertion is None: return None verifier = self.get_verifier() try: result = verifier.verify(assertion, audience, **kwargs) except Exception as e: result = None logger.warn('Error while verifying assertion %s with audience %s.', assertion, audience) logger.warn(e) if not result: return None else: return result.email
def test_improperly_configured(self): # Raise ImproperlyConfigured if SITE_URL doesn't match the request's # URL. request = self.factory.post('/', SERVER_NAME='www.blah.com') with self.assertRaises(ImproperlyConfigured): get_audience(request)
def test_properly_configured(self): # Return SITE_URL if it matches the request URL and DEBUG = True. request = self.factory.post('/', SERVER_NAME='example.com') self.assertEqual('http://example.com', get_audience(request))
def test_iterable(self): # Return correct url from iterable SITE_URL, if it contains request URL. request = self.factory.post('/', SERVER_NAME='example2.com') self.assertEqual('http://example2.com', get_audience(request))
def authenticate(self, assertion=None, audience=None, request=None, **kwargs): """ Authenticate a user by verifying a BrowserID assertion. Defers to the verifier returned by :func:`BrowserIDBackend.get_verifier` for verification. You may either pass the ``request`` parameter to determine the audience from the request, or pass the ``audience`` parameter explicitly. :param assertion: Assertion submitted by the user. This asserts that the user controls a specific email address. :param audience: The audience to use when verifying the assertion; this prevents another site using an assertion for their site to login to yours. This value takes precedence over the audience pulled from the request parameter, if given. :param request: The request that generated this authentication attempt. This is used to determine the audience to use during verification, using the :func:`django_browserid.base.get_audience` function. If the audience parameter is also passed, it will be used instead of the audience from the request. :param kwargs: All remaining keyword arguments are passed to the ``verify`` function on the verifier. """ if audience is None and request: audience = get_audience(request) if audience is None or assertion is None: return None verifier = self.get_verifier() result = verifier.verify(assertion, audience, **kwargs) 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_from_setting('BROWSERID_CREATE_USER') user = create_function(email) user_created.send(create_function, user=user) return user
def test_no_site_url(self): # If SITE_URL isn't set, use the domain from the request. request = self.factory.post('/', SERVER_NAME='www.blah.com') self.assertEqual('http://www.blah.com', get_audience(request))