pyramid-fullauth 0.4.0__py3.3.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.
Files changed (116) hide show
  1. EGG-INFO/PKG-INFO +184 -0
  2. EGG-INFO/SOURCES.txt +93 -0
  3. EGG-INFO/dependency_links.txt +1 -0
  4. EGG-INFO/not-zip-safe +1 -0
  5. EGG-INFO/requires.txt +13 -0
  6. EGG-INFO/top_level.txt +2 -0
  7. pyramid_fullauth/__init__.py +106 -0
  8. pyramid_fullauth/__pycache__/__init__.cpython-33.pyc +0 -0
  9. pyramid_fullauth/__pycache__/auth.cpython-33.pyc +0 -0
  10. pyramid_fullauth/__pycache__/events.cpython-33.pyc +0 -0
  11. pyramid_fullauth/__pycache__/exceptions.cpython-33.pyc +0 -0
  12. pyramid_fullauth/__pycache__/request.cpython-33.pyc +0 -0
  13. pyramid_fullauth/__pycache__/tools.cpython-33.pyc +0 -0
  14. pyramid_fullauth/auth.py +67 -0
  15. pyramid_fullauth/config/default.yaml +26 -0
  16. pyramid_fullauth/events.py +281 -0
  17. pyramid_fullauth/exceptions.py +47 -0
  18. pyramid_fullauth/models/__init__.py +190 -0
  19. pyramid_fullauth/models/__pycache__/__init__.cpython-33.pyc +0 -0
  20. pyramid_fullauth/models/extensions/__init__.py +39 -0
  21. pyramid_fullauth/models/extensions/__pycache__/__init__.cpython-33.pyc +0 -0
  22. pyramid_fullauth/models/mixins/__init__.py +9 -0
  23. pyramid_fullauth/models/mixins/__pycache__/__init__.cpython-33.pyc +0 -0
  24. pyramid_fullauth/models/mixins/__pycache__/email.cpython-33.pyc +0 -0
  25. pyramid_fullauth/models/mixins/__pycache__/password.cpython-33.pyc +0 -0
  26. pyramid_fullauth/models/mixins/email.py +128 -0
  27. pyramid_fullauth/models/mixins/password.py +141 -0
  28. pyramid_fullauth/request.py +70 -0
  29. pyramid_fullauth/resources/helpers/social.mako +17 -0
  30. pyramid_fullauth/resources/locale/pyramid_fullauth.pot +216 -0
  31. pyramid_fullauth/resources/templates/403.mako +11 -0
  32. pyramid_fullauth/resources/templates/_form.login.mako +44 -0
  33. pyramid_fullauth/resources/templates/_form.register.mako +47 -0
  34. pyramid_fullauth/resources/templates/_social.login.mako +17 -0
  35. pyramid_fullauth/resources/templates/activate.mako +14 -0
  36. pyramid_fullauth/resources/templates/email_change.mako +33 -0
  37. pyramid_fullauth/resources/templates/layout.mako +13 -0
  38. pyramid_fullauth/resources/templates/login.mako +17 -0
  39. pyramid_fullauth/resources/templates/register.mako +22 -0
  40. pyramid_fullauth/resources/templates/reset.mako +34 -0
  41. pyramid_fullauth/resources/templates/reset.proceed.mako +41 -0
  42. pyramid_fullauth/routing/__init__.py +5 -0
  43. pyramid_fullauth/routing/__pycache__/__init__.cpython-33.pyc +0 -0
  44. pyramid_fullauth/routing/__pycache__/predicates.cpython-33.pyc +0 -0
  45. pyramid_fullauth/routing/predicates.py +86 -0
  46. pyramid_fullauth/tools.py +69 -0
  47. pyramid_fullauth/views/__init__.py +15 -0
  48. pyramid_fullauth/views/__pycache__/__init__.cpython-33.pyc +0 -0
  49. pyramid_fullauth/views/__pycache__/social.cpython-33.pyc +0 -0
  50. pyramid_fullauth/views/basic/__init__.py +5 -0
  51. pyramid_fullauth/views/basic/__pycache__/__init__.cpython-33.pyc +0 -0
  52. pyramid_fullauth/views/basic/__pycache__/activate.cpython-33.pyc +0 -0
  53. pyramid_fullauth/views/basic/__pycache__/forbidden.cpython-33.pyc +0 -0
  54. pyramid_fullauth/views/basic/__pycache__/login.cpython-33.pyc +0 -0
  55. pyramid_fullauth/views/basic/__pycache__/logout.cpython-33.pyc +0 -0
  56. pyramid_fullauth/views/basic/__pycache__/register.cpython-33.pyc +0 -0
  57. pyramid_fullauth/views/basic/activate.py +44 -0
  58. pyramid_fullauth/views/basic/forbidden.py +46 -0
  59. pyramid_fullauth/views/basic/login.py +128 -0
  60. pyramid_fullauth/views/basic/logout.py +28 -0
  61. pyramid_fullauth/views/basic/profile/__init__.py +5 -0
  62. pyramid_fullauth/views/basic/profile/__pycache__/__init__.cpython-33.pyc +0 -0
  63. pyramid_fullauth/views/basic/profile/__pycache__/email.cpython-33.pyc +0 -0
  64. pyramid_fullauth/views/basic/profile/__pycache__/password.cpython-33.pyc +0 -0
  65. pyramid_fullauth/views/basic/profile/email.py +108 -0
  66. pyramid_fullauth/views/basic/profile/password.py +129 -0
  67. pyramid_fullauth/views/basic/register.py +111 -0
  68. pyramid_fullauth/views/social.py +221 -0
  69. tests/__init__.py +1 -0
  70. tests/__pycache__/__init__.cpython-33.pyc +0 -0
  71. tests/__pycache__/conftest.cpython-33.pyc +0 -0
  72. tests/__pycache__/tools.cpython-33.pyc +0 -0
  73. tests/conftest.py +201 -0
  74. tests/tools.py +54 -0
  75. tests/unit/__pycache__/test_default_settings.cpython-33.pyc +0 -0
  76. tests/unit/__pycache__/test_groupfinder.cpython-33.pyc +0 -0
  77. tests/unit/__pycache__/test_social_view.cpython-33.pyc +0 -0
  78. tests/unit/__pycache__/test_tools.cpython-33.pyc +0 -0
  79. tests/unit/__pycache__/test_user_active.cpython-33.pyc +0 -0
  80. tests/unit/__pycache__/test_user_admin.cpython-33.pyc +0 -0
  81. tests/unit/__pycache__/test_user_email.cpython-33.pyc +0 -0
  82. tests/unit/__pycache__/test_user_password.cpython-33.pyc +0 -0
  83. tests/unit/__pycache__/test_user_providers.cpython-33.pyc +0 -0
  84. tests/unit/__pycache__/test_user_repr.cpython-33.pyc +0 -0
  85. tests/unit/__pycache__/test_validators.cpython-33.pyc +0 -0
  86. tests/unit/test_default_settings.py +15 -0
  87. tests/unit/test_groupfinder.py +20 -0
  88. tests/unit/test_social_view.py +69 -0
  89. tests/unit/test_tools.py +24 -0
  90. tests/unit/test_user_active.py +31 -0
  91. tests/unit/test_user_admin.py +61 -0
  92. tests/unit/test_user_email.py +37 -0
  93. tests/unit/test_user_password.py +52 -0
  94. tests/unit/test_user_providers.py +24 -0
  95. tests/unit/test_user_repr.py +29 -0
  96. tests/unit/test_validators.py +44 -0
  97. tests/views/__init__.py +1 -0
  98. tests/views/__pycache__/__init__.cpython-33.pyc +0 -0
  99. tests/views/__pycache__/conftest.cpython-33.pyc +0 -0
  100. tests/views/__pycache__/test_activation.cpython-33.pyc +0 -0
  101. tests/views/__pycache__/test_email.cpython-33.pyc +0 -0
  102. tests/views/__pycache__/test_events.cpython-33.pyc +0 -0
  103. tests/views/__pycache__/test_login.cpython-33.pyc +0 -0
  104. tests/views/__pycache__/test_login_social.cpython-33.pyc +0 -0
  105. tests/views/__pycache__/test_logout.cpython-33.pyc +0 -0
  106. tests/views/__pycache__/test_register.cpython-33.pyc +0 -0
  107. tests/views/__pycache__/test_reset.cpython-33.pyc +0 -0
  108. tests/views/conftest.py +20 -0
  109. tests/views/test_activation.py +86 -0
  110. tests/views/test_email.py +175 -0
  111. tests/views/test_events.py +558 -0
  112. tests/views/test_login.py +172 -0
  113. tests/views/test_login_social.py +289 -0
  114. tests/views/test_logout.py +42 -0
  115. tests/views/test_register.py +138 -0
  116. tests/views/test_reset.py +105 -0
tests/conftest.py ADDED
@@ -0,0 +1,201 @@
1
+ """Most of the fixtures needed."""
2
+ import sys
3
+
4
+ from mock import Mock
5
+ import pytest
6
+
7
+ import transaction
8
+ from sqlalchemy import create_engine
9
+ from sqlalchemy.orm import scoped_session, sessionmaker
10
+ from sqlalchemy.pool import NullPool
11
+ from zope.sqlalchemy import ZopeTransactionExtension
12
+
13
+ from pyramid.compat import text_type
14
+ from pytest_pyramid import factories
15
+ import pyramid_basemodel
16
+
17
+ py2only = pytest.mark.skipif(
18
+ sys.version_info.major > 2,
19
+ reason='oauth2-python and velruse does not support python3')
20
+
21
+
22
+ @pytest.fixture
23
+ def web_request():
24
+ """Mocked web request for views testing."""
25
+ request = Mock()
26
+ config = Mock()
27
+ config.configure_mock(
28
+ **{'fullauth.register.password':
29
+ {'length_min': 8, 'confirm': True},
30
+ 'POST': {'confirm_password': '987654321'}
31
+ }
32
+ )
33
+
34
+ def _(message, *args, **kwargs):
35
+ return message
36
+ request._ = Mock(side_effect=_)
37
+ request.configure_mock(
38
+ **{'registry': {'config': config}}
39
+ )
40
+
41
+ return request
42
+
43
+ if sys.version_info.major == 2:
44
+ databases = ['sqlite', 'mysql', 'postgresql']
45
+ else:
46
+ databases = ['sqlite']
47
+
48
+
49
+ @pytest.fixture(scope='function', params=databases)
50
+ def db_session(request):
51
+ """SQLAlchemy session."""
52
+ from pyramid_fullauth.models import Base
53
+
54
+ if request.param == 'sqlite':
55
+ connection = 'sqlite:///fullauth.sqlite'
56
+ elif request.param == 'mysql':
57
+ request.getfuncargvalue('mysqldb') # takes care of creating database
58
+ connection = 'mysql+mysqldb://root:@127.0.0.1:3307/tests?charset=utf8'
59
+ elif request.param == 'postgresql':
60
+ request.getfuncargvalue('postgresql') # takes care of creating database
61
+ connection = 'postgresql+psycopg2://postgres:@127.0.0.1:5433/tests'
62
+
63
+ engine = create_engine(connection, echo=False, poolclass=NullPool)
64
+ pyramid_basemodel.Session = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
65
+ pyramid_basemodel.bind_engine(engine, pyramid_basemodel.Session, should_drop=True)
66
+
67
+ def destroy():
68
+ transaction.commit()
69
+ Base.metadata.drop_all(engine)
70
+
71
+ request.addfinalizer(destroy)
72
+
73
+ return pyramid_basemodel.Session
74
+
75
+
76
+ @pytest.fixture
77
+ def user(db_session):
78
+ """Default user."""
79
+ from pyramid_fullauth.models import User
80
+ from tests.tools import DEFAULT_USER
81
+ user = User(**DEFAULT_USER)
82
+ db_session.add(user)
83
+ transaction.commit()
84
+ return user
85
+
86
+
87
+ @pytest.fixture
88
+ def active_user(user, db_session):
89
+ """Active user."""
90
+ user = db_session.merge(user)
91
+ user.is_active = True
92
+ transaction.commit()
93
+ return user
94
+
95
+
96
+ @pytest.fixture(params=[
97
+ # (an @ character must separate the local and domain parts)
98
+ text_type('Abc.example.com'),
99
+ # (character dot(.) is last in local part)
100
+ text_type('Abc.@example.com'),
101
+ # (character dot(.) is double)
102
+ text_type('Abc..123@example.com'),
103
+ # (only one @ is allowed outside quotation marks)
104
+ text_type('A@b@c@example.com'),
105
+ # (none of the special characters in this local part is allowed outside quotation marks)
106
+ text_type('a"b(c)d,e:f;g<h>i[j\k]l@example.com'),
107
+ # (quoted strings must be dot separated, or the only element making up the local-part)
108
+ text_type('just"not"right@example.com'),
109
+ # (spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash)
110
+ text_type('this is"not\allowed@example.com'),
111
+ # (even if escaped (preceded by a backslash), spaces, quotes, and backslashes must still be contained by quotes)
112
+ text_type('this\ still\"not\\allowed@example.com'),
113
+ text_type('bad-mail'),
114
+ ])
115
+ def invalid_email(request):
116
+ """Parametrized fixture with all the incorrect emails."""
117
+ return request.param
118
+
119
+
120
+ default_config = factories.pyramid_config({
121
+ 'pyramid.includes': [
122
+ 'pyramid_tm',
123
+ 'pyramid_fullauth'
124
+ ]
125
+ })
126
+
127
+
128
+ extended_config = factories.pyramid_config({
129
+ 'pyramid.includes': [
130
+ 'pyramid_tm',
131
+ 'pyramid_fullauth',
132
+ 'tests.tools.include_views'
133
+ ]
134
+ })
135
+
136
+
137
+ short_config = factories.pyramid_config({
138
+ 'yml.location': 'tests:config/short_memory.yaml',
139
+ 'pyramid.includes': [
140
+ 'pyramid_tm',
141
+ 'tzf.pyramid_yml',
142
+ 'pyramid_fullauth',
143
+ 'tests.tools.include_views'
144
+ ]
145
+ })
146
+
147
+
148
+ social_config = factories.pyramid_config({
149
+ 'yml.location': 'tests:config/social.yaml',
150
+ 'pyramid.includes': [
151
+ 'pyramid_tm',
152
+ 'tzf.pyramid_yml',
153
+ 'pyramid_fullauth',
154
+ 'tests.tools.include_views'
155
+ ]
156
+ })
157
+
158
+
159
+ authable_config = factories.pyramid_config({
160
+ 'yml.location': 'tests:config',
161
+ 'env': 'login',
162
+ 'pyramid.includes': [
163
+ 'pyramid_tm',
164
+ 'tzf.pyramid_yml',
165
+ 'pyramid_fullauth',
166
+ 'tests.tools.include_views'
167
+ ]
168
+ })
169
+
170
+
171
+ nopassconfirm_config = factories.pyramid_config({
172
+ 'yml.location': 'tests:config/no_password_confirm.yaml',
173
+ 'env': 'login',
174
+ 'pyramid.includes': [
175
+ 'pyramid_tm',
176
+ 'tzf.pyramid_yml',
177
+ 'pyramid_fullauth',
178
+ 'tests.tools.include_views'
179
+ ]
180
+ })
181
+
182
+
183
+ nopassregister_config = factories.pyramid_config({
184
+ 'yml.location': 'tests:config/no_password_register.yaml',
185
+ 'env': 'login',
186
+ 'pyramid.includes': [
187
+ 'pyramid_tm',
188
+ 'tzf.pyramid_yml',
189
+ 'pyramid_fullauth',
190
+ 'tests.tools.include_views'
191
+ ]
192
+ })
193
+
194
+
195
+ default_app = factories.pyramid_app('default_config')
196
+ extended_app = factories.pyramid_app('extended_config')
197
+ short_app = factories.pyramid_app('short_config')
198
+ social_app = factories.pyramid_app('social_config')
199
+ authable_app = factories.pyramid_app('authable_config')
200
+ nopassconfirm_app = factories.pyramid_app('nopassconfirm_config')
201
+ nopassregister_app = factories.pyramid_app('nopassregister_config')
tests/tools.py ADDED
@@ -0,0 +1,54 @@
1
+ """Tools used for fullauth tests."""
2
+ from pyramid.view import view_config
3
+ from pyramid.compat import text_type
4
+
5
+ DEFAULT_USER = {
6
+ 'username': text_type('u1'),
7
+ 'password': text_type('password1'),
8
+ 'email': text_type('email@example.com'),
9
+ 'address_ip': text_type('127.0.0.1')
10
+ }
11
+
12
+
13
+ def authenticate(app, email=DEFAULT_USER['email'],
14
+ password=DEFAULT_USER['password'],
15
+ remember=False,
16
+ response_code=303):
17
+ """Authenticate user."""
18
+ res = app.get('/login')
19
+ form = res.form
20
+
21
+ form['email'] = email
22
+ form['password'] = password
23
+ if remember:
24
+ form['remember'] = 1
25
+
26
+ res = form.submit()
27
+
28
+ # We've been redirected after log in
29
+ assert res.status_code == response_code
30
+
31
+ return res
32
+
33
+
34
+ def is_user_logged(app):
35
+ """Check for auth_tkt cookies beeing set to see if user is logged in."""
36
+ cookies = app.cookies
37
+ if 'auth_tkt' in cookies and cookies['auth_tkt']:
38
+
39
+ return True
40
+ return False
41
+
42
+ # this below are additional views crafted for tests.
43
+
44
+
45
+ def include_views(config):
46
+ """Dummy pyramid 'plugin' adding dummy secret view."""
47
+ config.add_route('secret', '/secret')
48
+ config.scan('tests.tools')
49
+
50
+
51
+ @view_config(route_name="secret", permission="super_high", renderer='json')
52
+ def secret_view(request):
53
+ """Dummy view with redirect to login."""
54
+ return {}
@@ -0,0 +1,15 @@
1
+ """
2
+ Test for recognizing default settings.
3
+
4
+ Also helps to keeping them in line.
5
+ """
6
+
7
+
8
+ def test_defaults(default_config):
9
+ """Load defaults config for fullauth."""
10
+ # Config should get created based on fullauth defaults
11
+ assert 'fullauth' in default_config.registry['config']
12
+ # csrf should be turned on by default
13
+ assert default_config.registry['config']['fullauth']['check_csrf'] is True
14
+ # also hashalg should be anything but not md5 by default.
15
+ assert default_config.registry['config']['fullauth']['AuthTkt']['hashalg'] is not 'md5'
@@ -0,0 +1,20 @@
1
+ """Test groupfinder."""
2
+ import pytest
3
+
4
+ from pyramid_fullauth.auth import groupfinder
5
+
6
+
7
+ @pytest.mark.parametrize(['is_admin', 'is_active', 'groups'],
8
+ [(True, True, ['s:superadmin', 's:user']),
9
+ (True, False, ['s:superadmin', 's:inactive']),
10
+ (False, True, ['s:user']),
11
+ (False, False, ['s:inactive'])])
12
+ def test_groupfinder(web_request, db_session, user, is_admin, is_active, groups):
13
+ """Return special group assigned for user."""
14
+ user = db_session.merge(user)
15
+ user.is_admin = is_admin
16
+ user.is_active = is_active
17
+
18
+ web_request.user = user
19
+ assigned_groups = groupfinder(user.id, web_request)
20
+ assert groups == assigned_groups
@@ -0,0 +1,69 @@
1
+ """Unittest for social login view."""
2
+ import pytest
3
+ import mock
4
+ from pyramid.compat import text_type
5
+
6
+ from pyramid_fullauth.views.social import SocialLoginViews
7
+ from tests.conftest import py2only
8
+
9
+
10
+ @py2only
11
+ @pytest.mark.parametrize('profile, email', [
12
+ (
13
+ {
14
+ 'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
15
+ 'displayName': text_type('teddy'),
16
+ 'verifiedEmail': text_type('verified@email.co.uk'),
17
+ 'preferredUsername': text_type('teddy'),
18
+ 'emails': [{'value': text_type('aasd@bwwqwe.pl')}],
19
+ 'name': text_type('ted')
20
+ },
21
+ 'verified@email.co.uk'
22
+ ), (
23
+ {
24
+ 'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
25
+ 'displayName': text_type('teddy'),
26
+ 'preferredUsername': text_type('teddy'),
27
+ 'emails': [{'value': text_type('aasd@bwwqwe.pl')}],
28
+ 'name': text_type('ted')
29
+ },
30
+ 'aasd@bwwqwe.pl'
31
+ ), (
32
+ {
33
+ 'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
34
+ 'displayName': text_type('teddy'),
35
+ 'preferredUsername': text_type('teddy'),
36
+ 'emails': [{}],
37
+ 'name': text_type('ted')
38
+ },
39
+ '2343@facebook.com'
40
+ ), (
41
+ {
42
+ 'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
43
+ 'displayName': text_type('teddy'),
44
+ 'preferredUsername': text_type('teddy'),
45
+ 'emails': [],
46
+ 'name': text_type('ted')
47
+ },
48
+ '2343@facebook.com'
49
+ ), (
50
+ {
51
+ 'accounts': [{'domain': text_type('facebook.com'), 'userid': text_type('2343')}],
52
+ 'displayName': text_type('teddy'),
53
+ 'preferredUsername': text_type('teddy'),
54
+ 'name': text_type('ted')
55
+ },
56
+ '2343@facebook.com'
57
+ ),
58
+ ])
59
+ def test_email_from_context(profile, email):
60
+ """Test email_from_context email getting method."""
61
+ from velruse import AuthenticationComplete
62
+ context = AuthenticationComplete(
63
+ profile,
64
+ {'oauthAccessToken': '7897048593434'},
65
+ text_type('facebook'),
66
+ text_type('facebook')
67
+ )
68
+ view = SocialLoginViews(mock.MagicMock())
69
+ assert view._email_from_context(context) == email
@@ -0,0 +1,24 @@
1
+ """Test fullauth's tools."""
2
+ import pytest
3
+
4
+ from pyramid_fullauth.tools import validate_passsword, password_generator
5
+ from pyramid_fullauth.exceptions import (
6
+ EmptyError, ShortPasswordError, PasswordConfirmMismatchError
7
+ )
8
+
9
+
10
+ @pytest.mark.parametrize(('password', 'exception'),
11
+ [('', EmptyError),
12
+ ('1234', ShortPasswordError),
13
+ ('123456789', PasswordConfirmMismatchError)
14
+ ])
15
+ def test_raises_errors(web_request, password, exception):
16
+ """Test validate_passsword possible exceptions."""
17
+ with pytest.raises(exception):
18
+ validate_passsword(web_request, password)
19
+
20
+
21
+ @pytest.mark.parametrize('length', [5, 6])
22
+ def test_password_generator(length):
23
+ """Test password_generator generating proper length of a random password."""
24
+ assert len(password_generator(length)) == length
@@ -0,0 +1,31 @@
1
+ """User active method tests."""
2
+ import pytest
3
+
4
+ from pyramid_fullauth.models import User
5
+
6
+
7
+ def test_is_active_error():
8
+ """is_active can only be modified on object in session."""
9
+ with pytest.raises(AttributeError):
10
+ user = User()
11
+ user.is_active = True
12
+
13
+
14
+ def test_is_active(db_session, user):
15
+ """
16
+ Test is_active attribute.
17
+
18
+ Setting is_active to True, should result in change of activated_at date being set
19
+ Setting to false, should set deactivated_at.
20
+ """
21
+ user = db_session.merge(user)
22
+
23
+ assert not user.is_active
24
+ assert not user.activated_at
25
+ user.is_active = True
26
+ assert user.is_active
27
+ assert user.activated_at
28
+ assert not user.deactivated_at
29
+ user.is_active = False
30
+ assert not user.is_active
31
+ assert user.deactivated_at
@@ -0,0 +1,61 @@
1
+ """Test admin related behaviour on user model."""
2
+ import pytest
3
+ import transaction
4
+ from pyramid.compat import text_type
5
+
6
+ from pyramid_fullauth.models import User
7
+ from pyramid_fullauth.exceptions import DeleteException
8
+
9
+
10
+ def test_regular_user_not_admin(db_session, user):
11
+ """Regular user is_admin flag test."""
12
+ user = db_session.merge(user)
13
+
14
+ # Default user should not be an admin
15
+ assert not user.is_admin
16
+
17
+
18
+ def test_remove_last_admin(db_session, user):
19
+ """
20
+ Admin user is_admin flag test.
21
+
22
+ At least one admin needed.
23
+ """
24
+ user = db_session.merge(user)
25
+ user.is_admin = True
26
+ transaction.commit()
27
+ user = db_session.merge(user)
28
+
29
+ with pytest.raises(AttributeError):
30
+ user.is_admin = False
31
+
32
+
33
+ def test_delete_admin(db_session, user):
34
+ """Admin user soft delete behaviour - more than one admins present."""
35
+ user = db_session.merge(user)
36
+ user2 = User(
37
+ email=text_type('test2@example.com'),
38
+ address_ip='127.0.0.1',
39
+ password='pass',
40
+ is_admin=True
41
+ )
42
+ db_session.add(user2)
43
+ user.is_admin = True
44
+ transaction.commit()
45
+ user = db_session.merge(user)
46
+
47
+ user.delete()
48
+
49
+ assert user.deleted_at
50
+
51
+
52
+ def test_delete_last_admin(db_session, user):
53
+ """Admin user soft delete - only one admin, no deleting possible."""
54
+ user = db_session.merge(user)
55
+
56
+ user.is_admin = True
57
+ transaction.commit()
58
+ user = db_session.merge(user)
59
+
60
+ with pytest.raises(DeleteException):
61
+ user.delete()
@@ -0,0 +1,37 @@
1
+ """Test email related User methods."""
2
+ from pyramid.compat import text_type
3
+ from pyramid_fullauth.models import User
4
+
5
+ NEW_EMAIL = text_type('new@example.com')
6
+
7
+
8
+ def test_set_new_email():
9
+ """
10
+ Test User.set_new_email method.
11
+
12
+ setting new email should result in setting new_email field,
13
+ and key used to activate the change.
14
+ """
15
+ user = User()
16
+ assert user.email_change_key is None
17
+ assert user.new_email is None
18
+ user.set_new_email(NEW_EMAIL)
19
+
20
+ assert user.new_email == NEW_EMAIL
21
+ assert user.email_change_key
22
+
23
+
24
+ def test_change_email():
25
+ """
26
+ Test User.change_email method.
27
+
28
+ Calling it should copy new email set by set_new_email method
29
+ into regular email field.
30
+ """
31
+ user = User()
32
+ assert not user.email
33
+ user.set_new_email(NEW_EMAIL)
34
+ user.change_email()
35
+
36
+ assert not user.email_change_key
37
+ assert user.email == NEW_EMAIL
@@ -0,0 +1,52 @@
1
+ """Password related user model behaviour."""
2
+ import pytest
3
+ from pyramid.compat import text_type
4
+ import transaction
5
+
6
+ from pyramid_fullauth.models import User
7
+ from pyramid_fullauth.exceptions import EmptyError
8
+
9
+
10
+ def test_hash_checkout(db_session, user):
11
+ """User.check_password correct one."""
12
+ user = db_session.merge(user)
13
+ assert user.check_password(text_type('password1')) is True
14
+
15
+
16
+ def test_hash_checkout_bad(db_session, user):
17
+ """User.check_password() wrong password."""
18
+ user = db_session.merge(user)
19
+ assert user.check_password(text_type('password2')) is False
20
+
21
+
22
+ @pytest.mark.parametrize('password', ['haselko', 'haselko' * 1000])
23
+ def test_password_change(db_session, user, password):
24
+ """User password change."""
25
+ new_password = text_type(password)
26
+
27
+ user = db_session.merge(user)
28
+ old_password = user.password
29
+ old_salt = user._salt
30
+ user.password = new_password
31
+ transaction.commit()
32
+
33
+ user = db_session.query(User).filter(User.username == text_type('u1')).one()
34
+ assert not user.password == old_password
35
+ assert not user._salt == old_salt
36
+ assert user.check_password(new_password)
37
+
38
+
39
+ def test_password_empty(db_session, user):
40
+ """User try to set empty password."""
41
+ user = db_session.merge(user)
42
+
43
+ with pytest.raises(EmptyError):
44
+ user.password = text_type('')
45
+
46
+
47
+ def test_set_reset(db_session, user):
48
+ """User.set_reset - generates reset key for those who forgot their password. """
49
+ user = db_session.merge(user)
50
+ assert not user.reset_key
51
+ user.set_reset()
52
+ assert user.reset_key
@@ -0,0 +1,24 @@
1
+ """Test provider related user methods."""
2
+ from pyramid.compat import text_type
3
+ import transaction
4
+
5
+ from pyramid_fullauth.models import User
6
+ from pyramid_fullauth.models import AuthenticationProvider
7
+
8
+
9
+ def test_user_provider_id(db_session, user):
10
+ """User provider_id returns proper provider identification."""
11
+ user = db_session.merge(user)
12
+ email = user.email
13
+ # Provider does not exists yet
14
+ assert not user.provider_id('email')
15
+
16
+ provider = AuthenticationProvider()
17
+ provider.provider = text_type('email')
18
+ provider.provider_id = email
19
+ user.providers.append(provider)
20
+ transaction.commit()
21
+
22
+ user = db_session.query(User).filter(User.email == email).one()
23
+ # Provider exists
24
+ assert user.provider_id('email') == email
@@ -0,0 +1,29 @@
1
+ """Test user representation."""
2
+ from pyramid.compat import text_type
3
+
4
+ from pyramid_fullauth.models import User
5
+
6
+
7
+ def test_introduce_email():
8
+ """user gets introduced by e-mail only."""
9
+ user = User()
10
+ user.email = text_type('test@test.pl')
11
+ # To string should return concatenated email
12
+ assert str(user) == 'test@...'
13
+
14
+
15
+ def test_introduce_username():
16
+ """user gets introduced by username."""
17
+ user = User()
18
+ # 'User not saved should be represented by \'None\'')
19
+ assert str(user) == 'None'
20
+ user.id = 1
21
+ # if id is set, user should be presented with it
22
+ assert str(user) == '1'
23
+
24
+ user.email = text_type('test@test.pl')
25
+ user.username = text_type('testowy')
26
+ # To string should return username
27
+ assert str(user) == 'testowy'
28
+ # User should be represented by "<User (\'1\')>"
29
+ assert user.__repr__() == "<User ('1')>"