pyramid-fullauth 0.3.0__py2.7.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 +176 -0
- EGG-INFO/SOURCES.txt +91 -0
- EGG-INFO/dependency_links.txt +1 -0
- EGG-INFO/not-zip-safe +1 -0
- EGG-INFO/requires.txt +17 -0
- EGG-INFO/top_level.txt +2 -0
- pyramid_fullauth/__init__.py +105 -0
- pyramid_fullauth/__init__.pyc +0 -0
- pyramid_fullauth/auth.py +66 -0
- pyramid_fullauth/auth.pyc +0 -0
- pyramid_fullauth/events.py +267 -0
- pyramid_fullauth/events.pyc +0 -0
- pyramid_fullauth/exceptions.py +47 -0
- pyramid_fullauth/exceptions.pyc +0 -0
- pyramid_fullauth/models/__init__.py +189 -0
- pyramid_fullauth/models/__init__.pyc +0 -0
- pyramid_fullauth/models/mixins/__init__.py +9 -0
- pyramid_fullauth/models/mixins/__init__.pyc +0 -0
- pyramid_fullauth/models/mixins/email.py +81 -0
- pyramid_fullauth/models/mixins/email.pyc +0 -0
- pyramid_fullauth/models/mixins/password.py +141 -0
- pyramid_fullauth/models/mixins/password.pyc +0 -0
- pyramid_fullauth/request.py +69 -0
- pyramid_fullauth/request.pyc +0 -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 +14 -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/__init__.pyc +0 -0
- pyramid_fullauth/routing/predicates.py +86 -0
- pyramid_fullauth/routing/predicates.pyc +0 -0
- pyramid_fullauth/tools.py +66 -0
- pyramid_fullauth/tools.pyc +0 -0
- pyramid_fullauth/views/__init__.py +15 -0
- pyramid_fullauth/views/__init__.pyc +0 -0
- pyramid_fullauth/views/basic/__init__.py +5 -0
- pyramid_fullauth/views/basic/__init__.pyc +0 -0
- pyramid_fullauth/views/basic/activate.py +44 -0
- pyramid_fullauth/views/basic/activate.pyc +0 -0
- pyramid_fullauth/views/basic/forbidden.py +47 -0
- pyramid_fullauth/views/basic/forbidden.pyc +0 -0
- pyramid_fullauth/views/basic/login.py +128 -0
- pyramid_fullauth/views/basic/login.pyc +0 -0
- pyramid_fullauth/views/basic/logout.py +28 -0
- pyramid_fullauth/views/basic/logout.pyc +0 -0
- pyramid_fullauth/views/basic/profile/__init__.py +5 -0
- pyramid_fullauth/views/basic/profile/__init__.pyc +0 -0
- pyramid_fullauth/views/basic/profile/email.py +107 -0
- pyramid_fullauth/views/basic/profile/email.pyc +0 -0
- pyramid_fullauth/views/basic/profile/password.py +128 -0
- pyramid_fullauth/views/basic/profile/password.pyc +0 -0
- pyramid_fullauth/views/basic/register.py +110 -0
- pyramid_fullauth/views/basic/register.pyc +0 -0
- pyramid_fullauth/views/social.py +218 -0
- pyramid_fullauth/views/social.pyc +0 -0
- tests/__init__.py +1 -0
- tests/__init__.pyc +0 -0
- tests/conftest.py +190 -0
- tests/conftest.pyc +0 -0
- tests/tools.py +54 -0
- tests/tools.pyc +0 -0
- tests/unit/test_default_settings.py +15 -0
- tests/unit/test_default_settings.pyc +0 -0
- tests/unit/test_groupfinder.py +20 -0
- tests/unit/test_groupfinder.pyc +0 -0
- tests/unit/test_social_view.py +65 -0
- tests/unit/test_social_view.pyc +0 -0
- tests/unit/test_tools.py +24 -0
- tests/unit/test_tools.pyc +0 -0
- tests/unit/test_user_active.py +31 -0
- tests/unit/test_user_active.pyc +0 -0
- tests/unit/test_user_admin.py +61 -0
- tests/unit/test_user_admin.pyc +0 -0
- tests/unit/test_user_email.py +37 -0
- tests/unit/test_user_email.pyc +0 -0
- tests/unit/test_user_password.py +52 -0
- tests/unit/test_user_password.pyc +0 -0
- tests/unit/test_user_providers.py +24 -0
- tests/unit/test_user_providers.pyc +0 -0
- tests/unit/test_user_repr.py +29 -0
- tests/unit/test_user_repr.pyc +0 -0
- tests/unit/test_validators.py +44 -0
- tests/unit/test_validators.pyc +0 -0
- tests/views/__init__.py +1 -0
- tests/views/__init__.pyc +0 -0
- tests/views/conftest.py +15 -0
- tests/views/conftest.pyc +0 -0
- tests/views/test_activation.py +83 -0
- tests/views/test_activation.pyc +0 -0
- tests/views/test_email.py +175 -0
- tests/views/test_email.pyc +0 -0
- tests/views/test_events.py +546 -0
- tests/views/test_events.pyc +0 -0
- tests/views/test_login.py +151 -0
- tests/views/test_login.pyc +0 -0
- tests/views/test_login_social.py +254 -0
- tests/views/test_login_social.pyc +0 -0
- tests/views/test_logout.py +42 -0
- tests/views/test_logout.pyc +0 -0
- tests/views/test_register.py +134 -0
- tests/views/test_register.pyc +0 -0
- tests/views/test_reset.py +100 -0
- tests/views/test_reset.pyc +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
## -*- coding: utf-8 -*-
|
|
2
|
+
<%doc>
|
|
3
|
+
Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
4
|
+
|
|
5
|
+
This module is part of pyramid_fullauth and is released under
|
|
6
|
+
the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
7
|
+
</%doc>
|
|
8
|
+
<%inherit file="pyramid_fullauth:resources/templates/layout.mako" />
|
|
9
|
+
|
|
10
|
+
<h1>${_('Recover your password', domain='pyramid_fullauth')}</h1>
|
|
11
|
+
<div class="row-fluid">
|
|
12
|
+
<div class="span2"></div>
|
|
13
|
+
<div class="span8">
|
|
14
|
+
% if not status:
|
|
15
|
+
<div class="alert alert-error">${_('Error!', domain='pyramid_fullauth')} ${msg}</div>
|
|
16
|
+
% endif
|
|
17
|
+
<form class="form-horizontal" name="reset_password" method="POST" action="${request.current_route_path()}">
|
|
18
|
+
<fieldset>
|
|
19
|
+
<div class="control-group">
|
|
20
|
+
<label class="control-label" for="reset[email]">${_('Your e-mail address', domain='pyramid_fullauth')}:</label>
|
|
21
|
+
<div class="controls">
|
|
22
|
+
<input type="email" placeholder="username@hostname.com" name="email" id="reset[email]"/>
|
|
23
|
+
<input type="hidden" name="csrf_token" value="${csrf_token}" />
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="form-actions">
|
|
27
|
+
<button type="submit" class="btn btn-large">${_('Reset Password', domain='pyramid_fullauth')}</button>
|
|
28
|
+
</div>
|
|
29
|
+
</fieldset>
|
|
30
|
+
</form>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="span2"></div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## -*- coding: utf-8 -*-
|
|
2
|
+
<%doc>
|
|
3
|
+
Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
4
|
+
|
|
5
|
+
This module is part of pyramid_fullauth and is released under
|
|
6
|
+
the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
7
|
+
</%doc>
|
|
8
|
+
<%inherit file="pyramid_fullauth:resources/templates/layout.mako" />
|
|
9
|
+
|
|
10
|
+
<h1>${_('Recover your password - choose new password', domain='pyramid_fullauth')}</h1>
|
|
11
|
+
<div class="row-fluid">
|
|
12
|
+
<div class="span8">
|
|
13
|
+
% if not status:
|
|
14
|
+
<div class="alert alert-error">${_('Error!', domain='pyramid_fullauth')} ${msg}</div>
|
|
15
|
+
% endif
|
|
16
|
+
<form class="form-horizontal" id="reset_password" name="reset_password" method="POST" action="${request.current_route_path()}">
|
|
17
|
+
<input type="hidden" name="csrf_token" value="${csrf_token}" />
|
|
18
|
+
<fieldset>
|
|
19
|
+
<div class="control-group">
|
|
20
|
+
<label class="control-label" for="password">${_('Password', domain='pyramid_fullauth')}:</label>
|
|
21
|
+
<div class="controls">
|
|
22
|
+
<input type="password" id="password" name="password" placeholder="minimum 6 characters"/>
|
|
23
|
+
<span class="help-inline" id="password_status"></span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="control-group">
|
|
27
|
+
<label class="control-label" for="confirm_password">${_('Confirm password', domain='pyramid_fullauth')}:</label>
|
|
28
|
+
<div class="controls">
|
|
29
|
+
<input type="password" name="confirm_password" id="confirm_password"/>
|
|
30
|
+
<span class="help-inline" id="confirm_password_status"></span>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="form-actions">
|
|
34
|
+
<button type="submit" class="btn btn-large">${_('Reset Password', domain='pyramid_fullauth')}</button>
|
|
35
|
+
</div>
|
|
36
|
+
</fieldset>
|
|
37
|
+
</form>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="span2"></div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
Binary file
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Routing predicate definitions."""
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
8
|
+
from pyramid.config.predicates import CheckCSRFTokenPredicate
|
|
9
|
+
from pyramid.httpexceptions import HTTPUnauthorized
|
|
10
|
+
import pyramid_basemodel
|
|
11
|
+
|
|
12
|
+
from pyramid_fullauth.models import User
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def reset_hash(info, request):
|
|
16
|
+
"""
|
|
17
|
+
Check whether reset hash is correct.
|
|
18
|
+
|
|
19
|
+
:param dict info: pyramid info dict with path fragments and info
|
|
20
|
+
:param pyramid.request.Request request: request object
|
|
21
|
+
|
|
22
|
+
:returns: whether reset hash exists or not
|
|
23
|
+
:rtype: bool
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
reset_hash = info['match'].get('hash', None)
|
|
27
|
+
if reset_hash:
|
|
28
|
+
try:
|
|
29
|
+
info['match']['user'] = pyramid_basemodel.Session.query(User).filter(User.reset_key == reset_hash).one()
|
|
30
|
+
return True
|
|
31
|
+
except NoResultFound:
|
|
32
|
+
pass
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def change_email_hash(info, request):
|
|
37
|
+
"""
|
|
38
|
+
Check whether change email hash is correct.
|
|
39
|
+
|
|
40
|
+
:param dict info: pyramid info dict with path fragments and info
|
|
41
|
+
:param pyramid.request.Request request: request object
|
|
42
|
+
|
|
43
|
+
:returns: whether change email hash exists or not
|
|
44
|
+
:rtype: bool
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
change_email_hash = info['match'].get('hash', None)
|
|
48
|
+
if change_email_hash:
|
|
49
|
+
try:
|
|
50
|
+
info['match']['user'] = pyramid_basemodel.Session.query(User).filter(User.email_change_key == change_email_hash).one()
|
|
51
|
+
return True
|
|
52
|
+
except NoResultFound:
|
|
53
|
+
pass
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CSRFCheckPredicate(CheckCSRFTokenPredicate):
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
Run csrf check dependant on configuration.
|
|
61
|
+
|
|
62
|
+
.. note::
|
|
63
|
+
|
|
64
|
+
Raises HTTPUnauthorized exception if check fails.
|
|
65
|
+
|
|
66
|
+
:raises: pyramid.httpexceptions.HTTPUnauthorized
|
|
67
|
+
|
|
68
|
+
:returns: True if check succeeds or turned off.
|
|
69
|
+
:rtype: bool
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __call__(self, context, request):
|
|
74
|
+
"""
|
|
75
|
+
Run predicate check.
|
|
76
|
+
|
|
77
|
+
:param context:
|
|
78
|
+
:param pyramid.request.Request request:
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
if request.registry['config'].fullauth.check_csrf:
|
|
82
|
+
result = CheckCSRFTokenPredicate.__call__(self, context, request)
|
|
83
|
+
if not result:
|
|
84
|
+
raise HTTPUnauthorized
|
|
85
|
+
|
|
86
|
+
return True
|
|
Binary file
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Additional tools used in pyramid_fullauth."""
|
|
6
|
+
|
|
7
|
+
import string
|
|
8
|
+
from random import choice
|
|
9
|
+
from pyramid_fullauth.exceptions import (
|
|
10
|
+
EmptyError, ShortPasswordError, PasswordConfirmMismatchError
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def password_generator(length, chars=(string.letters + string.digits + string.punctuation)):
|
|
15
|
+
"""
|
|
16
|
+
Generate random password.
|
|
17
|
+
|
|
18
|
+
.. warning::
|
|
19
|
+
|
|
20
|
+
TODO: tests!
|
|
21
|
+
|
|
22
|
+
:param int length: length of a password to Generates
|
|
23
|
+
:param list chars: list of characters from which to choose
|
|
24
|
+
:returns: password
|
|
25
|
+
:rtype: str
|
|
26
|
+
"""
|
|
27
|
+
return u''.join([choice(chars) for i in range(length)])
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def validate_passsword(request, password, user=None):
|
|
31
|
+
"""
|
|
32
|
+
Validate password properly.
|
|
33
|
+
|
|
34
|
+
.. note::
|
|
35
|
+
|
|
36
|
+
If no user provided, password is just validated
|
|
37
|
+
|
|
38
|
+
:param pyramid.request.Request request: request object
|
|
39
|
+
:param str password: password to be set
|
|
40
|
+
:param pyramid_fullauth.models.User user: user object
|
|
41
|
+
|
|
42
|
+
:raises: pyramid_fullauth.exceptions.ValidateError
|
|
43
|
+
"""
|
|
44
|
+
password_config = request.registry['config'].fullauth.register.password
|
|
45
|
+
if not password:
|
|
46
|
+
raise EmptyError(
|
|
47
|
+
request._('Please enter your password',
|
|
48
|
+
domain='pyramid_fullauth'))
|
|
49
|
+
|
|
50
|
+
if password_config['length_min'] and\
|
|
51
|
+
len(password) < password_config['length_min']:
|
|
52
|
+
raise ShortPasswordError(
|
|
53
|
+
request._('Password is too short',
|
|
54
|
+
domain='pyramid_fullauth'))
|
|
55
|
+
|
|
56
|
+
# here if password doesn't match
|
|
57
|
+
if password_config['confirm']:
|
|
58
|
+
confirm_password = request.POST.get('confirm_password', u'')
|
|
59
|
+
if password != confirm_password:
|
|
60
|
+
raise PasswordConfirmMismatchError(
|
|
61
|
+
request._('password-mismatch',
|
|
62
|
+
default='Passwords don\'t match',
|
|
63
|
+
domain='pyramid_fullauth'))
|
|
64
|
+
|
|
65
|
+
if user:
|
|
66
|
+
user.password = password
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Views submodule."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseView(object):
|
|
9
|
+
|
|
10
|
+
"""basic view class."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, request):
|
|
13
|
+
"""Common init for views."""
|
|
14
|
+
self.request = request
|
|
15
|
+
self.config = request.registry['config'].fullauth
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Registration related views."""
|
|
6
|
+
|
|
7
|
+
from pyramid.view import view_config
|
|
8
|
+
from pyramid.httpexceptions import HTTPFound
|
|
9
|
+
from pyramid.security import NO_PERMISSION_REQUIRED
|
|
10
|
+
|
|
11
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
12
|
+
import pyramid_basemodel
|
|
13
|
+
|
|
14
|
+
from pyramid_fullauth.views import BaseView
|
|
15
|
+
from pyramid_fullauth.models import User
|
|
16
|
+
from pyramid_fullauth.events import AfterActivate
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@view_config(route_name='register:activate', permission=NO_PERMISSION_REQUIRED,
|
|
20
|
+
renderer="pyramid_fullauth:resources/templates/activate.mako")
|
|
21
|
+
class ActivateView(BaseView):
|
|
22
|
+
|
|
23
|
+
"""Activate account views."""
|
|
24
|
+
|
|
25
|
+
def __call__(self):
|
|
26
|
+
"""Process account activation."""
|
|
27
|
+
activate_hash = self.request.matchdict.get('hash')
|
|
28
|
+
user = None
|
|
29
|
+
response = {}
|
|
30
|
+
response['status'] = True
|
|
31
|
+
if activate_hash:
|
|
32
|
+
try:
|
|
33
|
+
user = pyramid_basemodel.Session.query(User).filter(User.activate_key == activate_hash).one()
|
|
34
|
+
if not user.is_active:
|
|
35
|
+
user.is_active = True
|
|
36
|
+
except NoResultFound:
|
|
37
|
+
response['status'] = False
|
|
38
|
+
try:
|
|
39
|
+
self.request.registry.notify(AfterActivate(self.request, user))
|
|
40
|
+
except HTTPFound as e:
|
|
41
|
+
# it's a redirect, let's follow it!
|
|
42
|
+
return e
|
|
43
|
+
|
|
44
|
+
return response
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Forbidden views."""
|
|
6
|
+
|
|
7
|
+
from pyramid.view import forbidden_view_config
|
|
8
|
+
from pyramid.security import authenticated_userid
|
|
9
|
+
from pyramid.httpexceptions import HTTPFound
|
|
10
|
+
|
|
11
|
+
from pyramid_fullauth.views import BaseView
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ForbiddenViews(BaseView):
|
|
15
|
+
|
|
16
|
+
"""Forbidden related views."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, request):
|
|
19
|
+
"""Set all responses status to 403 by default."""
|
|
20
|
+
super(ForbiddenViews, self).__init__(request)
|
|
21
|
+
self.request.response.status_code = 403
|
|
22
|
+
|
|
23
|
+
@forbidden_view_config(renderer='pyramid_fullauth:resources/templates/403.mako')
|
|
24
|
+
def forbidden(self):
|
|
25
|
+
"""Forbidden page view."""
|
|
26
|
+
# do not allow a user to login if they are already logged in
|
|
27
|
+
if authenticated_userid(self.request):
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
loc = self.request.route_path('login', _query=(('after', self.request.path),))
|
|
31
|
+
return HTTPFound(location=loc)
|
|
32
|
+
|
|
33
|
+
@forbidden_view_config(xhr=True, renderer='json')
|
|
34
|
+
def forbidden_json(self):
|
|
35
|
+
"""Forbidden xhr response."""
|
|
36
|
+
# do not allow a user to login if they are already logged in
|
|
37
|
+
if authenticated_userid(self.request):
|
|
38
|
+
return {'status': False, 'msg':
|
|
39
|
+
self.request._('forbidden-notallowed',
|
|
40
|
+
default='You are not allowed to use this function',
|
|
41
|
+
domain='pyramid_fullauth')}
|
|
42
|
+
|
|
43
|
+
return {'status': False,
|
|
44
|
+
'msg': self.request._('forbidden-login',
|
|
45
|
+
default='You have to be logged in to use this function',
|
|
46
|
+
domain='pyramid_fullauth'),
|
|
47
|
+
'login_url': self.request.route_path('login')}
|
|
Binary file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Authorisation related views."""
|
|
6
|
+
|
|
7
|
+
from pyramid.view import view_config
|
|
8
|
+
from pyramid.view import view_defaults
|
|
9
|
+
from pyramid.httpexceptions import HTTPFound
|
|
10
|
+
from pyramid.security import authenticated_userid
|
|
11
|
+
from pyramid.security import NO_PERMISSION_REQUIRED
|
|
12
|
+
|
|
13
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
14
|
+
import pyramid_basemodel
|
|
15
|
+
|
|
16
|
+
from pyramid_fullauth.views import BaseView
|
|
17
|
+
from pyramid_fullauth.models import User
|
|
18
|
+
from pyramid_fullauth.events import BeforeLogIn
|
|
19
|
+
from pyramid_fullauth.events import AfterLogIn
|
|
20
|
+
from pyramid_fullauth.events import AlreadyLoggedIn
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@view_defaults(route_name='login', permission=NO_PERMISSION_REQUIRED,
|
|
24
|
+
renderer='pyramid_fullauth:resources/templates/login.mako')
|
|
25
|
+
class BaseLoginView(BaseView):
|
|
26
|
+
|
|
27
|
+
"""Basic logic for login views."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, request):
|
|
30
|
+
"""Prepare login views."""
|
|
31
|
+
super(BaseLoginView, self).__init__(request)
|
|
32
|
+
|
|
33
|
+
self.response = {
|
|
34
|
+
'status': False,
|
|
35
|
+
'msg': self.request._('Login error', domain='pyramid_fullauth'),
|
|
36
|
+
'after': self.request.params.get(
|
|
37
|
+
'after', self.request.referer or '/'),
|
|
38
|
+
'csrf_token': self.request.session.get_csrf_token()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def _redirect_authenticated_user(self):
|
|
42
|
+
"""Redirect already logged in user away from login page."""
|
|
43
|
+
redirect = HTTPFound(location=self.response['after'])
|
|
44
|
+
try:
|
|
45
|
+
self.request.registry.notify(AlreadyLoggedIn(self.request))
|
|
46
|
+
except HTTPFound as redirect:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
if self.request.is_xhr:
|
|
50
|
+
self.response['status'] = True
|
|
51
|
+
self.response['msg'] = self.request._(
|
|
52
|
+
'Already logged in!',
|
|
53
|
+
domain='pyramid_fullauth')
|
|
54
|
+
self.response['after'] = redirect.location
|
|
55
|
+
return self.response
|
|
56
|
+
else:
|
|
57
|
+
return redirect
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@view_config(request_method='GET')
|
|
61
|
+
class LoginView(BaseLoginView):
|
|
62
|
+
|
|
63
|
+
"""Login view."""
|
|
64
|
+
|
|
65
|
+
def __call__(self):
|
|
66
|
+
"""Display login page."""
|
|
67
|
+
if authenticated_userid(self.request):
|
|
68
|
+
return self._redirect_authenticated_user()
|
|
69
|
+
|
|
70
|
+
self.request.registry.notify(BeforeLogIn(self.request, None))
|
|
71
|
+
|
|
72
|
+
self.response['status'] = True
|
|
73
|
+
del self.response['msg']
|
|
74
|
+
return self.response
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@view_config(request_method='POST', check_csrf=True)
|
|
78
|
+
@view_config(request_method='POST', check_csrf=True, xhr=True, renderer="json")
|
|
79
|
+
class LoginViewPost(BaseLoginView):
|
|
80
|
+
|
|
81
|
+
"""Login view POST method."""
|
|
82
|
+
|
|
83
|
+
def __call__(self):
|
|
84
|
+
"""Login action."""
|
|
85
|
+
if authenticated_userid(self.request):
|
|
86
|
+
return self._redirect_authenticated_user()
|
|
87
|
+
|
|
88
|
+
email = self.request.POST.get('email', '')
|
|
89
|
+
password = self.request.POST.get('password', '')
|
|
90
|
+
try:
|
|
91
|
+
user = pyramid_basemodel.Session.query(User).filter(User.email == email).one()
|
|
92
|
+
except NoResultFound:
|
|
93
|
+
self.request.registry.notify(BeforeLogIn(self.request, None))
|
|
94
|
+
|
|
95
|
+
self.response['msg'] = self.request._('Wrong e-mail or password.',
|
|
96
|
+
domain='pyramid_fullauth')
|
|
97
|
+
return self.response
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
self.request.registry.notify(BeforeLogIn(self.request, user))
|
|
101
|
+
except AttributeError as e:
|
|
102
|
+
self.response['msg'] = str(e)
|
|
103
|
+
return self.response
|
|
104
|
+
|
|
105
|
+
if user.check_password(password):
|
|
106
|
+
|
|
107
|
+
login_kwargs = {'remember_me': self.request.POST.get('remember')}
|
|
108
|
+
try:
|
|
109
|
+
# if remember in POST set cookie timeout to one month
|
|
110
|
+
self.request.registry.notify(AfterLogIn(self.request, user))
|
|
111
|
+
except AttributeError as e:
|
|
112
|
+
self.response['msg'] = str(e)
|
|
113
|
+
return self.response
|
|
114
|
+
except HTTPFound as redirect:
|
|
115
|
+
login_kwargs['location'] = redirect.location
|
|
116
|
+
|
|
117
|
+
redirect = self.request.login_perform(user, **login_kwargs)
|
|
118
|
+
if self.request.is_xhr:
|
|
119
|
+
self.response['status'] = True
|
|
120
|
+
del self.response['msg']
|
|
121
|
+
self.response['after'] = redirect.location
|
|
122
|
+
return self.response
|
|
123
|
+
else:
|
|
124
|
+
return redirect
|
|
125
|
+
else:
|
|
126
|
+
self.response['msg'] = self.request._('Wrong e-mail or password.',
|
|
127
|
+
domain='pyramid_fullauth')
|
|
128
|
+
return self.response
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""De-authentication related view."""
|
|
6
|
+
|
|
7
|
+
from pyramid.view import view_config
|
|
8
|
+
from pyramid.httpexceptions import HTTPFound
|
|
9
|
+
from pyramid.security import NO_PERMISSION_REQUIRED
|
|
10
|
+
|
|
11
|
+
from pyramid_fullauth.views import BaseView
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@view_config(route_name='logout', permission=NO_PERMISSION_REQUIRED)
|
|
15
|
+
class LogoutView(BaseView):
|
|
16
|
+
|
|
17
|
+
"""Logout view."""
|
|
18
|
+
|
|
19
|
+
def __call__(self):
|
|
20
|
+
"""Logout action."""
|
|
21
|
+
location = '/'
|
|
22
|
+
if self.config.redirects.logout:
|
|
23
|
+
location = self.request.route_path(self.config.redirects.logout)
|
|
24
|
+
# forget headers
|
|
25
|
+
self.request.logout()
|
|
26
|
+
|
|
27
|
+
return HTTPFound(location=location,
|
|
28
|
+
headers=self.request.response.headers)
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
|
|
2
|
+
#
|
|
3
|
+
# This module is part of pyramid_fullauth and is released under
|
|
4
|
+
# the MIT License (MIT): http://opensource.org/licenses/MIT
|
|
5
|
+
"""Email related views."""
|
|
6
|
+
|
|
7
|
+
from pyramid.view import view_config, view_defaults
|
|
8
|
+
from pyramid.httpexceptions import HTTPFound
|
|
9
|
+
from pyramid.security import NO_PERMISSION_REQUIRED
|
|
10
|
+
|
|
11
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
12
|
+
import pyramid_basemodel
|
|
13
|
+
|
|
14
|
+
from pyramid_fullauth.views import BaseView
|
|
15
|
+
from pyramid_fullauth.models import User
|
|
16
|
+
|
|
17
|
+
from pyramid_fullauth.events import BeforeEmailChange
|
|
18
|
+
from pyramid_fullauth.events import AfterEmailChange
|
|
19
|
+
from pyramid_fullauth.events import AfterEmailChangeActivation
|
|
20
|
+
from pyramid_fullauth.exceptions import EmptyError, EmailValidationError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@view_defaults(route_name='email:change', permission='email_change',
|
|
24
|
+
renderer='pyramid_fullauth:resources/templates/email_change.mako')
|
|
25
|
+
class EmailChangeViews(BaseView):
|
|
26
|
+
|
|
27
|
+
"""Email change related views."""
|
|
28
|
+
|
|
29
|
+
@view_config(request_method='GET')
|
|
30
|
+
def get(self):
|
|
31
|
+
"""Display email change request form."""
|
|
32
|
+
return {'status': True, 'csrf_token': self.request.session.get_csrf_token()}
|
|
33
|
+
|
|
34
|
+
@view_config(request_method='POST', check_csrf='csrf_token')
|
|
35
|
+
@view_config(request_method='POST', check_csrf='csrf_token', xhr=True, renderer="json")
|
|
36
|
+
def post(self):
|
|
37
|
+
"""Process email change request."""
|
|
38
|
+
csrf_token = self.request.session.get_csrf_token()
|
|
39
|
+
try:
|
|
40
|
+
pyramid_basemodel.Session.query(User).filter(
|
|
41
|
+
User.email == self.request.POST.get('email', '')
|
|
42
|
+
).one()
|
|
43
|
+
return {'status': False,
|
|
44
|
+
'msg': self.request._('User with this email exists',
|
|
45
|
+
domain='pyramid_fullauth'),
|
|
46
|
+
'csrf_token': csrf_token}
|
|
47
|
+
except NoResultFound:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
user = self.request.user
|
|
51
|
+
try:
|
|
52
|
+
self.request.registry.notify(BeforeEmailChange(self.request, user))
|
|
53
|
+
except AttributeError as e:
|
|
54
|
+
return {'status': False, 'msg': e.message, 'csrf_token': csrf_token}
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
user.set_new_email(self.request.POST.get('email', ''))
|
|
58
|
+
except EmptyError:
|
|
59
|
+
return {'status': False,
|
|
60
|
+
'msg': self.request._(
|
|
61
|
+
'E-mail is empty',
|
|
62
|
+
domain='pyramid_fullauth'),
|
|
63
|
+
'csrf_token': csrf_token}
|
|
64
|
+
except EmailValidationError:
|
|
65
|
+
return {'status': False,
|
|
66
|
+
'msg': self.request._(
|
|
67
|
+
'Incorrect e-mail format',
|
|
68
|
+
domain='pyramid_fullauth'),
|
|
69
|
+
'csrf_token': csrf_token}
|
|
70
|
+
|
|
71
|
+
response_values = {
|
|
72
|
+
'status': True,
|
|
73
|
+
'msg': self.request._('We sent you email to activate your new email address',
|
|
74
|
+
domain='pyramid_fullauth')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
self.request.registry.notify(AfterEmailChange(self.request, user))
|
|
79
|
+
except HTTPFound as redirect:
|
|
80
|
+
if self.request.is_xhr:
|
|
81
|
+
response_values['url'] = redirect.location
|
|
82
|
+
return response_values
|
|
83
|
+
else:
|
|
84
|
+
return redirect
|
|
85
|
+
else:
|
|
86
|
+
if self.request.is_xhr:
|
|
87
|
+
return response_values
|
|
88
|
+
else:
|
|
89
|
+
return HTTPFound(location='/')
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@view_config(route_name='email:change:continue', permission=NO_PERMISSION_REQUIRED,
|
|
93
|
+
renderer='pyramid_fullauth:resources/templates/email_change.mako')
|
|
94
|
+
class EmailChangeAccept(BaseView):
|
|
95
|
+
|
|
96
|
+
"""Display email change success."""
|
|
97
|
+
|
|
98
|
+
def __call__(self):
|
|
99
|
+
"""View that changes user email."""
|
|
100
|
+
user = self.request.matchdict.get('user')
|
|
101
|
+
user.change_email()
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
self.request.registry.notify(AfterEmailChangeActivation(self.request, user))
|
|
105
|
+
except HTTPFound as redirect:
|
|
106
|
+
return redirect
|
|
107
|
+
return HTTPFound(location='/')
|
|
Binary file
|