Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

hook up auth verify to a session and use that for the pin (bug 805673)

  • Loading branch information...
commit d717465aa91540a1d419e8b27f520534340b0e78 1 parent 7f111a8
@andymckay andymckay authored
View
1  requirements/prod.txt
@@ -23,6 +23,7 @@ nose==1.2.1
ordereddict==1.1
django-picklefield==0.2.1
python-dateutil==2.1
+python-memcached==1.48
requests==0.14.0
schematic==0.2
six==1.2.0
View
1  settings_test.py
@@ -1,7 +1,6 @@
# This file overrides what's in your webpay/settings/local.py while
# testing.
-
# This tells the runner to skip the Solitude client lib tests.
# If you set this to a URL it should look like http://localhost:9000
# but you probably don't want to use your local dev server.
View
17 webpay/auth/decorators.py
@@ -0,0 +1,17 @@
+import functools
+
+import commonware.log
+
+from django.core.exceptions import PermissionDenied
+
+log = commonware.log.getLogger('w.auth')
+
+
+def user_verified(f):
+ @functools.wraps(f)
+ def wrapper(request, *args, **kw):
+ if not request.session.get('uuid'):
+ log.error('No uuid in session, not verified.')
+ raise PermissionDenied
+ return f(request, *args, **kw)
+ return wrapper
View
1  webpay/auth/tests/__init__.py
@@ -0,0 +1 @@
+from test import SessionTestCase
View
78 webpay/auth/tests/test.py
@@ -0,0 +1,78 @@
+from django import test
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.http import HttpRequest
+from django.utils.importlib import import_module
+
+import mock
+from nose.tools import eq_
+
+good_assertion = {u'status': u'okay',
+ u'audience': u'http://some.site',
+ u'expires': 1351707833170,
+ u'email': u'a@a.com',
+ u'issuer': u'login.persona.org'}
+
+
+class SessionTestCase(test.TestCase):
+ """
+ A wrapper around Django tests to provide a verify method for use
+ in testing.
+ """
+
+ def verify(self, uuid):
+ # This is a rip off of the Django test client login.
+ engine = import_module(settings.SESSION_ENGINE)
+
+ # Create a fake request to store login details.
+ request = HttpRequest()
+ request.session = engine.SessionStore()
+
+ request.session['uuid'] = uuid
+ request.session.save()
+
+ # Set the cookie to represent the session.
+ session_cookie = settings.SESSION_COOKIE_NAME
+ self.client.cookies[session_cookie] = request.session.session_key
+ cookie_data = {
+ 'max-age': None,
+ 'path': '/',
+ 'domain': settings.SESSION_COOKIE_DOMAIN,
+ 'secure': settings.SESSION_COOKIE_SECURE or None,
+ 'expires': None,
+ }
+ self.client.cookies[session_cookie].update(cookie_data)
+
+ def unverify(self):
+ # Remove the browserid verification.
+ del self.client.cookies[settings.SESSION_COOKIE_NAME]
+
+
+@mock.patch.object(settings, 'DOMAIN', 'web.pay')
+class TestAuth(SessionTestCase):
+
+ def setUp(self):
+ self.url = reverse('auth.verify')
+
+ @mock.patch('webpay.auth.views.verify_assertion')
+ def test_good(self, verify_assertion):
+ verify_assertion.return_value = good_assertion
+ eq_(self.client.post(self.url, {'assertion': 'good'}).status_code, 200)
+
+ @mock.patch('webpay.auth.views.verify_assertion')
+ def test_session(self, verify_assertion):
+ verify_assertion.return_value = good_assertion
+ self.client.post(self.url, {'assertion': 'good'})
+ assert self.client.session['uuid'].startswith('web.pay:')
+
+ @mock.patch('webpay.auth.views.verify_assertion')
+ def test_bad(self, verify_assertion):
+ verify_assertion.return_value = False
+ eq_(self.client.post(self.url, {'assertion': 'bad'}).status_code, 400)
+
+ @mock.patch('webpay.auth.views.verify_assertion')
+ def test_session_cleaned(self, verify_assertion):
+ self.verify('a:b')
+ verify_assertion.return_value = False
+ eq_(self.client.post(self.url, {'assertion': 'bad'}).status_code, 400)
+ eq_(self.client.session.get('uuid'), None)
View
21 webpay/auth/tests/test_utils_.py
@@ -0,0 +1,21 @@
+from django import test
+from django.conf import settings
+
+import mock
+from webpay.auth.utils import get_uuid
+
+
+@mock.patch.object(settings, 'DOMAIN', 'web.pay')
+class TestUUID(test.TestCase):
+
+ def test_good(self):
+ res = get_uuid('f@f.com')
+ assert res.startswith('web.pay:')
+
+ def test_unicode(self):
+ res = get_uuid(u'f@f.com')
+ assert res.startswith('web.pay:')
+
+ def test_bad(self):
+ with self.assertRaises(ValueError):
+ get_uuid(None)
View
30 webpay/auth/utils.py
@@ -0,0 +1,30 @@
+import hashlib
+
+from django.conf import settings
+
+
+def get_uuid(email):
+ """
+ Given an email returns the hash of the email for this site. This will be
+ consistent for each email for this site and can be used as the uuid in
+ solitude. Because the leakage of the email is more of a privacy concern
+ than a security concern, we are just doing a simple sha1 hash.
+
+ :email: the email to hash.
+ """
+ if not isinstance(email, basestring):
+ raise ValueError('get_uuid requires a string or unicode')
+ hashed = hashlib.sha1()
+ hashed.update(email)
+ return '%s:%s' % (settings.DOMAIN, hashed.hexdigest())
+
+
+def get_user(request):
+ try:
+ return request.session.get('uuid')
+ except KeyError:
+ raise KeyError('Attempt to access user without it being set, '
+ 'did you use the user_verified decorator?')
+
+def set_user(request, email):
+ request.session['uuid'] = get_uuid(email)
View
6 webpay/auth/views.py
@@ -6,7 +6,9 @@
from django_browserid.forms import BrowserIDForm
from session_csrf import anonymous_csrf_exempt
-log = commonware.log.getLogger('w.pay')
+from utils import set_user
+
+log = commonware.log.getLogger('w.auth')
@anonymous_csrf_exempt
@@ -19,6 +21,8 @@ def verify(request):
get_audience(request))
if result:
log.info('assertion ok: %s' % result)
+ set_user(request, result['email'])
return http.HttpResponse('ok')
+ request.session.clear()
return http.HttpResponseBadRequest()
View
23 webpay/pin/tests/test_views.py
@@ -1,24 +1,29 @@
from django.core.urlresolvers import reverse
-from django.test import TestCase
from mock import patch
from nose.tools import eq_
from lib.solitude.api import client
from lib.solitude.errors import ERROR_STRINGS
+from webpay.auth.tests import SessionTestCase
from webpay.pay import get_payment_url
-class PinViewTestCase(TestCase):
+class PinViewTestCase(SessionTestCase):
url_name = ''
def setUp(self):
self.url = reverse(self.url_name)
+ self.verify('a:b')
class CreatePinViewTest(PinViewTestCase):
url_name = 'pin.create'
+ def test_unauth(self):
+ self.unverify()
+ eq_(self.client.post(self.url, data={'pin': '1234'}).status_code, 403)
+
@patch('lib.solitude.api.client.create_buyer', auto_spec=True)
@patch('lib.solitude.api.client.change_pin', auto_spec=True)
@patch.object(client, 'get_buyer', lambda x: {})
@@ -76,6 +81,10 @@ def test_buyer_does_exist_with_alpha_pin(self, create_buyer):
class VerifyPinViewTest(PinViewTestCase):
url_name = 'pin.verify'
+ def test_unauth(self):
+ self.unverify()
+ eq_(self.client.post(self.url, data={'pin': '1234'}).status_code, 403)
+
@patch.object(client, 'verify_pin', lambda x, y: True)
def test_good_pin(self):
res = self.client.post(self.url, data={'pin': '1234'})
@@ -86,10 +95,20 @@ def test_bad_pin(self):
res = self.client.post(self.url, data={'pin': '1234'})
assert not 'Success' in res.content
+ @patch.object(client, 'verify_pin')
+ def test_uuid_used(self, verify_pin):
+ verify_pin.return_value = True
+ self.client.post(self.url, data={'pin': '1234'})
+ eq_(verify_pin.call_args[0][0], 'a:b')
+
class ChangePinViewTest(PinViewTestCase):
url_name = 'pin.change'
+ def test_unauth(self):
+ self.unverify()
+ eq_(self.client.post(self.url, data={'pin': '1234'}).status_code, 403)
+
@patch('lib.solitude.api.client.change_pin', auto_spec=True)
@patch.object(client, 'verify_pin', lambda x, y: True)
@patch.object(client, 'get_buyer', lambda x: {'uuid': x})
View
28 webpay/pin/views.py
@@ -1,22 +1,24 @@
from django import http
+from django.core.exceptions import PermissionDenied
from django.shortcuts import render
+import commonware.log
from session_csrf import anonymous_csrf_exempt
from lib.solitude.api import client
+from webpay.auth.decorators import user_verified
+from webpay.auth.utils import get_user
from webpay.pay import get_payment_url
from . import forms
+log = commonware.log.getLogger('w.pin')
-# TODO(Wraithan): remove all the anonymous once identity is figured out.
-@anonymous_csrf_exempt
+
+@user_verified
def create(request):
form = forms.CreatePinForm()
if request.method == 'POST':
- # TODO(Wraithan): Get the buyer's UUID once identity is figured out
- # with webpay.
- stub_uuid = 'dat:uuid'
- form = forms.CreatePinForm(uuid=stub_uuid, data=request.POST)
+ form = forms.CreatePinForm(uuid=get_user(request), data=request.POST)
if form.is_valid():
if hasattr(form, 'buyer'):
res = client.change_pin(form.buyer, form.cleaned_data['pin'])
@@ -29,27 +31,21 @@ def create(request):
return render(request, 'pin/create.html', {'form': form})
-@anonymous_csrf_exempt
+@user_verified
def verify(request):
form = forms.VerifyPinForm()
if request.method == 'POST':
- # TODO(Wraithan): Get the buyer's UUID once identity is figured out
- # with webpay.
- stub_uuid = 'dat:uuid'
- form = forms.VerifyPinForm(uuid=stub_uuid, data=request.POST)
+ form = forms.VerifyPinForm(uuid=get_user(request), data=request.POST)
if form.is_valid():
return http.HttpResponseRedirect(get_payment_url())
return render(request, 'pin/verify.html', {'form': form})
-@anonymous_csrf_exempt
+@user_verified
def change(request):
form = forms.ChangePinForm()
if request.method == 'POST':
- # TODO(Wraithan): Get the buyer's UUID once identity is figured out
- # with webpay.
- stub_uuid = 'dat:uuid'
- form = forms.ChangePinForm(uuid=stub_uuid, data=request.POST)
+ form = forms.ChangePinForm(uuid=get_user(request), data=request.POST)
if form.is_valid():
res = client.change_pin(form.buyer, form.cleaned_data['pin'])
if form.handle_client_errors(res):
View
2  webpay/settings/base.py
@@ -126,6 +126,8 @@
'mobility.middleware.XMobileMiddleware',
)
+SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
+
# This is the key and secret for purchases, our special marketplace key and
# secret for selling apps.
KEY = 'marketplace' # would typically be a URL
Please sign in to comment.
Something went wrong with that request. Please try again.