From c0dcead75767087f2bb7024db451b5207547f78d Mon Sep 17 00:00:00 2001 From: Austin King Date: Wed, 30 Nov 2011 18:00:15 -0800 Subject: [PATCH] Updating README. Adding warning when SITE_URL doesn't look right. --- CHANGELOG.rst | 10 +++- README.rst | 58 ++++++++++++++------- django_browserid/auth.py | 28 +++++++--- django_browserid/tests/test_verification.py | 3 +- 4 files changed, 69 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9d84a23..a63b8b1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,9 +1,15 @@ CHANGELOG --------- -0.9.2 - *NOTE* get_audience API breakage - Train 2011-10-20 now accepts the scheme and port number as part of the audience. +0.9.2a - **API breakage** - BrowserID Train 2011-10-20 now accepts the scheme and port number as part of the audience. - get_audience(self, host, port) has become get_audience(self, request, host, port) + ``get_audience(host, port)`` has become ``get_audience(request)`` + ``authenticate(assertion, host, port)`` has become ``authenticate(assertion, audience)`` + + ``verify`` has been updated, so if you use the basic integration with browserid_verify, you should be fine. This mainly + breaks sites that have custom account creation or use the ``auth.authenticate`` function. + + To improve security, you should add ``SITE_URL`` to your Django settings. See README.rst for details. 0.9.1 - Create User refactoring, navigator.id, httplib2/verify fixes diff --git a/README.rst b/README.rst index e501a7c..9a5e2e5 100644 --- a/README.rst +++ b/README.rst @@ -62,21 +62,6 @@ You can also set the following optional config in ``settings.py`` # Path to redirect to on unsuccessful login attempt. LOGIN_REDIRECT_URL_FAILURE = '/' -Unless your really noodling around with BrowserID, you probably won't need these -optional config in ``settings.py`` (they have sensible defaults): :: - - # URL of a BrowserID verification service. - BROWSERID_VERIFICATION_URL = 'https://browserid.org/verify' - - # Proxy Info, see httplib2 documentation - BROWSERID_PROXY_INFO = None - - # CA cert file for validating SSL certificate - BROWSERID_CACERT_FILE = None - - # Disable SSL cert validation - BROWSERID_DISABLE_CERT_CHECK = False - # Create user accounts automatically if no user is found. BROWSERID_CREATE_USER = True @@ -115,12 +100,27 @@ Finally, you'll need some Javascript to handle the onclick event. If you use ``d }); }); +Automatic Account Creation +-------------------------- + +``django-browserid`` will automatically create a user account for new users if the setting ``BROWSERID_CREATE_USER`` is set to ``True`` in ``settings.py``. The user account will be created with the verified email returned from the BrowserID verification service, and a URL safe base64 encoded SHA1 of the email with the padding removed as the username. + +To provide a customized username, you can provide a different algorytm via your settings.py. :: + + # settings.py + BROWSERID_CREATE_USER = True + def username(email): + return email.split('@')[0] + BROWSERID_USERNAME_ALGO = username + +You can __disable account creation__, but continue to use the ``browserid_verify`` view to authenticate existing users with the following: :: + + BROWSERID_CREATE_USER = False + Creating User Accounts ---------------------- -``django-browserid`` will automatically create a user account for new users if the setting ``BROWSERID_CREATE_USER`` is set to ``True`` in ``settings.py``. The user account will be created with the verified email returned from the BrowserID verification service, and a URL safe base64 encoded SHA1 of the email with the padding removed as the username. - -If you do not wish to automatically create user accounts, you may manually verify a BrowserID assertion with something like the following: :: +If you want full control over account creation, don't use django-browserid's browserid_verify view. Create your own view and use ``verify`` to manually verify a BrowserID assertion with something like the following: :: from django_browserid.auth import get_audience, verify from django_browserid.forms import BrowserIDForm @@ -131,12 +131,12 @@ If you do not wish to automatically create user accounts, you may manually verif if request.method == 'POST': form = BrowserIDForm(data=request.POST) if not form.is_valid(): - result = verify(form.cleaned_data['assertion'], get_audience()) + result = verify(form.cleaned_data['assertion'], get_audience(request)) if result: # check for user account, create account for new users, etc user = my_get_or_create_user(result.email) -``result`` will be False if the assertion failed, or a dictionary similar to the following: :: +``result`` will be ``False`` if the assertion failed, or a dictionary similar to the following: :: { u'audience': u'https://mysite.com:443', @@ -148,6 +148,24 @@ If you do not wish to automatically create user accounts, you may manually verif You are of course then free to store the email in the session and prompt the user to sign up using a chosen identifier as their username, or whatever else makes sense for your site. +Obscure Options +------- + +Unless your really noodling around with BrowserID, you probably won't need these +optional config in ``settings.py`` (they have sensible defaults): :: + + # URL of a BrowserID verification service. + BROWSERID_VERIFICATION_URL = 'https://browserid.org/verify' + + # Proxy Info, see httplib2 documentation + BROWSERID_PROXY_INFO = None + + # CA cert file for validating SSL ceprtificate + BROWSERID_CACERT_FILE = None + + # Disable SSL cert validation + BROWSERID_DISABLE_CERT_CHECK = False + License ------- diff --git a/django_browserid/auth.py b/django_browserid/auth.py index 5e63124..0175840 100644 --- a/django_browserid/auth.py +++ b/django_browserid/auth.py @@ -47,30 +47,44 @@ def get_audience(request): """ site_url = getattr(settings, 'SITE_URL', False) + # Note audience based on request for developer warnings + if request.is_secure(): + req_proto = 'https://' + else: + req_proto = 'http://' + req_domain = request.get_host() + # If we don't define it explicitly if not site_url: - if request.is_secure(): - req_proto = 'https://' - else: - req_proto = 'http://' protocol = getattr(settings, 'PROTOCOL', req_proto) - req_domain = request.get_host() if not getattr(settings, 'DOMAIN'): log.warning('django-browserid WARNING you are missing ' 'settings.SITE_URL. This is not a secure way ' 'to verify assertions. Please fix me. ' 'Setting domain to %s.' % req_domain) - domain = getattr(settings, 'DOMAIN', req_domain) + # DOMAIN is example.com req_domain is example.com:8001 + domain = getattr(settings, 'DOMAIN', req_domain.split(':')[0]) + standards = {'https://': 443, 'http://': 80} - port = getattr(settings, 'PORT', standards[protocol]) + if ':' in req_domain: + req_port = req_domain.split(':')[1] + else: + req_port = None + port = getattr(settings, 'PORT', req_port or standards[protocol]) if port == standards[protocol]: site_url = ''.join(map(str, (protocol, domain))) else: site_url = ''.join(map(str, (protocol, domain, ':', port))) + req_url = "%s%s" % (req_proto, req_domain) + if site_url != "%s%s" % (req_proto, req_domain): + log.warning('Misconfigured SITE_URL? settings has [%s], but ' + 'actual request was [%s] BrowserID may fail on ' + 'audience' % (site_url, req_url)) return site_url + def default_username_algo(email): # store the username as a base64 encoded sha1 of the email address # this protects against data leakage because usernames are often diff --git a/django_browserid/tests/test_verification.py b/django_browserid/tests/test_verification.py index 9dd05ed..a9063b3 100644 --- a/django_browserid/tests/test_verification.py +++ b/django_browserid/tests/test_verification.py @@ -83,7 +83,8 @@ def test_authenticate_create_user(fake): """Test that automatic user creation works when enabled.""" with positive_assertion(fake): setattr(settings, 'BROWSERID_CREATE_USER', True) - delattr(settings, 'BROWSERID_USERNAME_ALGO') + if getattr(settings, 'BROWSERID_USERNAME_ALGO', None): + delattr(settings, 'BROWSERID_USERNAME_ALGO') user = auth.authenticate(**authenticate_kwargs) # user should have been created assert user