allura-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From brond...@apache.org
Subject [01/10] allura git commit: [#8117] tests
Date Tue, 06 Sep 2016 14:50:34 GMT
Repository: allura
Updated Branches:
  refs/heads/master a733a05e7 -> 9ecc62604


[#8117] tests


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/55eb7341
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/55eb7341
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/55eb7341

Branch: refs/heads/master
Commit: 55eb73415e97e766c476c04630bfa0b97b13a043
Parents: 0354d74
Author: Dave Brondsema <dave@brondsema.net>
Authored: Mon Aug 29 17:22:06 2016 -0400
Committer: Dave Brondsema <dave@brondsema.net>
Committed: Tue Sep 6 10:38:51 2016 -0400

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py           |   4 +-
 Allura/allura/lib/multifactor.py            |   8 +-
 Allura/allura/lib/phone/__init__.py         |   1 +
 Allura/allura/lib/plugin.py                 |  17 +-
 Allura/allura/lib/security.py               |   5 +-
 Allura/allura/lib/spam/__init__.py          |   4 +-
 Allura/allura/model/auth.py                 |   1 -
 Allura/allura/templates/user_prefs.html     |   2 +-
 Allura/allura/tests/decorators.py           |   6 +-
 Allura/allura/tests/functional/test_auth.py | 224 +++++++++++++++++++++++
 Allura/allura/tests/test_helpers.py         |  13 +-
 Allura/allura/tests/test_multifactor.py     | 116 +++++++++++-
 AlluraTest/alluratest/validation.py         |   9 +
 13 files changed, 395 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 93a0875..7dbfa1b 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -246,7 +246,7 @@ class AuthController(BaseController):
                 em.send_verification_link()
             flash('User "%s" registered. Verification link was sent to your email.' % username)
         else:
-            plugin.AuthenticationProvider.get(request).login(user)  # TODO test this flow
+            plugin.AuthenticationProvider.get(request).login(user)
             flash('User "%s" registered' % username)
         redirect('/')
 
@@ -337,7 +337,7 @@ class AuthController(BaseController):
     def do_multifactor(self, code, **kwargs):
         if 'multifactor-username' not in session:
             tg.flash('Your multifactor login was disrupted, please start over.', 'error')
-            redirect('/auth', return_to=kwargs.get('return_to', ''))
+            redirect('/auth/', return_to=kwargs.get('return_to', ''))
 
         user = M.User.by_username(session['multifactor-username'])
         totp_service = TotpService.get()

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/lib/multifactor.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/multifactor.py b/Allura/allura/lib/multifactor.py
index a558750..9970893 100644
--- a/Allura/allura/lib/multifactor.py
+++ b/Allura/allura/lib/multifactor.py
@@ -53,6 +53,9 @@ class TotpService(object):
 
     @classmethod
     def get(cls):
+        '''
+        :rtype: TotpService
+        '''
         method = config.get('auth.multifactor.totp.service', 'mongodb')
         return g.entry_points['multifactor_totp'][method]()
 
@@ -93,7 +96,6 @@ class TotpService(object):
     def get_totp(self, user):
         '''
         :param user: a :class:`User <allura.model.auth.User>`
-        :param bool generate_new: generate (but does not save) if one does not exist already
         :return:
         '''
         key = self.get_secret_key(user)
@@ -130,8 +132,8 @@ class MongodbTotpService(TotpService):
         if user.is_anonymous():
             return None
         record = M.TotpKey.query.get(user_id=user._id)
-        if record:
-            return record.key
+        if record and record.key:
+            return bytes(record.key)
 
     def set_secret_key(self, user, key):
         from allura import model as M

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/lib/phone/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/phone/__init__.py b/Allura/allura/lib/phone/__init__.py
index 1b14575..dd46f2f 100644
--- a/Allura/allura/lib/phone/__init__.py
+++ b/Allura/allura/lib/phone/__init__.py
@@ -66,6 +66,7 @@ class PhoneService(object):
     def get(cls, config, entry_points):
         """
         Return an instance of PhoneService implementation based on ``config``.
+        :rtype: PhoneService
         """
         method = config.get('phone.method')
         if not method:

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 832b5c4..7127120 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -89,7 +89,10 @@ class AuthenticationProvider(object):
 
     @classmethod
     def get(cls, request):
-        '''returns the AuthenticationProvider instance for this request'''
+        '''
+        returns the AuthenticationProvider instance for this request
+        :rtype: AuthenticationProvider
+        '''
         try:
             result = cls._loaded_ep
         except AttributeError:
@@ -689,6 +692,9 @@ class ProjectRegistrationProvider(object):
 
     @classmethod
     def get(cls):
+        '''
+        :rtype: ProjectRegistrationProvider
+        '''
         from allura.lib import app_globals
         method = config.get('registration.method', 'local')
         return app_globals.Globals().entry_points['registration'][method]()
@@ -1292,6 +1298,9 @@ class ThemeProvider(object):
 
     @classmethod
     def get(cls):
+        '''
+        :rtype: ThemeProvider
+        '''
         name = config.get('theme', 'allura')
         return g.entry_points['theme'][name]()
 
@@ -1381,6 +1390,9 @@ class UserPreferencesProvider(object):
 
     @classmethod
     def get(cls):
+        '''
+        :rtype: UserPreferencesProvider
+        '''
         method = config.get('user_prefs_storage.method', 'local')
         return g.entry_points['user_prefs'][method]()
 
@@ -1580,6 +1592,9 @@ class ImportIdConverter(object):
 
     @classmethod
     def get(cls):
+        '''
+        :rtype: ImportIdConverter
+        '''
         converter = config.get('import_id_converter')
         if converter:
             return g.entry_points['allura.import_id_converter'][converter]()

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/lib/security.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index b26a23c..bd98c9a 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -51,7 +51,10 @@ class Credentials(object):
 
     @classmethod
     def get(cls):
-        'get the global :class:`Credentials` instance'
+        '''
+        get the global :class:`Credentials` instance
+        :rtype: Credentials
+        '''
         import allura
         return allura.credentials
 

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/lib/spam/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/__init__.py b/Allura/allura/lib/spam/__init__.py
index 4bf3917..3d4fe7f 100644
--- a/Allura/allura/lib/spam/__init__.py
+++ b/Allura/allura/lib/spam/__init__.py
@@ -42,7 +42,9 @@ class SpamFilter(object):
 
     @classmethod
     def get(cls, config, entry_points):
-        """Return an instance of the SpamFilter impl specified in ``config``.
+        """
+        Return an instance of the SpamFilter impl specified in ``config``.
+        :rtype: SpamFilter
         """
         method = config.get('spam.method')
         if not method:

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index fa26353..c1c8235 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -302,7 +302,6 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
         email_format=str,
         disable_user_messages=bool,
         multifactor=bool,
-        #totp=S.Binary,
     ))
     # Additional top-level fields can/should be accessed with get/set_pref also
     # Not sure why we didn't put them within the 'preferences' dictionary :(

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 66450e5..59a5497 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -123,7 +123,7 @@
             </a></p>
             {% if user_multifactor %}
                 <p><b class="fa fa-qrcode"></b> <a href="totp_view">View
existing configuration</a></p>
-                <form action="multifactor_disable" method="post">
+                <form action="multifactor_disable" id="multifactor_disable" method="post">
                 <p>
                     <b class="fa fa-trash"></b> <a href="#" class="disable">Disable</a>
                 </p>

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/tests/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/decorators.py b/Allura/allura/tests/decorators.py
index 46369d1..e4e18ba 100644
--- a/Allura/allura/tests/decorators.py
+++ b/Allura/allura/tests/decorators.py
@@ -171,8 +171,10 @@ def audits(*messages, **kwargs):
     else:
         preamble = ''
     for message in messages:
-        assert M.AuditLog.query.find(dict(
-            message=re.compile(preamble + message))).count(), 'Could not find "%s"' % message
+        assert M.AuditLog.query.find(dict(message=re.compile(preamble + message))).count(),
\
+            'Could not find "%s"%s' % (message,
+                                       '\nYou may need to escape the regex chars in the text
you are matching'
+                                       if message != re.escape(message) else '')
 
 
 @contextlib.contextmanager

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index 38e57ad..3483e6f 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -17,9 +17,13 @@
 
 import calendar
 from datetime import datetime, time, timedelta
+from time import time as time_time
 import json
 from urlparse import urlparse, parse_qs
 from urllib import urlencode
+
+from allura.lib.multifactor import TotpService
+from allura.tests.decorators import audits, out_audits
 from bson import ObjectId
 
 import re
@@ -1985,3 +1989,223 @@ class TestCSRFProtection(TestController):
     def test_token_present_on_first_request(self):
         r = self.app.get('/auth/')
         assert_true(r.form['_session_id'].value)
+
+
+class TestTwoFactor(TestController):
+
+    sample_key = b'\x00K\xda\xbfv\xc2B\xaa\x1a\xbe\xa5\x96b\xb2\xa0Z:\xc9\xcf\x8a'
+
+    def _init_totp(self, username='test-admin'):
+        user = M.User.query.get(username=username)
+        totp_srv = TotpService().get()
+        totp_srv.set_secret_key(user, self.sample_key)
+        user.set_pref('multifactor', True)
+
+    def test_settings_on(self):
+        r = self.app.get('/auth/preferences/')
+        assert r.html.find(attrs={'class': 'preferences multifactor'})
+
+    def test_settings_off(self):
+        with h.push_config(config, **{'auth.multifactor.totp': 'false'}):
+            r = self.app.get('/auth/preferences/')
+        assert not r.html.find(attrs={'class': 'preferences multifactor'})
+
+    def test_user_disabled(self):
+        r = self.app.get('/auth/preferences/')
+        info_html = str(r.html.find(attrs={'class': 'preferences multifactor'}))
+        assert_in('disabled', info_html)
+
+    def test_user_enabled(self):
+        self._init_totp()
+        r = self.app.get('/auth/preferences/')
+        info_html = str(r.html.find(attrs={'class': 'preferences multifactor'}))
+        assert_in('enabled', info_html)
+
+    def test_reconfirm_auth(self):
+        from datetime import datetime as real_datetime
+        with patch('allura.lib.decorators.datetime') as datetime:
+            datetime.min = real_datetime.min
+
+            # reconfirm required at first
+            datetime.utcnow.return_value = real_datetime(2016, 1, 1, 0, 0, 0)
+            r = self.app.get('/auth/preferences/totp_new')
+            assert_in('Password Confirmation', r)
+
+            # submit form, and its not required
+            r.form['password'] = 'foo'
+            r = r.form.submit()
+            assert_not_in('Password Confirmation', r)
+
+            # still not required
+            datetime.utcnow.return_value = real_datetime(2016, 1, 1, 0, 0, 45)
+            r = self.app.get('/auth/preferences/totp_new')
+            assert_not_in('Password Confirmation', r)
+
+            # required later
+            datetime.utcnow.return_value = real_datetime(2016, 1, 1, 0, 1, 3)
+            r = self.app.get('/auth/preferences/totp_new')
+            assert_in('Password Confirmation', r)
+
+    def test_enable_totp(self):
+        with out_audits(user=True):
+            r = self.app.get('/auth/preferences/totp_new')
+            assert_in('Password Confirmation', r)
+
+        with audits('Visited multifactor new TOTP page', user=True):
+            r.form['password'] = 'foo'
+            r = r.form.submit()
+            assert_in('Scan this barcode', r)
+
+        first_key_shown = r.session['totp_new_key']
+
+        with audits('Failed to set up multifactor TOTP \(wrong code\)', user=True):
+            r.form['code'] = ''
+            r = r.form.submit()
+            assert_in('Invalid', r)
+            assert_equal(first_key_shown, r.session['totp_new_key'])  # different keys on
each pageload would be bad!
+
+        new_totp = TotpService().Totp(r.session['totp_new_key'])
+        code = new_totp.generate(time_time())
+        r.form['code'] = code
+        with audits('Set up multifactor TOTP', user=True):
+            r = r.form.submit()
+            assert_equal('Two factor authentication has now been set up.', json.loads(self.webflash(r))['message'],
+                         self.webflash(r))
+
+    def test_reset_totp(self):
+        self._init_totp()
+
+        # access page
+        r = self.app.get('/auth/preferences/totp_new')
+        assert_in('Password Confirmation', r)
+
+        # reconfirm password to get to it
+        r.form['password'] = 'foo'
+        r = r.form.submit()
+
+        # confirm warning message, and key is not changed yet
+        assert_in('Scan this barcode', r)
+        assert_in('this will invalidate your previous', r)
+        current_key = TotpService.get().get_secret_key(M.User.query.get(username='test-admin'))
+        assert_equal(self.sample_key, current_key)
+
+        # incorrect submission
+        r.form['code'] = ''
+        r = r.form.submit()
+        assert_in('Invalid', r)
+
+        # still unchanged key
+        current_key = TotpService.get().get_secret_key(M.User.query.get(username='test-admin'))
+        assert_equal(self.sample_key, current_key)
+
+        # valid submission
+        new_key = r.session['totp_new_key']
+        new_totp = TotpService().Totp(new_key)
+        code = new_totp.generate(time_time())
+        r.form['code'] = code
+        r = r.form.submit()
+        assert_equal('Two factor authentication has now been set up.', json.loads(self.webflash(r))['message'],
+                     self.webflash(r))
+
+        # new key in place
+        current_key = TotpService.get().get_secret_key(M.User.query.get(username='test-admin'))
+        assert_equal(new_key, current_key)
+        assert_not_equal(self.sample_key, current_key)
+
+    def test_disable(self):
+        self._init_totp()
+
+        self.app.get('/auth/multifactor_disable', status=404)  # GET not allowed
+
+        # get form and submit
+        r = self.app.get('/auth/preferences/')
+        form = r.forms['multifactor_disable']
+        r = form.submit()
+
+        # confirm first, no change
+        assert_in('Password Confirmation', r)
+        user = M.User.query.get(username='test-admin')
+        assert_equal(user.get_pref('multifactor'), True)
+
+        # confirm submit, everything goes off
+        r.form['password'] = 'foo'
+        with audits('Disabled multifactor TOTP', user=True):
+            r = r.form.submit()
+            assert_equal('Multifactor authentication has now been disabled.', json.loads(self.webflash(r))['message'],
+                         self.webflash(r))
+        user = M.User.query.get(username='test-admin')
+        assert_equal(user.get_pref('multifactor'), False)
+        assert_equal(TotpService().get().get_secret_key(user), None)
+
+    def test_login_totp(self):
+        self._init_totp()
+
+        # so test-admin isn't automatically logged in for all requests
+        self.app.extra_environ = {'disable_auth_magic': 'True'}
+
+        # regular login
+        r = self.app.get('/auth/?return_to=/p/foo')
+        r.form['username'] = 'test-admin'
+        r.form['password'] = 'foo'
+        r = r.form.submit()
+
+        # check results
+        assert r.location.endswith('/auth/multifactor?return_to=%2Fp%2Ffoo'), r
+        r = r.follow()
+        assert not r.session.get('username')
+
+        # try an invalid code
+        r.form['code'] = 'invalid-code'
+        r = r.form.submit()
+        assert_in('Invalid code', r)
+        assert not r.session.get('username')
+
+        # use a valid code
+        totp = TotpService().Totp(self.sample_key)
+        code = totp.generate(time_time())
+        r.form['code'] = code
+        r = r.form.submit()
+
+        # confirm login and final page
+        assert_equal(r.session['username'], 'test-admin')
+        assert r.location.endswith('/p/foo'), r
+
+    def test_login_totp_disrupted(self):
+        self._init_totp()
+
+        # so test-admin isn't automatically logged in for all requests
+        self.app.extra_environ = {'disable_auth_magic': 'True'}
+
+        # regular login
+        r = self.app.get('/auth/')
+        r.form['username'] = 'test-admin'
+        r.form['password'] = 'foo'
+        r = r.form.submit()
+        r = r.follow()
+
+        # go to some other page instead of filling out the 2FA code
+        other_r = self.app.get('/')
+
+        # then try to complete the 2FA form
+        totp = TotpService().Totp(self.sample_key)
+        code = totp.generate(time_time())
+        r.form['code'] = code
+        r = r.form.submit()
+
+        # sent back to regular login
+        assert_equal('Your multifactor login was disrupted, please start over.', json.loads(self.webflash(r))['message'],
+                     self.webflash(r))
+        r = r.follow()
+        assert_in('Password Login', r)
+
+    def test_view_key(self):
+        self._init_totp()
+
+        with out_audits(user=True):
+            r = self.app.get('/auth/preferences/totp_view')
+            assert_in('Password Confirmation', r)
+
+        with audits('Viewed multifactor TOTP config page', user=True):
+            r.form['password'] = 'foo'
+            r = r.form.submit()
+            assert_in('Scan this barcode', r)

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/tests/test_helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 296a7c3..cb7e1a6 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -22,6 +22,7 @@ from os import path
 from datetime import datetime, timedelta
 import time
 
+import PIL
 from mock import Mock, patch
 from pylons import tmpl_context as c
 from nose.tools import eq_, assert_equals
@@ -82,6 +83,7 @@ def test_escape_json():
     outputdata = h.escape_json(inputdata)
     assert_equals(outputdata, outputsample)
 
+
 def test_really_unicode():
     here_dir = path.dirname(__file__)
     s = h.really_unicode('\xef\xbb\xbf<?xml version="1.0" encoding="utf-8" ?>')
@@ -585,4 +587,13 @@ def test_convert_bools():
     assert_equals(h.convert_bools({'foo': 'true', 'baz': ' TRUE '}),
                   {'foo': True, 'baz': True})
     assert_equals(h.convert_bools({'foo': 'true', 'baz': ' TRUE '}, prefix='ba'),
-                  {'foo': 'true', 'baz': True})
\ No newline at end of file
+                  {'foo': 'true', 'baz': True})
+
+
+def test_base64uri():
+    img_file = path.join(path.dirname(__file__), 'data', 'user.png')
+    with open(img_file) as img_file_handle:
+        img = PIL.Image.open(img_file_handle)
+        b64img = h.base64uri(img)
+        assert b64img.startswith('data:image/png;base64,'), b64img[:100]
+        assert len(b64img) > 500

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/Allura/allura/tests/test_multifactor.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_multifactor.py b/Allura/allura/tests/test_multifactor.py
index b531807..c3b8f97 100644
--- a/Allura/allura/tests/test_multifactor.py
+++ b/Allura/allura/tests/test_multifactor.py
@@ -17,10 +17,17 @@
 
 
 import textwrap
+import os
 
-from nose.tools import assert_equal
+import bson
+import ming
+from cryptography.hazmat.primitives.twofactor import InvalidToken
+from mock import patch, Mock
+from nose.tools import assert_equal, assert_raises
+from tg import config
 
-from allura.lib.multifactor import GoogleAuthenticatorFile
+from allura.lib.multifactor import GoogleAuthenticatorFile, TotpService, MongodbTotpService
+from allura.lib.multifactor import GoogleAuthenticatorPamFilesystemTotpService
 
 
 class TestGoogleAuthenticatorFile(object):
@@ -62,3 +69,108 @@ class TestGoogleAuthenticatorFile(object):
     def test_dump2(self):
         gaf = GoogleAuthenticatorFile.load(self.sample2)
         assert_equal(gaf.dump(), self.sample2)
+
+
+class TestTotpService(object):
+
+    sample_key = b'\x00K\xda\xbfv\xc2B\xaa\x1a\xbe\xa5\x96b\xb2\xa0Z:\xc9\xcf\x8a'
+    sample_time = 1472502664
+    # these generate code 283397
+
+    def test_constructor(self):
+        totp = TotpService().Totp(key=None)
+        assert totp
+
+    @patch('allura.lib.multifactor.time')
+    def test_verify_types(self, time):
+        time.return_value = self.sample_time
+        srv = TotpService()
+        totp = srv.Totp(key=self.sample_key)
+        srv.verify(totp, u'283 397')
+        srv.verify(totp, b'283397')
+
+    @patch('allura.lib.multifactor.time')
+    def test_verify_window(self, time):
+        time.return_value = self.sample_time
+        srv = TotpService()
+        totp = srv.Totp(key=self.sample_key)
+        srv.verify(totp, b'283397')
+
+        time.return_value = self.sample_time + 30
+        srv.verify(totp, b'283397')
+
+        time.return_value = self.sample_time + 60
+        with assert_raises(InvalidToken):
+            srv.verify(totp, b'283397')
+
+        time.return_value = self.sample_time - 30
+        with assert_raises(InvalidToken):
+            srv.verify(totp, b'283397')
+
+    def test_get_qr_code(self):
+        srv = TotpService()
+        totp = srv.Totp(key=None)
+        user = Mock(username='some-user-guy')
+        config['site_name'] = 'Our Website'
+        assert srv.get_qr_code(totp, user)
+
+
+class TestMongodbTotpService():
+    sample_key = b'\x00K\xda\xbfv\xc2B\xaa\x1a\xbe\xa5\x96b\xb2\xa0Z:\xc9\xcf\x8a'
+
+    def setUp(self):
+        config = {
+            'ming.main.uri': 'mim://allura_test',
+        }
+        ming.configure(**config)
+
+    def test_none(self):
+        srv = MongodbTotpService()
+        user = Mock(_id=bson.ObjectId(),
+                    is_anonymous=lambda: False,
+                    )
+        assert_equal(None, srv.get_secret_key(user))
+
+    def test_set_get(self):
+        srv = MongodbTotpService()
+        user = Mock(_id=bson.ObjectId(),
+                    is_anonymous=lambda: False,
+                    )
+        srv.set_secret_key(user, self.sample_key)
+        assert_equal(self.sample_key, srv.get_secret_key(user))
+
+    def test_delete(self):
+        srv = MongodbTotpService()
+        user = Mock(_id=bson.ObjectId(),
+                    is_anonymous=lambda: False,
+                    )
+        srv.set_secret_key(user, self.sample_key)
+        assert_equal(self.sample_key, srv.get_secret_key(user))
+        srv.set_secret_key(user, None)
+        assert_equal(None, srv.get_secret_key(user))
+
+
+class TestGoogleAuthenticatorPamFilesystemTotpService():
+    sample_key = b'\x00K\xda\xbfv\xc2B\xaa\x1a\xbe\xa5\x96b\xb2\xa0Z:\xc9\xcf\x8a'
+
+    def setUp(self):
+        config['auth.multifactor.totp.filesystem.basedir'] = os.path.join(os.getenv('TMPDIR',
'/tmp'), 'totp-test')
+
+    def test_none(self):
+        srv = GoogleAuthenticatorPamFilesystemTotpService()
+        user = Mock(username='some-user-guy')
+        assert_equal(None, srv.get_secret_key(user))
+
+    def test_set_get(self):
+        srv = GoogleAuthenticatorPamFilesystemTotpService()
+        user = Mock(username='some-user-guy')
+        srv.set_secret_key(user, self.sample_key)
+        assert_equal(self.sample_key, srv.get_secret_key(user))
+
+    def test_delete(self):
+        srv = GoogleAuthenticatorPamFilesystemTotpService()
+        user = Mock(username='some-user-guy')
+        srv.set_secret_key(user, self.sample_key)
+        assert_equal(self.sample_key, srv.get_secret_key(user))
+        srv.set_secret_key(user, None)
+        assert_equal(None, srv.get_secret_key(user))

http://git-wip-us.apache.org/repos/asf/allura/blob/55eb7341/AlluraTest/alluratest/validation.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/validation.py b/AlluraTest/alluratest/validation.py
index b6252a2..fba20af 100644
--- a/AlluraTest/alluratest/validation.py
+++ b/AlluraTest/alluratest/validation.py
@@ -296,6 +296,9 @@ class ValidatingTestApp(PostParamCheckingTestApp):
         return params, kw
 
     def get(self, *args, **kw):
+        '''
+        :rtype: webtest.app.TestResponse
+        '''
         val_params, kw = self._get_validation_params(kw)
         resp = super(ValidatingTestApp, self).get(*args, **kw)
         if not self.validate_skip and not val_params['validate_skip']:
@@ -303,6 +306,9 @@ class ValidatingTestApp(PostParamCheckingTestApp):
         return resp
 
     def post(self, *args, **kw):
+        '''
+        :rtype: webtest.app.TestResponse
+        '''
         val_params, kw = self._get_validation_params(kw)
         resp = super(ValidatingTestApp, self).post(*args, **kw)
         if not self.validate_skip and not val_params['validate_skip']:
@@ -310,6 +316,9 @@ class ValidatingTestApp(PostParamCheckingTestApp):
         return resp
 
     def delete(self, *args, **kw):
+        '''
+        :rtype: webtest.app.TestResponse
+        '''
         val_params, kw = self._get_validation_params(kw)
         resp = super(ValidatingTestApp, self).delete(*args, **kw)
         if not self.validate_skip and not val_params['validate_skip']:


Mime
View raw message