merge --squash openid branch to take care of a false merge commit in the
basic_auth branch that openid is forked from Commits squashed together (in reverse chronological order): - do the label thing only for boolean fields - made edit_account to autofocus on the first field - added feature to render_divs where if field.label == '' then it will render form.description the same a render_label - added allow_registration check - refactored create_user - removed verification_key from create_user - removed get_user from openid - cleanup after removing openid from template_env.globals - fix for werkzueg 0.9.1 - cleanup after merge - more tests - restored openid extra_validation just for safety - tests for openid - deleted openid extra_validation - passed next parameter in session for openid - fixed a bug that was deleting the messages - implemented openid store using sqlalchemy - ask openid provider for 'nickname' to prefill username in registration form - refactored delete openid url to work with generic urls such as google and to not allow a user to delete a url if it is there only one and they don't have a pw - refactored login to register user workflow, which fixed a problem where the 'or register with a password link' wasn't showing up when the finish_login view called the register view because there wasn't any redirect. - added the ability to remove openid's - added the ability to add openids to an existing account - refactored start_login and finish_login views - modified edit_account.html to use render_divs - modified gmg/edit/views to behave appropriatly if no password authentication is enabled. moved the update email stuff to it's own funtion to make edit_account view cleaner. edit_account now modifies the form depending on the plugins. - minor typos - added retrieving email from openid provider - moved allow_registration check to a decorator - moved check if auth is enabled to a decorator - changed openid user registration to go through login first - cleanup after merge - modified verification emails to use itsdangerous tokens - added error handling on bad token, fixed route, and added tests - added support for user to change email address - added link to login view openid/password in login template - updated openid get_user function - modified get_user function to take kwargs instead of username - no need for user might be email kwarg in check_login_simple - added gen_password_hash and check_password functions to auth/__init__ - added focus to form input - made imports fully qualified - modified basic_auth.check_login to check that the user has a pw_hash first - changed occurances of form.data['whatever'] to form.whatever.data - convert tabs to spaces in register template, remove unsed templates, and fixed trans tags in templates - in process of openid login. it works, but needs major imporvements - make password field required in basic_auth form - check if password field present in basic_auth create_user - modified openid create_user function - modified models based on Elronds suggestions - changed register form action to a variable to be passed in by the view using the template - openid plugin v0, still need to authenticate via openid. - added a register_user function to be able to use in a plugin's register view, and modified auth/views.register to redirect to openid/register if appropriate. - Modified basic_auth plugin to work with modified auth plugin hooks. Added context variables. Removed basic_auth/tools which was previously renamed to basic_auth/lib. - modified auth/__init__ hooks to work better with multiple plugins. Removed auth/lib.py. And added a basic_extra_verification function that all plugins will use. - added models and migrations for openid plugin
This commit is contained in:
committed by
Christopher Allan Webber
parent
ac0bc6a1e1
commit
5adb906a0a
41
mediagoblin/tests/auth_configs/openid_appconfig.ini
Normal file
41
mediagoblin/tests/auth_configs/openid_appconfig.ini
Normal file
@@ -0,0 +1,41 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
[mediagoblin]
|
||||
direct_remote_path = /test_static/
|
||||
email_sender_address = "notice@mediagoblin.example.org"
|
||||
email_debug_mode = true
|
||||
|
||||
# TODO: Switch to using an in-memory database
|
||||
sql_engine = "sqlite:///%(here)s/user_dev/mediagoblin.db"
|
||||
|
||||
# Celery shouldn't be set up by the application as it's setup via
|
||||
# mediagoblin.init.celery.from_celery
|
||||
celery_setup_elsewhere = true
|
||||
|
||||
[storage:publicstore]
|
||||
base_dir = %(here)s/user_dev/media/public
|
||||
base_url = /mgoblin_media/
|
||||
|
||||
[storage:queuestore]
|
||||
base_dir = %(here)s/user_dev/media/queue
|
||||
|
||||
[celery]
|
||||
CELERY_ALWAYS_EAGER = true
|
||||
CELERY_RESULT_DBURI = "sqlite:///%(here)s/user_dev/celery.db"
|
||||
BROKER_HOST = "sqlite:///%(here)s/user_dev/kombu.db"
|
||||
|
||||
[plugins]
|
||||
[[mediagoblin.plugins.openid]]
|
||||
@@ -14,7 +14,6 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import urlparse
|
||||
import datetime
|
||||
import pkg_resources
|
||||
import pytest
|
||||
|
||||
@@ -236,6 +235,7 @@ def test_authentication_views(test_app):
|
||||
# Make a new user
|
||||
test_user = fixture_add_user(active_user=False)
|
||||
|
||||
|
||||
# Get login
|
||||
# ---------
|
||||
test_app.get('/auth/login/')
|
||||
|
||||
@@ -32,3 +32,4 @@ BROKER_HOST = "sqlite:///%(here)s/user_dev/kombu.db"
|
||||
[[mediagoblin.plugins.httpapiauth]]
|
||||
[[mediagoblin.plugins.piwigo]]
|
||||
[[mediagoblin.plugins.basic_auth]]
|
||||
[[mediagoblin.plugins.openid]]
|
||||
|
||||
372
mediagoblin/tests/test_openid.py
Normal file
372
mediagoblin/tests/test_openid.py
Normal file
@@ -0,0 +1,372 @@
|
||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import urlparse
|
||||
import pkg_resources
|
||||
import pytest
|
||||
import mock
|
||||
|
||||
from openid.consumer.consumer import SuccessResponse
|
||||
|
||||
from mediagoblin import mg_globals
|
||||
from mediagoblin.db.base import Session
|
||||
from mediagoblin.db.models import User
|
||||
from mediagoblin.plugins.openid.models import OpenIDUserURL
|
||||
from mediagoblin.tests.tools import get_app, fixture_add_user
|
||||
from mediagoblin.tools import template
|
||||
|
||||
|
||||
# App with plugin enabled
|
||||
@pytest.fixture()
|
||||
def openid_plugin_app(request):
|
||||
return get_app(
|
||||
request,
|
||||
mgoblin_config=pkg_resources.resource_filename(
|
||||
'mediagoblin.tests.auth_configs',
|
||||
'openid_appconfig.ini'))
|
||||
|
||||
|
||||
class TestOpenIDPlugin(object):
|
||||
def _setup(self, openid_plugin_app, value=True, edit=False, delete=False):
|
||||
if value:
|
||||
response = SuccessResponse(mock.Mock(), mock.Mock())
|
||||
if edit or delete:
|
||||
response.identity_url = u'http://add.myopenid.com'
|
||||
else:
|
||||
response.identity_url = u'http://real.myopenid.com'
|
||||
self._finish_verification = mock.Mock(return_value=response)
|
||||
else:
|
||||
self._finish_verification = mock.Mock(return_value=False)
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._response_email', mock.Mock(return_value=None))
|
||||
@mock.patch('mediagoblin.plugins.openid.views._response_nickname', mock.Mock(return_value=None))
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
def _setup_start(self, openid_plugin_app, edit, delete):
|
||||
if edit:
|
||||
self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
|
||||
'/edit/openid/finish/'))
|
||||
elif delete:
|
||||
self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
|
||||
'/edit/openid/delete/finish/'))
|
||||
else:
|
||||
self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
|
||||
'/auth/openid/login/finish/'))
|
||||
_setup_start(self, openid_plugin_app, edit, delete)
|
||||
|
||||
def test_bad_login(self, openid_plugin_app):
|
||||
""" Test that attempts to login with invalid paramaters"""
|
||||
|
||||
# Test GET request for auth/register page
|
||||
res = openid_plugin_app.get('/auth/register/').follow()
|
||||
|
||||
# Make sure it redirected to the correct place
|
||||
assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
|
||||
|
||||
# Test GET request for auth/login page
|
||||
res = openid_plugin_app.get('/auth/login/')
|
||||
res.follow()
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
|
||||
|
||||
# Test GET request for auth/openid/register page
|
||||
res = openid_plugin_app.get('/auth/openid/register/')
|
||||
res.follow()
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
|
||||
|
||||
# Test GET request for auth/openid/login/finish page
|
||||
res = openid_plugin_app.get('/auth/openid/login/finish/')
|
||||
res.follow()
|
||||
|
||||
# Correct redirect?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
|
||||
|
||||
# Test GET request for auth/openid/login page
|
||||
res = openid_plugin_app.get('/auth/openid/login/')
|
||||
|
||||
# Correct place?
|
||||
assert 'mediagoblin/plugins/openid/login.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# Try to login with an empty form
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/login/', {})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
|
||||
form = context['login_form']
|
||||
assert form.openid.errors == [u'This field is required.']
|
||||
|
||||
# Try to login with wrong form values
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/login/', {
|
||||
'openid': 'not_a_url.com'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
|
||||
form = context['login_form']
|
||||
assert form.openid.errors == [u'Please enter a valid url.']
|
||||
|
||||
# Should be no users in the db
|
||||
assert User.query.count() == 0
|
||||
|
||||
# Phony OpenID URl
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/login/', {
|
||||
'openid': 'http://phoney.myopenid.com/'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
|
||||
form = context['login_form']
|
||||
assert form.openid.errors == [u'Sorry, the OpenID server could not be found']
|
||||
|
||||
def test_login(self, openid_plugin_app):
|
||||
"""Tests that test login and registion with openid"""
|
||||
# Test finish_login redirects correctly when response = False
|
||||
self._setup(openid_plugin_app, False)
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
@mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
|
||||
def _test_non_response():
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/auth/openid/login/', {
|
||||
'openid': 'http://phoney.myopenid.com/'})
|
||||
res.follow()
|
||||
|
||||
# Correct Place?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
|
||||
assert 'mediagoblin/plugins/openid/login.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
_test_non_response()
|
||||
|
||||
# Test login with new openid
|
||||
# Need to clear_test_template_context before calling _setup
|
||||
template.clear_test_template_context()
|
||||
self._setup(openid_plugin_app)
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
@mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
|
||||
def _test_new_user():
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/login/', {
|
||||
'openid': u'http://real.myopenid.com'})
|
||||
|
||||
# Right place?
|
||||
assert 'mediagoblin/auth/register.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
|
||||
register_form = context['register_form']
|
||||
|
||||
# Register User
|
||||
res = openid_plugin_app.post(
|
||||
'/auth/openid/register/', {
|
||||
'openid': register_form.openid.data,
|
||||
'username': u'chris',
|
||||
'email': u'chris@example.com'})
|
||||
res.follow()
|
||||
|
||||
# Correct place?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/u/chris/'
|
||||
assert 'mediagoblin/user_pages/user.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# No need to test if user is in logged in and verification email
|
||||
# awaits, since openid uses the register_user function which is
|
||||
# tested in test_auth
|
||||
|
||||
# Logout User
|
||||
openid_plugin_app.get('/auth/logout')
|
||||
|
||||
# Get user and detach from session
|
||||
test_user = mg_globals.database.User.find_one({
|
||||
'username': u'chris'})
|
||||
Session.expunge(test_user)
|
||||
|
||||
# Log back in
|
||||
# Could not get it to work by 'POST'ing to /auth/openid/login/
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/auth/openid/login/finish/', {
|
||||
'openid': u'http://real.myopenid.com'})
|
||||
res.follow()
|
||||
|
||||
assert urlparse.urlsplit(res.location)[2] == '/'
|
||||
assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# Make sure user is in the session
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
|
||||
session = context['request'].session
|
||||
assert session['user_id'] == unicode(test_user.id)
|
||||
|
||||
_test_new_user()
|
||||
|
||||
# Test register with empty form
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/register/', {})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
|
||||
register_form = context['register_form']
|
||||
|
||||
assert register_form.openid.errors == [u'This field is required.']
|
||||
assert register_form.email.errors == [u'This field is required.']
|
||||
assert register_form.username.errors == [u'This field is required.']
|
||||
|
||||
# Try to register with existing username and email
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/register/', {
|
||||
'openid': 'http://real.myopenid.com',
|
||||
'email': 'chris@example.com',
|
||||
'username': 'chris'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
|
||||
register_form = context['register_form']
|
||||
|
||||
assert register_form.username.errors == [u'Sorry, a user with that name already exists.']
|
||||
assert register_form.email.errors == [u'Sorry, a user with that email address already exists.']
|
||||
assert register_form.openid.errors == [u'Sorry, an account is already registered to that OpenID.']
|
||||
|
||||
def test_add_delete(self, openid_plugin_app):
|
||||
"""Test adding and deleting openids"""
|
||||
# Add user
|
||||
test_user = fixture_add_user(password='')
|
||||
openid = OpenIDUserURL()
|
||||
openid.openid_url = 'http://real.myopenid.com'
|
||||
openid.user_id = test_user.id
|
||||
openid.save()
|
||||
|
||||
# Log user in
|
||||
template.clear_test_template_context()
|
||||
self._setup(openid_plugin_app)
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
@mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
|
||||
def _login_user():
|
||||
openid_plugin_app.post(
|
||||
'/auth/openid/login/finish/', {
|
||||
'openid': u'http://real.myopenid.com'})
|
||||
|
||||
_login_user()
|
||||
|
||||
# Try and delete only OpenID url
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/edit/openid/delete/', {
|
||||
'openid': 'http://real.myopenid.com'})
|
||||
assert 'mediagoblin/plugins/openid/delete.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# Add OpenID to user
|
||||
# Empty form
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/edit/openid/', {})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
|
||||
form = context['form']
|
||||
assert form.openid.errors == [u'This field is required.']
|
||||
|
||||
# Try with a bad url
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/edit/openid/', {
|
||||
'openid': u'not_a_url.com'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
|
||||
form = context['form']
|
||||
assert form.openid.errors == [u'Please enter a valid url.']
|
||||
|
||||
# Try with a url that's already registered
|
||||
template.clear_test_template_context()
|
||||
openid_plugin_app.post(
|
||||
'/edit/openid/', {
|
||||
'openid': 'http://real.myopenid.com'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
|
||||
form = context['form']
|
||||
assert form.openid.errors == [u'Sorry, an account is already registered to that OpenID.']
|
||||
|
||||
# Test adding openid to account
|
||||
# Need to clear_test_template_context before calling _setup
|
||||
template.clear_test_template_context()
|
||||
self._setup(openid_plugin_app, edit=True)
|
||||
|
||||
# Need to remove openid_url from db because it was added at setup
|
||||
openid = OpenIDUserURL.query.filter_by(
|
||||
openid_url=u'http://add.myopenid.com')
|
||||
openid.delete()
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
@mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
|
||||
def _test_add():
|
||||
# Successful add
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/edit/openid/', {
|
||||
'openid': u'http://add.myopenid.com'})
|
||||
res.follow()
|
||||
|
||||
# Correct place?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/edit/account/'
|
||||
assert 'mediagoblin/edit/edit_account.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# OpenID Added?
|
||||
new_openid = mg_globals.database.OpenIDUserURL.find_one(
|
||||
{'openid_url': u'http://add.myopenid.com'})
|
||||
assert new_openid
|
||||
|
||||
_test_add()
|
||||
|
||||
# Test deleting openid from account
|
||||
# Need to clear_test_template_context before calling _setup
|
||||
template.clear_test_template_context()
|
||||
self._setup(openid_plugin_app, delete=True)
|
||||
|
||||
# Need to add OpenID back to user because it was deleted during
|
||||
# patch
|
||||
openid = OpenIDUserURL()
|
||||
openid.openid_url = 'http://add.myopenid.com'
|
||||
openid.user_id = test_user.id
|
||||
openid.save()
|
||||
|
||||
@mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
|
||||
@mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
|
||||
def _test_delete(self, test_user):
|
||||
# Delete openid from user
|
||||
# Create another user to test deleting OpenID that doesn't belong to them
|
||||
new_user = fixture_add_user(username='newman')
|
||||
openid = OpenIDUserURL()
|
||||
openid.openid_url = 'http://realfake.myopenid.com/'
|
||||
openid.user_id = new_user.id
|
||||
openid.save()
|
||||
|
||||
# Try and delete OpenID url that isn't the users
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/edit/openid/delete/', {
|
||||
'openid': 'http://realfake.myopenid.com/'})
|
||||
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/delete.html']
|
||||
form = context['form']
|
||||
assert form.openid.errors == [u'That OpenID is not registered to this account.']
|
||||
|
||||
# Delete OpenID
|
||||
# Kind of weird to POST to delete/finish
|
||||
template.clear_test_template_context()
|
||||
res = openid_plugin_app.post(
|
||||
'/edit/openid/delete/finish/', {
|
||||
'openid': u'http://add.myopenid.com'})
|
||||
res.follow()
|
||||
|
||||
# Correct place?
|
||||
assert urlparse.urlsplit(res.location)[2] == '/edit/account/'
|
||||
assert 'mediagoblin/edit/edit_account.html' in template.TEMPLATE_TEST_CONTEXT
|
||||
|
||||
# OpenID deleted?
|
||||
new_openid = mg_globals.database.OpenIDUserURL.find_one(
|
||||
{'openid_url': u'http://add.myopenid.com'})
|
||||
assert not new_openid
|
||||
|
||||
_test_delete(self, test_user)
|
||||
Reference in New Issue
Block a user