pyramid-fullauth 0.4.0__py3.4.egg
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- EGG-INFO/PKG-INFO +184 -0
- EGG-INFO/SOURCES.txt +93 -0
- EGG-INFO/dependency_links.txt +1 -0
- EGG-INFO/not-zip-safe +1 -0
- EGG-INFO/requires.txt +13 -0
- EGG-INFO/top_level.txt +2 -0
- pyramid_fullauth/__init__.py +106 -0
- pyramid_fullauth/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/__pycache__/auth.cpython-34.pyc +0 -0
- pyramid_fullauth/__pycache__/events.cpython-34.pyc +0 -0
- pyramid_fullauth/__pycache__/exceptions.cpython-34.pyc +0 -0
- pyramid_fullauth/__pycache__/request.cpython-34.pyc +0 -0
- pyramid_fullauth/__pycache__/tools.cpython-34.pyc +0 -0
- pyramid_fullauth/auth.py +67 -0
- pyramid_fullauth/config/default.yaml +26 -0
- pyramid_fullauth/events.py +281 -0
- pyramid_fullauth/exceptions.py +47 -0
- pyramid_fullauth/models/__init__.py +190 -0
- pyramid_fullauth/models/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/models/extensions/__init__.py +39 -0
- pyramid_fullauth/models/extensions/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/models/mixins/__init__.py +9 -0
- pyramid_fullauth/models/mixins/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/models/mixins/__pycache__/email.cpython-34.pyc +0 -0
- pyramid_fullauth/models/mixins/__pycache__/password.cpython-34.pyc +0 -0
- pyramid_fullauth/models/mixins/email.py +128 -0
- pyramid_fullauth/models/mixins/password.py +141 -0
- pyramid_fullauth/request.py +70 -0
- pyramid_fullauth/resources/helpers/social.mako +17 -0
- pyramid_fullauth/resources/locale/pyramid_fullauth.pot +216 -0
- pyramid_fullauth/resources/templates/403.mako +11 -0
- pyramid_fullauth/resources/templates/_form.login.mako +44 -0
- pyramid_fullauth/resources/templates/_form.register.mako +47 -0
- pyramid_fullauth/resources/templates/_social.login.mako +17 -0
- pyramid_fullauth/resources/templates/activate.mako +14 -0
- pyramid_fullauth/resources/templates/email_change.mako +33 -0
- pyramid_fullauth/resources/templates/layout.mako +13 -0
- pyramid_fullauth/resources/templates/login.mako +17 -0
- pyramid_fullauth/resources/templates/register.mako +22 -0
- pyramid_fullauth/resources/templates/reset.mako +34 -0
- pyramid_fullauth/resources/templates/reset.proceed.mako +41 -0
- pyramid_fullauth/routing/__init__.py +5 -0
- pyramid_fullauth/routing/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/routing/__pycache__/predicates.cpython-34.pyc +0 -0
- pyramid_fullauth/routing/predicates.py +86 -0
- pyramid_fullauth/tools.py +69 -0
- pyramid_fullauth/views/__init__.py +15 -0
- pyramid_fullauth/views/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/views/__pycache__/social.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__init__.py +5 -0
- pyramid_fullauth/views/basic/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__pycache__/activate.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__pycache__/forbidden.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__pycache__/login.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__pycache__/logout.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/__pycache__/register.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/activate.py +44 -0
- pyramid_fullauth/views/basic/forbidden.py +46 -0
- pyramid_fullauth/views/basic/login.py +128 -0
- pyramid_fullauth/views/basic/logout.py +28 -0
- pyramid_fullauth/views/basic/profile/__init__.py +5 -0
- pyramid_fullauth/views/basic/profile/__pycache__/__init__.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/profile/__pycache__/email.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/profile/__pycache__/password.cpython-34.pyc +0 -0
- pyramid_fullauth/views/basic/profile/email.py +108 -0
- pyramid_fullauth/views/basic/profile/password.py +129 -0
- pyramid_fullauth/views/basic/register.py +111 -0
- pyramid_fullauth/views/social.py +221 -0
- tests/__init__.py +1 -0
- tests/__pycache__/__init__.cpython-34.pyc +0 -0
- tests/__pycache__/conftest.cpython-34.pyc +0 -0
- tests/__pycache__/tools.cpython-34.pyc +0 -0
- tests/conftest.py +201 -0
- tests/tools.py +54 -0
- tests/unit/__pycache__/test_default_settings.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_groupfinder.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_social_view.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_tools.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_active.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_admin.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_email.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_password.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_providers.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_user_repr.cpython-34.pyc +0 -0
- tests/unit/__pycache__/test_validators.cpython-34.pyc +0 -0
- tests/unit/test_default_settings.py +15 -0
- tests/unit/test_groupfinder.py +20 -0
- tests/unit/test_social_view.py +69 -0
- tests/unit/test_tools.py +24 -0
- tests/unit/test_user_active.py +31 -0
- tests/unit/test_user_admin.py +61 -0
- tests/unit/test_user_email.py +37 -0
- tests/unit/test_user_password.py +52 -0
- tests/unit/test_user_providers.py +24 -0
- tests/unit/test_user_repr.py +29 -0
- tests/unit/test_validators.py +44 -0
- tests/views/__init__.py +1 -0
- tests/views/__pycache__/__init__.cpython-34.pyc +0 -0
- tests/views/__pycache__/conftest.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_activation.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_email.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_events.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_login.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_login_social.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_logout.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_register.cpython-34.pyc +0 -0
- tests/views/__pycache__/test_reset.cpython-34.pyc +0 -0
- tests/views/conftest.py +20 -0
- tests/views/test_activation.py +86 -0
- tests/views/test_email.py +175 -0
- tests/views/test_events.py +558 -0
- tests/views/test_login.py +172 -0
- tests/views/test_login_social.py +289 -0
- tests/views/test_logout.py +42 -0
- tests/views/test_register.py +138 -0
- tests/views/test_reset.py +105 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Log in related test."""
|
|
2
|
+
import pytest
|
|
3
|
+
import transaction
|
|
4
|
+
|
|
5
|
+
from tests.tools import authenticate, is_user_logged, DEFAULT_USER
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_login_view(default_app):
|
|
9
|
+
"""Login view."""
|
|
10
|
+
res = default_app.get('/login')
|
|
11
|
+
assert res.form
|
|
12
|
+
assert 'email' in res.form.fields
|
|
13
|
+
assert 'password' in res.form.fields
|
|
14
|
+
assert 'remember' in res.form.fields
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_login_view_secret(extended_app):
|
|
18
|
+
"""Login:Form displayed after redirect from protected view."""
|
|
19
|
+
res = extended_app.get('/secret', status=302)
|
|
20
|
+
# check if redirect is correct
|
|
21
|
+
assert res.headers['Location'] == 'http://localhost/login?after=%2Fsecret'
|
|
22
|
+
# redirecting
|
|
23
|
+
res = extended_app.get(res.headers['Location'])
|
|
24
|
+
assert res.form
|
|
25
|
+
assert 'email' in res.form.fields
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize('email', (
|
|
29
|
+
DEFAULT_USER['email'].lower(),
|
|
30
|
+
DEFAULT_USER['email'].upper()
|
|
31
|
+
))
|
|
32
|
+
def test_login_ok(active_user, extended_app, email):
|
|
33
|
+
"""Actually log in test."""
|
|
34
|
+
res = extended_app.get('/secret', status=302)
|
|
35
|
+
res = res.follow()
|
|
36
|
+
res = extended_app.get('/login?after=%2Fsecret')
|
|
37
|
+
|
|
38
|
+
assert is_user_logged(extended_app) is False
|
|
39
|
+
|
|
40
|
+
res = authenticate(extended_app, email=email)
|
|
41
|
+
assert 'Max-Age=' not in str(res)
|
|
42
|
+
|
|
43
|
+
assert is_user_logged(extended_app) is True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_login_remember(active_user, extended_app):
|
|
47
|
+
"""Login user and mark remember me field."""
|
|
48
|
+
res = extended_app.get('/login')
|
|
49
|
+
assert is_user_logged(extended_app) is False
|
|
50
|
+
|
|
51
|
+
res = authenticate(extended_app, remember=True)
|
|
52
|
+
|
|
53
|
+
assert is_user_logged(extended_app) is True
|
|
54
|
+
assert 'Max-Age=' in str(res)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_login_inactive(user, extended_app):
|
|
58
|
+
"""Log in inactive user."""
|
|
59
|
+
assert is_user_logged(extended_app) is False
|
|
60
|
+
|
|
61
|
+
authenticate(extended_app)
|
|
62
|
+
|
|
63
|
+
assert is_user_logged(extended_app) is True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_login_redirects(active_user, extended_app):
|
|
67
|
+
"""Login with redirects."""
|
|
68
|
+
res = extended_app.get('/secret', status=302)
|
|
69
|
+
assert res.status_code == 302
|
|
70
|
+
res = res.follow()
|
|
71
|
+
res.form['email'] = DEFAULT_USER['email']
|
|
72
|
+
res.form['password'] = DEFAULT_USER['password']
|
|
73
|
+
res = res.form.submit()
|
|
74
|
+
|
|
75
|
+
assert is_user_logged(extended_app) is True
|
|
76
|
+
assert res.status_code == 303
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@pytest.mark.parametrize('user_kwargs', (
|
|
80
|
+
{'password': 'wrong password'},
|
|
81
|
+
{'email': 'not@registered.py'}
|
|
82
|
+
))
|
|
83
|
+
def test_login_wrong(active_user, user_kwargs, extended_app):
|
|
84
|
+
"""Use wrong password during authentication."""
|
|
85
|
+
res = authenticate(extended_app, response_code=200, **user_kwargs)
|
|
86
|
+
|
|
87
|
+
assert 'Error! Wrong e-mail or password.' in res
|
|
88
|
+
assert res
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize('post_data', (
|
|
92
|
+
{
|
|
93
|
+
'email': DEFAULT_USER['email'],
|
|
94
|
+
'password': DEFAULT_USER['password'],
|
|
95
|
+
}, {
|
|
96
|
+
'email': DEFAULT_USER['email'],
|
|
97
|
+
'password': DEFAULT_USER['password'],
|
|
98
|
+
'csrf_token': '8934798289723789347892397832789432789'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
))
|
|
102
|
+
def test_login_csrf_error(active_user, extended_app, post_data):
|
|
103
|
+
"""Try to log in with erroneus csrf token."""
|
|
104
|
+
res = extended_app.get('/login', status=200)
|
|
105
|
+
assert res
|
|
106
|
+
res = extended_app.post('/login', post_data, status=401)
|
|
107
|
+
|
|
108
|
+
assert is_user_logged(extended_app) is False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_login_success_xhr(active_user, extended_app):
|
|
112
|
+
"""Test xhr authentication."""
|
|
113
|
+
res = extended_app.get('/login')
|
|
114
|
+
post_data = {
|
|
115
|
+
'email': DEFAULT_USER['email'],
|
|
116
|
+
'password': DEFAULT_USER['password'],
|
|
117
|
+
'csrf_token': res.form['csrf_token'].value
|
|
118
|
+
}
|
|
119
|
+
extended_app.get('/secret', status=302)
|
|
120
|
+
res = extended_app.post('/login?after=%2Fsecret',
|
|
121
|
+
post_data,
|
|
122
|
+
xhr=True,
|
|
123
|
+
expect_errors=True)
|
|
124
|
+
|
|
125
|
+
assert res.content_type == 'application/json'
|
|
126
|
+
assert res.json['status'] is True
|
|
127
|
+
assert 'after' in res.json
|
|
128
|
+
assert is_user_logged(extended_app) is True
|
|
129
|
+
|
|
130
|
+
# second call
|
|
131
|
+
res = extended_app.post('/login?after=%2Fsecret',
|
|
132
|
+
post_data,
|
|
133
|
+
xhr=True,
|
|
134
|
+
expect_errors=True)
|
|
135
|
+
assert res.json['status'] is True
|
|
136
|
+
assert res.json['msg'] == 'Already logged in!'
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_default_login_forbidden(active_user, authable_app):
|
|
140
|
+
"""After successful login, user should get 403 on secret page."""
|
|
141
|
+
authable_app.get('/secret', status=302)
|
|
142
|
+
forbidden = authable_app.get('/secret', xhr=True, status=403)
|
|
143
|
+
assert forbidden.json['status'] is False
|
|
144
|
+
authenticate(authable_app)
|
|
145
|
+
authable_app.get('/secret', status=403)
|
|
146
|
+
# go back to secret page
|
|
147
|
+
forbidden = authable_app.get('/secret', xhr=True, status=403)
|
|
148
|
+
# no permission, but logged.
|
|
149
|
+
assert forbidden.json['status'] is False
|
|
150
|
+
assert 'login_url' not in forbidden.json
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_default_login_redirectaway(active_user, authable_app):
|
|
154
|
+
"""After successful login, access to login page should result in redirect."""
|
|
155
|
+
authenticate(authable_app)
|
|
156
|
+
res = authable_app.get('/login', status=303)
|
|
157
|
+
assert res.location == 'http://localhost/'
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_login_invalid_cookie(db_session, active_user, extended_app):
|
|
161
|
+
"""Test access login page by deleted user."""
|
|
162
|
+
res = authenticate(extended_app)
|
|
163
|
+
assert 'Max-Age=' not in str(res)
|
|
164
|
+
|
|
165
|
+
assert is_user_logged(extended_app) is True
|
|
166
|
+
|
|
167
|
+
db_session.delete(active_user)
|
|
168
|
+
transaction.commit()
|
|
169
|
+
|
|
170
|
+
# will rise Attribute error
|
|
171
|
+
res = extended_app.get('/login')
|
|
172
|
+
assert res.status_code == 200, "Should stay since user is no longer valid!"
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Social network login test."""
|
|
2
|
+
import transaction
|
|
3
|
+
from pyramid import testing
|
|
4
|
+
from pyramid.compat import text_type
|
|
5
|
+
from mock import MagicMock
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from velruse import AuthenticationComplete
|
|
9
|
+
from pyramid_fullauth.views.social import SocialLoginViews
|
|
10
|
+
except ImportError:
|
|
11
|
+
# tests will be skipped anyway
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
from pyramid_fullauth.models import User
|
|
15
|
+
|
|
16
|
+
from tests.conftest import py2only
|
|
17
|
+
from tests.views.conftest import mock_translate
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@py2only
|
|
21
|
+
def test_social_login_link(social_app):
|
|
22
|
+
"""Login:Form displayed social form."""
|
|
23
|
+
res = social_app.get('/login')
|
|
24
|
+
assert ('Connect with facebook</a>' in res.body.decode('unicode_escape'))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@py2only
|
|
28
|
+
def test_social_click_link(social_app):
|
|
29
|
+
"""Click social login link."""
|
|
30
|
+
res = social_app.get('/login/facebook?scope=email%2Coffline_access', status=302)
|
|
31
|
+
assert (res.headers['Location'].startswith(
|
|
32
|
+
'https://www.facebook.com/dialog/oauth/?scope=email%2Coffline_access&state='))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@py2only
|
|
36
|
+
def test_social_login_register(social_config, db_session):
|
|
37
|
+
"""Register fresh user and logs him in."""
|
|
38
|
+
profile = {
|
|
39
|
+
'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
|
|
40
|
+
'displayName': text_type('teddy'),
|
|
41
|
+
'verifiedEmail': text_type('we@po.pl'),
|
|
42
|
+
'preferredUsername': text_type('teddy'),
|
|
43
|
+
'emails': [{'value': text_type('aasd@bwwqwe.pl')}],
|
|
44
|
+
'name': text_type('ted')
|
|
45
|
+
}
|
|
46
|
+
credentials = {'oauthAccessToken': '7897048593434'}
|
|
47
|
+
provider_name = text_type('facebook')
|
|
48
|
+
provider_type = text_type('facebook')
|
|
49
|
+
request = testing.DummyRequest()
|
|
50
|
+
request.user = None
|
|
51
|
+
request.registry = social_config.registry
|
|
52
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
53
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
54
|
+
|
|
55
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
56
|
+
request.login_perform.return_value = {'status': True}
|
|
57
|
+
view = SocialLoginViews(request)
|
|
58
|
+
out = view()
|
|
59
|
+
assert out == {'status': True}
|
|
60
|
+
transaction.commit()
|
|
61
|
+
|
|
62
|
+
# read first new account
|
|
63
|
+
user = db_session.query(User).one()
|
|
64
|
+
assert user.is_active
|
|
65
|
+
assert user.provider_id('facebook') == profile['accounts'][0]['userid']
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@py2only
|
|
69
|
+
def test_login_different_social_account(social_config, db_session, facebook_user):
|
|
70
|
+
"""
|
|
71
|
+
Login with different social account than connected from same provider.
|
|
72
|
+
|
|
73
|
+
System should let user in, but not change connection.
|
|
74
|
+
"""
|
|
75
|
+
# profile mock response
|
|
76
|
+
profile = {
|
|
77
|
+
# facebook user id is different than user's
|
|
78
|
+
'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
|
|
79
|
+
'displayName': text_type('teddy'),
|
|
80
|
+
'verifiedEmail': facebook_user.email,
|
|
81
|
+
'preferredUsername': text_type('teddy'),
|
|
82
|
+
'emails': [{'value': text_type('aasd@bwwqwe.pl')}],
|
|
83
|
+
'name': text_type('ted')
|
|
84
|
+
}
|
|
85
|
+
request = testing.DummyRequest()
|
|
86
|
+
request.user = None
|
|
87
|
+
request.registry = social_config.registry
|
|
88
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
89
|
+
request.context = AuthenticationComplete(
|
|
90
|
+
profile,
|
|
91
|
+
{'oauthAccessToken': '7897048593434'},
|
|
92
|
+
text_type('facebook'),
|
|
93
|
+
text_type('facebook'))
|
|
94
|
+
|
|
95
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
96
|
+
request.login_perform.return_value = {'status': True}
|
|
97
|
+
view = SocialLoginViews(request)
|
|
98
|
+
out = view()
|
|
99
|
+
# user should be authenticated recognized by email!
|
|
100
|
+
assert out['status'] is True
|
|
101
|
+
assert facebook_user.provider_id('facebook') is not profile['accounts'][0]['userid']
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@py2only
|
|
105
|
+
def test_login_social_connect(social_config, active_user, db_session):
|
|
106
|
+
"""Connect and logs user in."""
|
|
107
|
+
user = db_session.merge(active_user)
|
|
108
|
+
|
|
109
|
+
profile = {
|
|
110
|
+
'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
|
|
111
|
+
'displayName': text_type('teddy'),
|
|
112
|
+
'preferredUsername': text_type('teddy'),
|
|
113
|
+
'emails': [{'value': user.email}],
|
|
114
|
+
'name': text_type('ted')
|
|
115
|
+
}
|
|
116
|
+
credentials = {'oauthAccessToken': '7897048593434'}
|
|
117
|
+
provider_name = text_type('facebook')
|
|
118
|
+
provider_type = text_type('facebook')
|
|
119
|
+
user = None
|
|
120
|
+
request = testing.DummyRequest()
|
|
121
|
+
request.user = user
|
|
122
|
+
request.registry = social_config.registry
|
|
123
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
124
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
125
|
+
|
|
126
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
127
|
+
request.login_perform.return_value = {'status': True}
|
|
128
|
+
view = SocialLoginViews(request)
|
|
129
|
+
out = view()
|
|
130
|
+
assert out == {'status': True}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@py2only
|
|
134
|
+
def test_logged_social_connect_account(social_config, active_user, db_session):
|
|
135
|
+
"""Connect facebook account to logged in user."""
|
|
136
|
+
user = db_session.merge(active_user)
|
|
137
|
+
|
|
138
|
+
profile = {
|
|
139
|
+
'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
|
|
140
|
+
'displayName': text_type('teddy'),
|
|
141
|
+
'preferredUsername': text_type('teddy'),
|
|
142
|
+
'emails': [{'value': text_type('aasd@basd.pl')}],
|
|
143
|
+
'name': text_type('ted')
|
|
144
|
+
}
|
|
145
|
+
credentials = {'oauthAccessToken': '7897048593434'}
|
|
146
|
+
provider_name = text_type('facebook')
|
|
147
|
+
provider_type = text_type('facebook')
|
|
148
|
+
request = testing.DummyRequest()
|
|
149
|
+
request.user = user
|
|
150
|
+
request.registry = social_config.registry
|
|
151
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
152
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
153
|
+
request._ = mock_translate
|
|
154
|
+
|
|
155
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
156
|
+
request.login_perform.return_value = {'status': True}
|
|
157
|
+
view = SocialLoginViews(request)
|
|
158
|
+
out = view()
|
|
159
|
+
assert out['status'] is True
|
|
160
|
+
|
|
161
|
+
transaction.commit()
|
|
162
|
+
user = db_session.merge(user)
|
|
163
|
+
assert user.provider_id('facebook') == profile['accounts'][0]['userid']
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@py2only
|
|
167
|
+
def test_logged_social_connect_self(social_config, facebook_user, db_session):
|
|
168
|
+
"""Connect self."""
|
|
169
|
+
user = db_session.merge(facebook_user)
|
|
170
|
+
|
|
171
|
+
profile = {
|
|
172
|
+
'accounts': [{'domain': text_type('facebook.com'), 'userid': user.provider_id('facebook')}],
|
|
173
|
+
'displayName':
|
|
174
|
+
text_type('teddy'),
|
|
175
|
+
'preferredUsername':
|
|
176
|
+
text_type('teddy'),
|
|
177
|
+
'emails':
|
|
178
|
+
[{'value': user.email}],
|
|
179
|
+
'name':
|
|
180
|
+
text_type('ted')
|
|
181
|
+
}
|
|
182
|
+
credentials = {'oauthAccessToken':
|
|
183
|
+
'7897048593434'}
|
|
184
|
+
provider_name = text_type('facebook')
|
|
185
|
+
provider_type = text_type('facebook')
|
|
186
|
+
request = testing.DummyRequest()
|
|
187
|
+
request.user = user
|
|
188
|
+
request.registry = social_config.registry
|
|
189
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
190
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
191
|
+
request._ = mock_translate
|
|
192
|
+
|
|
193
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
194
|
+
request.login_perform.return_value = {'status': True}
|
|
195
|
+
view = SocialLoginViews(request)
|
|
196
|
+
out = view()
|
|
197
|
+
assert out['status'] is True
|
|
198
|
+
|
|
199
|
+
user = db_session.merge(facebook_user)
|
|
200
|
+
assert user.provider_id('facebook') == profile['accounts'][0]['userid']
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@py2only
|
|
204
|
+
def test_logged_social_connect_second_account(social_config, facebook_user, db_session):
|
|
205
|
+
"""Connect second facebook account to logged in user."""
|
|
206
|
+
user = db_session.merge(facebook_user)
|
|
207
|
+
|
|
208
|
+
# mock request
|
|
209
|
+
profile = {
|
|
210
|
+
'accounts':
|
|
211
|
+
[{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
|
|
212
|
+
'displayName':
|
|
213
|
+
text_type('teddy'),
|
|
214
|
+
'preferredUsername':
|
|
215
|
+
text_type('teddy'),
|
|
216
|
+
'emails':
|
|
217
|
+
[{'value': text_type('aasd@basd.pl')}],
|
|
218
|
+
'name':
|
|
219
|
+
text_type('ted')
|
|
220
|
+
}
|
|
221
|
+
credentials = {'oauthAccessToken':
|
|
222
|
+
'7897048593434'}
|
|
223
|
+
provider_name = text_type('facebook')
|
|
224
|
+
provider_type = text_type('facebook')
|
|
225
|
+
request = testing.DummyRequest()
|
|
226
|
+
request.user = user
|
|
227
|
+
request.registry = social_config.registry
|
|
228
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
229
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
230
|
+
request._ = mock_translate
|
|
231
|
+
|
|
232
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
233
|
+
request.login_perform.return_value = {'status': True}
|
|
234
|
+
view = SocialLoginViews(request)
|
|
235
|
+
out = view()
|
|
236
|
+
# status should be false
|
|
237
|
+
assert out['status'] is False
|
|
238
|
+
assert out['msg'] == 'Your account is already connected to other ${provider} account.'
|
|
239
|
+
assert user.provider_id('facebook') is not profile['accounts'][0]['userid']
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@py2only
|
|
243
|
+
def test_logged_social_connect_used_account(social_config, facebook_user, db_session):
|
|
244
|
+
"""Try to connect facebook account to logged in user used by other user."""
|
|
245
|
+
# this user will be logged and trying to connect facebook's user account.
|
|
246
|
+
fresh_user = User(
|
|
247
|
+
email=text_type('new@user.pl'),
|
|
248
|
+
password=text_type('somepassword'),
|
|
249
|
+
address_ip=text_type('127.0.0.1')
|
|
250
|
+
)
|
|
251
|
+
db_session.add(fresh_user)
|
|
252
|
+
transaction.commit()
|
|
253
|
+
user = db_session.merge(facebook_user)
|
|
254
|
+
fresh_user = db_session.merge(fresh_user)
|
|
255
|
+
|
|
256
|
+
# mock request
|
|
257
|
+
profile = {
|
|
258
|
+
'accounts':
|
|
259
|
+
[{'domain': text_type('facebook.com'), 'userid': user.provider_id('facebook')}],
|
|
260
|
+
'displayName':
|
|
261
|
+
text_type('teddy'),
|
|
262
|
+
'preferredUsername':
|
|
263
|
+
text_type('teddy'),
|
|
264
|
+
'emails':
|
|
265
|
+
[{'value': text_type('aasd@basd.pl')}],
|
|
266
|
+
'name':
|
|
267
|
+
text_type('ted')
|
|
268
|
+
}
|
|
269
|
+
credentials = {'oauthAccessToken': '7897048593434'}
|
|
270
|
+
provider_name = text_type('facebook')
|
|
271
|
+
provider_type = text_type('facebook')
|
|
272
|
+
request = testing.DummyRequest()
|
|
273
|
+
request.user = fresh_user
|
|
274
|
+
request.registry = social_config.registry
|
|
275
|
+
request.remote_addr = text_type('127.0.0.123')
|
|
276
|
+
request.context = AuthenticationComplete(profile, credentials, provider_name, provider_type)
|
|
277
|
+
request._ = mock_translate
|
|
278
|
+
|
|
279
|
+
request.login_perform = MagicMock(name='login_perform')
|
|
280
|
+
request.login_perform.return_value = {'status': True}
|
|
281
|
+
# call!
|
|
282
|
+
view = SocialLoginViews(request)
|
|
283
|
+
out = view()
|
|
284
|
+
# status should be false
|
|
285
|
+
assert out['status'] is False
|
|
286
|
+
assert out['msg'] == 'This ${provider} account is already connected with other account.'
|
|
287
|
+
transaction.begin()
|
|
288
|
+
fresh_user = db_session.merge(fresh_user)
|
|
289
|
+
assert fresh_user.provider_id('facebook') is None
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Logout related tests."""
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from tests.tools import authenticate, is_user_logged
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_logout(active_user, extended_app):
|
|
8
|
+
"""Check logout action."""
|
|
9
|
+
authenticate(extended_app)
|
|
10
|
+
assert is_user_logged(extended_app) is True
|
|
11
|
+
|
|
12
|
+
extended_app.get('/logout', status=303)
|
|
13
|
+
assert is_user_logged(extended_app) is False
|
|
14
|
+
res = extended_app.get('/secret', status=302)
|
|
15
|
+
assert res.status_code == 302
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_logout_login(active_user, extended_config, extended_app):
|
|
19
|
+
"""Check logout action with configured logout redirection."""
|
|
20
|
+
extended_config.registry['config'].fullauth.redirects.logout = 'login'
|
|
21
|
+
authenticate(extended_app)
|
|
22
|
+
assert is_user_logged(extended_app) is True
|
|
23
|
+
|
|
24
|
+
res = extended_app.get('/logout', status=303)
|
|
25
|
+
assert is_user_logged(extended_app) is False
|
|
26
|
+
# redirection should be done to login page.
|
|
27
|
+
assert '/login' in res.location
|
|
28
|
+
res = extended_app.get('/secret', status=302)
|
|
29
|
+
assert res.status_code == 302
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_automatic_logout(active_user, short_config, short_app):
|
|
33
|
+
"""Test automatic logout."""
|
|
34
|
+
timeout = short_config.registry['config']['fullauth']['AuthTkt']['timeout'] + 1
|
|
35
|
+
|
|
36
|
+
authenticate(short_app)
|
|
37
|
+
# Simulating inactivity
|
|
38
|
+
time.sleep(timeout)
|
|
39
|
+
res = short_app.get('/email/change')
|
|
40
|
+
assert res.headers['Location'] == 'http://localhost/login?after=%2Femail%2Fchange'
|
|
41
|
+
res = res.follow()
|
|
42
|
+
assert res.form
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Registration related tests."""
|
|
2
|
+
try:
|
|
3
|
+
from HTMLParser import HTMLParser
|
|
4
|
+
except ImportError:
|
|
5
|
+
from html.parser import HTMLParser
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
import transaction
|
|
9
|
+
from pyramid.compat import text_type
|
|
10
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
11
|
+
|
|
12
|
+
from pyramid_fullauth.models import User
|
|
13
|
+
from tests.tools import DEFAULT_USER
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_register_view(default_app):
|
|
17
|
+
"""Display register form."""
|
|
18
|
+
res = default_app.get('/register')
|
|
19
|
+
assert res.form
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize('email, password', (
|
|
23
|
+
# casual
|
|
24
|
+
(DEFAULT_USER['email'], DEFAULT_USER['password']),
|
|
25
|
+
# loooong password
|
|
26
|
+
(DEFAULT_USER['email'], DEFAULT_USER['password'] * 10000),
|
|
27
|
+
))
|
|
28
|
+
def test_register_success(db_session, default_app, email, password):
|
|
29
|
+
"""Register user successfully."""
|
|
30
|
+
assert db_session.query(User).count() == 0
|
|
31
|
+
|
|
32
|
+
res = default_app.get('/register')
|
|
33
|
+
res.form['email'] = email
|
|
34
|
+
res.form['password'] = password
|
|
35
|
+
res.form['confirm_password'] = password
|
|
36
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'})
|
|
37
|
+
transaction.commit()
|
|
38
|
+
|
|
39
|
+
user = db_session.query(User).filter(User.email == email).one()
|
|
40
|
+
|
|
41
|
+
# User should not have active account at this moment
|
|
42
|
+
assert user.is_active is not None
|
|
43
|
+
assert user.check_password(password)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_register_user_exists(db_session, user, default_app):
|
|
47
|
+
"""Trying to register existing user."""
|
|
48
|
+
res = default_app.get('/register')
|
|
49
|
+
res.form['email'] = DEFAULT_USER['email']
|
|
50
|
+
res.form['password'] = DEFAULT_USER['password']
|
|
51
|
+
res.form['confirm_password'] = DEFAULT_USER['password']
|
|
52
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'})
|
|
53
|
+
transaction.commit()
|
|
54
|
+
|
|
55
|
+
assert 'User with given e-mail already exists' in res.body.decode('unicode_escape')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.mark.parametrize('email, password, confirm_password, error', (
|
|
59
|
+
# no email
|
|
60
|
+
(None, DEFAULT_USER['password'], DEFAULT_USER['password'],
|
|
61
|
+
'E-mail is empty'),
|
|
62
|
+
# empty email
|
|
63
|
+
('', DEFAULT_USER['password'], DEFAULT_USER['password'],
|
|
64
|
+
'E-mail is empty'),
|
|
65
|
+
# not matching passwords
|
|
66
|
+
(DEFAULT_USER['email'], DEFAULT_USER['password'], DEFAULT_USER['password'] + 'Typo',
|
|
67
|
+
'Passwords don\'t match'),
|
|
68
|
+
# long, incorect email
|
|
69
|
+
(text_type('email') * 100 + text_type('@wap.pl'), DEFAULT_USER['password'], DEFAULT_USER['password'],
|
|
70
|
+
'Incorrect e-mail format'),
|
|
71
|
+
# too short password
|
|
72
|
+
(DEFAULT_USER['email'], '12', '12',
|
|
73
|
+
'Password is too short'),
|
|
74
|
+
# empty password
|
|
75
|
+
(DEFAULT_USER['email'], '', '', 'Please enter your password'),
|
|
76
|
+
))
|
|
77
|
+
def test_register_error(db_session, default_app, email, password, confirm_password, error):
|
|
78
|
+
"""Error in registration process."""
|
|
79
|
+
assert db_session.query(User).count() == 0
|
|
80
|
+
|
|
81
|
+
res = default_app.get('/register')
|
|
82
|
+
if email is not None:
|
|
83
|
+
res.form['email'] = email
|
|
84
|
+
res.form['password'] = password
|
|
85
|
+
res.form['confirm_password'] = confirm_password
|
|
86
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'})
|
|
87
|
+
transaction.commit()
|
|
88
|
+
|
|
89
|
+
assert error in HTMLParser().unescape(res.body.decode('unicode_escape'))
|
|
90
|
+
assert db_session.query(User).count() == 0
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_register_wrong_csrf(db_session, default_app):
|
|
94
|
+
"""CSRF error during registration."""
|
|
95
|
+
assert db_session.query(User).count() == 0
|
|
96
|
+
|
|
97
|
+
res = default_app.get('/register')
|
|
98
|
+
res.form['email'] = DEFAULT_USER['email']
|
|
99
|
+
res.form['password'] = DEFAULT_USER['password']
|
|
100
|
+
res.form['confirm_password'] = DEFAULT_USER['password']
|
|
101
|
+
res.form['csrf_token'] = 'wrong_token'
|
|
102
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'},
|
|
103
|
+
status=401)
|
|
104
|
+
transaction.commit()
|
|
105
|
+
|
|
106
|
+
assert db_session.query(User).count() == 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_no_pass_confirm(db_session, nopassconfirm_app):
|
|
110
|
+
"""Register without password confirm option."""
|
|
111
|
+
assert db_session.query(User).count() == 0
|
|
112
|
+
|
|
113
|
+
res = nopassconfirm_app.get('/register')
|
|
114
|
+
res.form['email'] = DEFAULT_USER['email']
|
|
115
|
+
res.form['password'] = DEFAULT_USER['password']
|
|
116
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'})
|
|
117
|
+
|
|
118
|
+
assert 'Passwords don\'t match!' not in HTMLParser().unescape(res.body.decode('unicode_escape'))
|
|
119
|
+
transaction.commit()
|
|
120
|
+
|
|
121
|
+
user = db_session.query(User).filter(User.email == DEFAULT_USER['email']).one()
|
|
122
|
+
|
|
123
|
+
# User should not have active account at this moment
|
|
124
|
+
assert user.is_active is not None
|
|
125
|
+
assert user.check_password(DEFAULT_USER['password'])
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_register_action_no_password_required(db_session, nopassregister_app):
|
|
129
|
+
"""Register without password required."""
|
|
130
|
+
with pytest.raises(NoResultFound):
|
|
131
|
+
db_session.query(User).filter(User.email == DEFAULT_USER['email']).one()
|
|
132
|
+
|
|
133
|
+
res = nopassregister_app.get('/register')
|
|
134
|
+
res.form['email'] = DEFAULT_USER['email']
|
|
135
|
+
res = res.form.submit(extra_environ={'REMOTE_ADDR': '0.0.0.0'})
|
|
136
|
+
transaction.commit()
|
|
137
|
+
|
|
138
|
+
db_session.query(User).filter(User.email == DEFAULT_USER['email']).one()
|