|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 from logging import WARNING |
|
18 |
|
19 # import interfaces |
|
20 from pyams_form.interfaces.form import IWidgetsSuffixViewletsManager |
|
21 from pyams_skin.layer import IPyAMSLayer |
|
22 from pyams_security.interfaces import AuthenticatedPrincipalEvent, LOGIN_REFERER_KEY, ISecurityManager, ILoginView |
|
23 |
|
24 # import packages |
|
25 from authomatic.adapters import WebObAdapter |
|
26 from authomatic.core import Authomatic |
|
27 from authomatic.providers import oauth1, oauth2 |
|
28 from pyams_template.template import template_config |
|
29 from pyams_utils.registry import query_utility |
|
30 from pyams_viewlet.viewlet import Viewlet, viewlet_config |
|
31 from pyramid.httpexceptions import HTTPNotFound |
|
32 from pyramid.response import Response |
|
33 from pyramid.security import remember |
|
34 from pyramid.view import view_config |
|
35 |
|
36 |
|
37 # |
|
38 # Authomatic social login |
|
39 # |
|
40 |
|
41 @viewlet_config(name='social-login-suffix', layer=IPyAMSLayer, |
|
42 manager=IWidgetsSuffixViewletsManager, view=ILoginView, weight=50) |
|
43 @template_config(template='templates/social-login.pt') |
|
44 class SocialLoginViewletsSuffix(Viewlet): |
|
45 """Social login viewlets suffix""" |
|
46 |
|
47 def __new__(cls, *args, **kwargs): |
|
48 manager = query_utility(ISecurityManager) |
|
49 if not manager.enable_social_login: |
|
50 return None |
|
51 return Viewlet.__new__(cls) |
|
52 |
|
53 @property |
|
54 def use_popup(self): |
|
55 manager = query_utility(ISecurityManager) |
|
56 return manager.social_login_use_popup |
|
57 |
|
58 |
|
59 CONFIG = { |
|
60 |
|
61 'twitter': { |
|
62 'id': 10, |
|
63 # Provider class |
|
64 'class_': oauth1.Twitter, |
|
65 |
|
66 # Twitter is an AuthorizationProvider so we need to set several other properties too: |
|
67 'consumer_key': 'F41qIog95eVs07RupYccokWbk', |
|
68 'consumer_secret': 'yrEYMT3VnkT4yvCsXvaKDWok5gehU6fjz38e2FTphE8BXaapUA', |
|
69 }, |
|
70 |
|
71 'facebook': { |
|
72 'id': 20, |
|
73 # Provider class |
|
74 'class_': oauth2.Facebook, |
|
75 |
|
76 # Facebook is an AuthorizationProvider too. |
|
77 'consumer_key': '417712258385794', |
|
78 'consumer_secret': '0a2bfc72bdc05caff800b8e46fe8ca64', |
|
79 |
|
80 # But it is also an OAuth 2.0 provider and it needs scope. |
|
81 'scope': ['email'], |
|
82 }, |
|
83 |
|
84 'google': { |
|
85 'id': 30, |
|
86 # Provider class |
|
87 'class_': oauth2.Google, |
|
88 'consumer_key': '203791088227-a2nuqdo2t74ebv1n0s8houb4jfmjtp1t.apps.googleusercontent.com', |
|
89 'consumer_secret': 'gsATqmPQASMtC60OACMcOH0i', |
|
90 'scope': ['email'] |
|
91 } |
|
92 } |
|
93 |
|
94 |
|
95 @view_config(route_name='login') |
|
96 def login(request): |
|
97 """Login view for Authautomatic authentication""" |
|
98 # check security manager utility |
|
99 manager = query_utility(ISecurityManager) |
|
100 if (manager is None) or not manager.enable_social_login: |
|
101 raise HTTPNotFound() |
|
102 # store referer |
|
103 session = request.session |
|
104 if LOGIN_REFERER_KEY not in session: |
|
105 referer = request.referer |
|
106 session[LOGIN_REFERER_KEY] = referer |
|
107 # init authomatic |
|
108 provider_name = request.matchdict.get('provider_name') |
|
109 authomatic = Authomatic(config=CONFIG, secret=manager.authomatic_secret, logging_level=WARNING) |
|
110 # perform login |
|
111 response = Response() |
|
112 result = authomatic.login(WebObAdapter(request, response), provider_name) |
|
113 if result: |
|
114 if result.error: |
|
115 pass |
|
116 elif result.user: |
|
117 if not (result.user.id and result.user.name): |
|
118 result.user.update() |
|
119 principal_id = 'oauth:{0}.{1}'.format(provider_name, result.user.id) |
|
120 request.registry.notify(AuthenticatedPrincipalEvent('oauth', |
|
121 principal_id=principal_id, |
|
122 user=result.user)) |
|
123 headers = remember(request, principal_id) |
|
124 response.headerlist.extend(headers) |
|
125 if manager.social_login_use_popup: |
|
126 response.text = result.popup_html() |
|
127 response.status_code = 302 |
|
128 if LOGIN_REFERER_KEY in session: |
|
129 response.location = session[LOGIN_REFERER_KEY] |
|
130 del session[LOGIN_REFERER_KEY] |
|
131 else: |
|
132 response.location = '/' |
|
133 return response |