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.
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-34.pyc +0 -0
  9. pyramid_fullauth/__pycache__/auth.cpython-34.pyc +0 -0
  10. pyramid_fullauth/__pycache__/events.cpython-34.pyc +0 -0
  11. pyramid_fullauth/__pycache__/exceptions.cpython-34.pyc +0 -0
  12. pyramid_fullauth/__pycache__/request.cpython-34.pyc +0 -0
  13. pyramid_fullauth/__pycache__/tools.cpython-34.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-34.pyc +0 -0
  20. pyramid_fullauth/models/extensions/__init__.py +39 -0
  21. pyramid_fullauth/models/extensions/__pycache__/__init__.cpython-34.pyc +0 -0
  22. pyramid_fullauth/models/mixins/__init__.py +9 -0
  23. pyramid_fullauth/models/mixins/__pycache__/__init__.cpython-34.pyc +0 -0
  24. pyramid_fullauth/models/mixins/__pycache__/email.cpython-34.pyc +0 -0
  25. pyramid_fullauth/models/mixins/__pycache__/password.cpython-34.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-34.pyc +0 -0
  44. pyramid_fullauth/routing/__pycache__/predicates.cpython-34.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-34.pyc +0 -0
  49. pyramid_fullauth/views/__pycache__/social.cpython-34.pyc +0 -0
  50. pyramid_fullauth/views/basic/__init__.py +5 -0
  51. pyramid_fullauth/views/basic/__pycache__/__init__.cpython-34.pyc +0 -0
  52. pyramid_fullauth/views/basic/__pycache__/activate.cpython-34.pyc +0 -0
  53. pyramid_fullauth/views/basic/__pycache__/forbidden.cpython-34.pyc +0 -0
  54. pyramid_fullauth/views/basic/__pycache__/login.cpython-34.pyc +0 -0
  55. pyramid_fullauth/views/basic/__pycache__/logout.cpython-34.pyc +0 -0
  56. pyramid_fullauth/views/basic/__pycache__/register.cpython-34.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-34.pyc +0 -0
  63. pyramid_fullauth/views/basic/profile/__pycache__/email.cpython-34.pyc +0 -0
  64. pyramid_fullauth/views/basic/profile/__pycache__/password.cpython-34.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-34.pyc +0 -0
  71. tests/__pycache__/conftest.cpython-34.pyc +0 -0
  72. tests/__pycache__/tools.cpython-34.pyc +0 -0
  73. tests/conftest.py +201 -0
  74. tests/tools.py +54 -0
  75. tests/unit/__pycache__/test_default_settings.cpython-34.pyc +0 -0
  76. tests/unit/__pycache__/test_groupfinder.cpython-34.pyc +0 -0
  77. tests/unit/__pycache__/test_social_view.cpython-34.pyc +0 -0
  78. tests/unit/__pycache__/test_tools.cpython-34.pyc +0 -0
  79. tests/unit/__pycache__/test_user_active.cpython-34.pyc +0 -0
  80. tests/unit/__pycache__/test_user_admin.cpython-34.pyc +0 -0
  81. tests/unit/__pycache__/test_user_email.cpython-34.pyc +0 -0
  82. tests/unit/__pycache__/test_user_password.cpython-34.pyc +0 -0
  83. tests/unit/__pycache__/test_user_providers.cpython-34.pyc +0 -0
  84. tests/unit/__pycache__/test_user_repr.cpython-34.pyc +0 -0
  85. tests/unit/__pycache__/test_validators.cpython-34.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-34.pyc +0 -0
  99. tests/views/__pycache__/conftest.cpython-34.pyc +0 -0
  100. tests/views/__pycache__/test_activation.cpython-34.pyc +0 -0
  101. tests/views/__pycache__/test_email.cpython-34.pyc +0 -0
  102. tests/views/__pycache__/test_events.cpython-34.pyc +0 -0
  103. tests/views/__pycache__/test_login.cpython-34.pyc +0 -0
  104. tests/views/__pycache__/test_login_social.cpython-34.pyc +0 -0
  105. tests/views/__pycache__/test_logout.cpython-34.pyc +0 -0
  106. tests/views/__pycache__/test_register.cpython-34.pyc +0 -0
  107. tests/views/__pycache__/test_reset.cpython-34.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
@@ -0,0 +1,5 @@
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
+ """Groups profile views."""
@@ -0,0 +1,108 @@
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 HTTPRedirection, HTTPSeeOther
9
+ from pyramid.security import NO_PERMISSION_REQUIRED
10
+
11
+ from sqlalchemy.orm.exc import NoResultFound
12
+ from pyramid.compat import text_type
13
+ import pyramid_basemodel
14
+
15
+ from pyramid_fullauth.views import BaseView
16
+ from pyramid_fullauth.models import User
17
+
18
+ from pyramid_fullauth.events import BeforeEmailChange
19
+ from pyramid_fullauth.events import AfterEmailChange
20
+ from pyramid_fullauth.events import AfterEmailChangeActivation
21
+ from pyramid_fullauth.exceptions import EmptyError, EmailValidationError
22
+
23
+
24
+ @view_defaults(route_name='email:change', permission='email_change',
25
+ renderer='pyramid_fullauth:resources/templates/email_change.mako')
26
+ class EmailChangeViews(BaseView):
27
+
28
+ """Email change related views."""
29
+
30
+ @view_config(request_method='GET')
31
+ def get(self):
32
+ """Display email change request form."""
33
+ return {'status': True, 'csrf_token': self.request.session.get_csrf_token()}
34
+
35
+ @view_config(request_method='POST', check_csrf='csrf_token')
36
+ @view_config(request_method='POST', check_csrf='csrf_token', xhr=True, renderer="json")
37
+ def post(self):
38
+ """Process email change request."""
39
+ csrf_token = self.request.session.get_csrf_token()
40
+ try:
41
+ pyramid_basemodel.Session.query(User).filter(
42
+ User.email == self.request.POST.get('email', '')
43
+ ).one()
44
+ return {'status': False,
45
+ 'msg': self.request._('User with this email exists',
46
+ domain='pyramid_fullauth'),
47
+ 'csrf_token': csrf_token}
48
+ except NoResultFound:
49
+ pass
50
+
51
+ user = self.request.user
52
+ try:
53
+ self.request.registry.notify(BeforeEmailChange(self.request, user))
54
+ except AttributeError as e:
55
+ return {'status': False, 'msg': text_type(e), 'csrf_token': csrf_token}
56
+
57
+ try:
58
+ user.set_new_email(self.request.POST.get('email', ''))
59
+ except EmptyError:
60
+ return {'status': False,
61
+ 'msg': self.request._(
62
+ 'E-mail is empty',
63
+ domain='pyramid_fullauth'),
64
+ 'csrf_token': csrf_token}
65
+ except EmailValidationError:
66
+ return {'status': False,
67
+ 'msg': self.request._(
68
+ 'Incorrect e-mail format',
69
+ domain='pyramid_fullauth'),
70
+ 'csrf_token': csrf_token}
71
+
72
+ response_values = {
73
+ 'status': True,
74
+ 'msg': self.request._('We sent you email to activate your new email address',
75
+ domain='pyramid_fullauth')
76
+ }
77
+
78
+ try:
79
+ self.request.registry.notify(AfterEmailChange(self.request, user))
80
+ except HTTPRedirection as redirect:
81
+ if self.request.is_xhr:
82
+ response_values['url'] = redirect.location
83
+ return response_values
84
+ else:
85
+ return redirect
86
+ else:
87
+ if self.request.is_xhr:
88
+ return response_values
89
+ else:
90
+ return HTTPSeeOther(location='/')
91
+
92
+
93
+ @view_config(route_name='email:change:continue', permission=NO_PERMISSION_REQUIRED,
94
+ renderer='pyramid_fullauth:resources/templates/email_change.mako')
95
+ class EmailChangeAccept(BaseView):
96
+
97
+ """Display email change success."""
98
+
99
+ def __call__(self):
100
+ """View that changes user email."""
101
+ user = self.request.matchdict.get('user')
102
+ user.change_email()
103
+
104
+ try:
105
+ self.request.registry.notify(AfterEmailChangeActivation(self.request, user))
106
+ except HTTPRedirection as redirect:
107
+ return redirect
108
+ return HTTPSeeOther(location='/')
@@ -0,0 +1,129 @@
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
+ """Profile related views."""
6
+
7
+ from pyramid.view import view_config, view_defaults
8
+ from pyramid.httpexceptions import HTTPRedirection, HTTPSeeOther
9
+ from pyramid.security import NO_PERMISSION_REQUIRED
10
+
11
+ from sqlalchemy.orm.exc import NoResultFound
12
+ from pyramid.compat import text_type
13
+ import pyramid_basemodel
14
+
15
+ from pyramid_fullauth.views import BaseView
16
+ from pyramid_fullauth.models import User, AuthenticationProvider
17
+
18
+ from pyramid_fullauth.events import BeforeReset
19
+ from pyramid_fullauth.events import AfterResetRequest
20
+ from pyramid_fullauth.events import AfterReset
21
+ from pyramid_fullauth.exceptions import ValidateError
22
+ from pyramid_fullauth.tools import validate_passsword
23
+
24
+
25
+ @view_defaults(route_name='password:reset', permission=NO_PERMISSION_REQUIRED,
26
+ renderer='pyramid_fullauth:resources/templates/reset.mako')
27
+ class PasswordResetView(BaseView):
28
+
29
+ """Profile related views."""
30
+
31
+ @view_config()
32
+ def get(self):
33
+ """Password reset request view."""
34
+ csrf_token = self.request.session.get_csrf_token()
35
+
36
+ return {'status': True, 'csrf_token': csrf_token}
37
+
38
+ @view_config(request_method='POST', check_csrf=True)
39
+ def post(self):
40
+ """View processing requests to reset password."""
41
+ csrf_token = self.request.session.get_csrf_token()
42
+
43
+ try:
44
+ user = pyramid_basemodel.Session.query(User).filter(
45
+ User.email == self.request.POST.get('email', '')).one()
46
+ except NoResultFound:
47
+ return {'status': False,
48
+ 'msg': self.request._('user-not-exists',
49
+ default='User does not exists',
50
+ domain='pyramid_fullauth'),
51
+ 'csrf_token': csrf_token}
52
+
53
+ user.set_reset()
54
+ try:
55
+ self.request.registry.notify(AfterResetRequest(self.request, user))
56
+ except HTTPRedirection as redirect:
57
+ return redirect
58
+
59
+ return HTTPSeeOther(location='/')
60
+
61
+
62
+ @view_defaults(route_name='password:reset:continue', permission=NO_PERMISSION_REQUIRED,
63
+ renderer='pyramid_fullauth:resources/templates/reset.proceed.mako')
64
+ class PasswordResetContinueView(BaseView):
65
+
66
+ """
67
+ Password reset views.
68
+
69
+ These views display actual reset password views.
70
+ """
71
+
72
+ @view_config(request_method='GET')
73
+ def get(self):
74
+ """Display actual password reset form."""
75
+ self.request.logout()
76
+ return {
77
+ 'status': True,
78
+ 'csrf_token': self.request.session.get_csrf_token()
79
+ }
80
+
81
+ @view_config(request_method='POST', check_csrf=True)
82
+ def post(self):
83
+ """Validate and possibly accept new email."""
84
+ user = self.request.matchdict.get('user')
85
+
86
+ password = self.request.POST.get('password', None)
87
+ password_confirm = self.request.POST.get('confirm_password', None)
88
+ if password == password_confirm:
89
+
90
+ try:
91
+ self.request.registry.notify(BeforeReset(self.request, user))
92
+ validate_passsword(self.request, password, user)
93
+
94
+ user.reset_key = None
95
+ try:
96
+ pyramid_basemodel.Session.query(AuthenticationProvider).filter(
97
+ AuthenticationProvider.user_id == user.id,
98
+ AuthenticationProvider.provider == text_type('email')
99
+ ).one()
100
+ except NoResultFound:
101
+ user.providers.append(
102
+ AuthenticationProvider(
103
+ provider=text_type('email'),
104
+ provider_id=user.id
105
+ )
106
+ )
107
+
108
+ pyramid_basemodel.Session.flush()
109
+ except (ValidateError, AttributeError) as e:
110
+ return {
111
+ 'status': False,
112
+ 'msg': text_type(e),
113
+ 'csrf_token': self.request.session.get_csrf_token()
114
+ }
115
+
116
+ try:
117
+ self.request.registry.notify(AfterReset(self.request, user))
118
+ except HTTPRedirection as redirect:
119
+ return redirect
120
+ else:
121
+ return {
122
+ 'status': False,
123
+ 'msg': self.request._('password-mismatch',
124
+ default='Password doesn\'t match',
125
+ domain='pyramid_fullauth'),
126
+ 'csrf_token': self.request.session.get_csrf_token()
127
+ }
128
+
129
+ return self.get()
@@ -0,0 +1,111 @@
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, view_defaults
8
+ from pyramid.httpexceptions import HTTPRedirection
9
+ from pyramid.security import NO_PERMISSION_REQUIRED
10
+
11
+ from pyramid.compat import text_type
12
+ import pyramid_basemodel
13
+
14
+ from pyramid_fullauth.views import BaseView
15
+ from pyramid_fullauth.models import User, AuthenticationProvider
16
+ from pyramid_fullauth.events import BeforeRegister, AfterRegister
17
+ from pyramid_fullauth import tools
18
+ from pyramid_fullauth.exceptions import ValidateError
19
+
20
+
21
+ @view_defaults(route_name='register', permission=NO_PERMISSION_REQUIRED,
22
+ renderer="pyramid_fullauth:resources/templates/register.mako")
23
+ class RegisterView(BaseView):
24
+
25
+ """Registration views."""
26
+
27
+ @view_config(request_method='GET')
28
+ def get(self):
29
+ """Display registration form."""
30
+ csrf_token = self.request.session.get_csrf_token()
31
+ return {'status': True, 'msg': None, 'csrf_token': csrf_token, 'errors': {}}
32
+
33
+ @view_config(request_method='POST', check_csrf=True)
34
+ @view_config(request_method='POST', check_csrf=True, xhr=True, renderer="json")
35
+ def post(self):
36
+ """Process registration request."""
37
+ response = {
38
+ 'status': False,
39
+ 'msg': self.request._('You have an error in your registration form',
40
+ domain='pyramid_fullauth'),
41
+ 'csrf_token': self.request.session.get_csrf_token(),
42
+ 'errors': {},
43
+ }
44
+
45
+ user = User()
46
+ user.address_ip = self.request.remote_addr
47
+
48
+ response = self._fillin_user(response, user)
49
+
50
+ if not response['errors']:
51
+ response['status'] = True
52
+ response['msg'] = self.request._(
53
+ 'You have successfully registered', domain='pyramid_fullauth')
54
+
55
+ try:
56
+ self.request.registry.notify(AfterRegister(self.request, user, response))
57
+ except HTTPRedirection as redirect:
58
+ return redirect
59
+
60
+ return response
61
+
62
+ def _fillin_user(self, response, user):
63
+ """
64
+ Fill new user object in, with sent data.
65
+
66
+ :param dict response: response to return from register view
67
+ :param pyramid_fullauth.models.User user: new user object
68
+
69
+ :returns: response
70
+ :rtype: dict
71
+ """
72
+ email = self.request.POST.get('email', text_type(''))
73
+ # here if e-mail is already in database
74
+
75
+ if pyramid_basemodel.Session.query(User).filter(User.email == email).count() != 0:
76
+ response['errors']['email'] = self.request._(
77
+ 'User with given e-mail already exists!',
78
+ domain='pyramid_fullauth')
79
+ try:
80
+ try:
81
+ user.email = email
82
+ except ValidateError as e:
83
+ # do not overwrite existing error
84
+ if 'email' not in response['errors']:
85
+ response['errors']['email'] = text_type(e)
86
+
87
+ if self.config.register.password.require:
88
+ try:
89
+ tools.validate_passsword(self.request,
90
+ self.request.POST.get('password', text_type('')),
91
+ user)
92
+ except ValidateError as e:
93
+ response['errors']['password'] = text_type(e)
94
+ else:
95
+ user.password = tools.password_generator(
96
+ self.config.register.password.length_min)
97
+
98
+ self.request.registry.notify(BeforeRegister(self.request, user, response['errors']))
99
+ if not response['errors']:
100
+ pyramid_basemodel.Session.add(user)
101
+ pyramid_basemodel.Session.flush()
102
+
103
+ # lets add AuthenticationProvider as email!
104
+ user.providers.append(
105
+ AuthenticationProvider(provider=text_type('email'), provider_id=user.id))
106
+ else:
107
+ return response
108
+ except AttributeError as e:
109
+ response['errors']['msg'] = text_type(e)
110
+
111
+ return response
@@ -0,0 +1,221 @@
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
+ """Social login/registration view."""
6
+ import logging
7
+
8
+ from pyramid.view import view_config
9
+ from pyramid.httpexceptions import HTTPRedirection
10
+ from pyramid.security import NO_PERMISSION_REQUIRED
11
+
12
+ from sqlalchemy.orm.exc import NoResultFound
13
+ from sqlalchemy.exc import IntegrityError
14
+ from pyramid.compat import text_type
15
+ import pyramid_basemodel
16
+
17
+ from pyramid_fullauth.views import BaseView
18
+ from pyramid_fullauth.models import User
19
+ from pyramid_fullauth.models import AuthenticationProvider
20
+ from pyramid_fullauth.events import BeforeSocialRegister
21
+ from pyramid_fullauth.events import AfterSocialRegister
22
+ from pyramid_fullauth.events import AfterSocialLogIn
23
+ from pyramid_fullauth.events import SocialAccountAlreadyConnected
24
+ from pyramid_fullauth import tools
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ @view_config(context='velruse.AuthenticationComplete',
30
+ permission=NO_PERMISSION_REQUIRED,
31
+ renderer="pyramid_fullauth:resources/templates/register.mako")
32
+ class SocialLoginViews(BaseView):
33
+
34
+ """Social login views definition."""
35
+
36
+ def set_provider(self, user, provider_name, user_provider_id):
37
+ """
38
+ Set authentication provider on user.
39
+
40
+ This method will connect given provider with given user,
41
+ unless provider_id has already been used on another user.
42
+
43
+ :param pyramid_fullauth.user.User user: user object
44
+ :param str provider_name: provider name
45
+ :param str user_provider_id: user id delivered by given provider
46
+
47
+ :returns: whether user got/was connected or the connection was made with another user.
48
+ :rtype: bool
49
+ """
50
+ if user.id:
51
+ try:
52
+ provider_auth = pyramid_basemodel.Session.query(
53
+ AuthenticationProvider
54
+ ).filter(
55
+ AuthenticationProvider.user_id == user.id,
56
+ AuthenticationProvider.provider == provider_name
57
+ ).one()
58
+ if provider_auth.provider_id != user_provider_id:
59
+ return False
60
+ return True
61
+ except NoResultFound:
62
+ pass
63
+
64
+ provider_auth = AuthenticationProvider(
65
+ provider=provider_name,
66
+ provider_id=user_provider_id
67
+ )
68
+ user.providers.append(provider_auth)
69
+ pyramid_basemodel.Session.flush()
70
+ return True
71
+
72
+ def __call__(self):
73
+ """
74
+ Action provides social authorization functionality.
75
+
76
+ When authorization with facebook or twitter is done successfully
77
+ action tries to find existing user in database,
78
+ if it exists - login this user, otherwise creates new user.
79
+ """
80
+ context = self.request.context
81
+ response_values = {
82
+ 'status': False,
83
+ 'csrf_token': self.request.session.get_csrf_token()
84
+ }
85
+ user = self.request.user
86
+
87
+ if user:
88
+ # Lets try to connect our user
89
+ return self._connect_user(response_values)
90
+
91
+ else:
92
+ try:
93
+ user = pyramid_basemodel.Session.query(
94
+ User
95
+ ).join(AuthenticationProvider).filter(
96
+ AuthenticationProvider.provider == context.provider_name,
97
+ AuthenticationProvider.provider_id == context.profile['accounts'][0]['userid']
98
+ ).one()
99
+ except NoResultFound:
100
+ user = None
101
+
102
+ # If the user for the provider was not found then check
103
+ # if in the DB exists user with the same email
104
+ if not user:
105
+ user = self._register_user()
106
+ try:
107
+ self.request.registry.notify(
108
+ AfterSocialRegister(
109
+ self.request, user, context.profile, response_values))
110
+ except HTTPRedirection as redirect:
111
+ # it's a redirect, let's follow it!
112
+ return redirect
113
+
114
+ # if we're here, user exists,
115
+ try:
116
+ self.request.registry.notify(AfterSocialLogIn(self.request, user, context.profile))
117
+ except HTTPRedirection as redirect:
118
+ # it's a redirect, let's follow it!
119
+ return self.request.login_perform(user, location=redirect.location)
120
+ else:
121
+ return self.request.login_perform(user)
122
+
123
+ def _connect_user(self, response_values):
124
+ """
125
+ Connect user to social account.
126
+
127
+ :param dict response_values:
128
+
129
+ :returns: response values with any message,
130
+ or HTTPRedirection if raised in SocialAccountAlreadyConnected
131
+ :rtype: dict
132
+ """
133
+ context = self.request.context
134
+ user = self.request.user
135
+ try:
136
+ if not self.set_provider(
137
+ user, context.provider_name, context.profile['accounts'][0]['userid']
138
+ ):
139
+ response_values['msg'] = self.request._(
140
+ 'Your account is already connected to other ${provider} account.',
141
+ domain='pyramid_fullauth',
142
+ mapping={'provider': context.provider_name}
143
+ )
144
+ else:
145
+ response_values['status'] = True
146
+ response_values['msg'] = self.request._(
147
+ 'Your account has been connected to ${provider} account.',
148
+ domain='pyramid_fullauth',
149
+ mapping={'provider': context.provider_name}
150
+ )
151
+ except IntegrityError:
152
+ response_values['msg'] = self.request._(
153
+ 'This ${provider} account is already connected with other account.',
154
+ domain='pyramid_fullauth',
155
+ mapping={'provider': context.provider_name}
156
+ )
157
+ try:
158
+ self.request.registry.notify(
159
+ SocialAccountAlreadyConnected(
160
+ self.request, user, context.profile, response_values))
161
+ except HTTPRedirection as redirect:
162
+ # it's a redirect, let's follow it!
163
+ return redirect
164
+ return response_values
165
+
166
+ def _register_user(self):
167
+ """Actually register new user in the system based on context values."""
168
+ context = self.request.context
169
+ email = self._email_from_context(context)
170
+
171
+ try:
172
+ user = pyramid_basemodel.Session.query(User).filter(User.email == email).one()
173
+ # If we are here that means that in the DB exists user with the same email but without the provider
174
+ # then we connect social account to this user
175
+ if not self.set_provider(
176
+ user, context.provider_name, context.profile['accounts'][0]['userid']
177
+ ):
178
+ # authenticating user with different social account than assigned,
179
+ # recogniced by same email address used
180
+ logger.debug('''Authenticated {user.id} connected to
181
+ {context.provider_name} id {connected_id},
182
+ with {userid}'''.format(
183
+ user=user,
184
+ context=context,
185
+ connected_id=user.provider_id(context.provider_name),
186
+ userid=context.profile['accounts'][0]['userid']))
187
+ pyramid_basemodel.Session.flush()
188
+ except NoResultFound:
189
+ length_min = self.config.register.password.length_min
190
+ user = User(
191
+ email=email,
192
+ password=tools.password_generator(length_min),
193
+ address_ip=self.request.remote_addr
194
+ )
195
+ self.request.registry.notify(BeforeSocialRegister(self.request, user, context.profile))
196
+ self.set_provider(user, context.provider_name, context.profile['accounts'][0]['userid'])
197
+ pyramid_basemodel.Session.add(user)
198
+ pyramid_basemodel.Session.flush()
199
+ user.is_active = True
200
+ return user
201
+
202
+ def _email_from_context(self, context):
203
+ """
204
+ Extract or generate email from context values.
205
+
206
+ :param velruse.AuthenticationComplete context: velruse context
207
+ """
208
+ # getting verified email from provider
209
+ if 'verifiedEmail' in context.profile:
210
+ return context.profile['verifiedEmail']
211
+ # getting first of the emails provided by social login provider
212
+ elif 'emails' in context.profile and context.profile['emails'] and 'value' in context.profile['emails'][0]:
213
+ return context.profile['emails'][0]['value']
214
+ # generating some random email address based on social userid and provider domain
215
+ else:
216
+ return text_type(
217
+ '{0}@{1}'.format(
218
+ context.profile['accounts'][0]['userid'],
219
+ context.profile['accounts'][0]['domain']
220
+ )
221
+ )
tests/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Tests."""
Binary file