|
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 |
|
18 # import interfaces |
|
19 from pyams_security.interfaces import ISecurityManager, ICredentialsPlugin, IAuthenticationPlugin, \ |
|
20 IDirectoryPlugin, AuthenticatedPrincipalEvent |
|
21 from pyramid.interfaces import IAuthenticationPolicy |
|
22 |
|
23 # import packages |
|
24 from pyams_security.principal import UnknownPrincipal, MissingPrincipal |
|
25 from pyams_utils.registry import query_utility |
|
26 from pyams_utils.request import check_request |
|
27 from pyramid.authentication import AuthTktCookieHelper |
|
28 from pyramid.decorator import reify |
|
29 from pyramid.security import Everyone, Authenticated |
|
30 from zope.container.folder import Folder |
|
31 from zope.interface import implementer |
|
32 from zope.schema.fieldproperty import FieldProperty |
|
33 |
|
34 |
|
35 @implementer(ISecurityManager) |
|
36 class SecurityManager(Folder): |
|
37 """Security manager utility""" |
|
38 |
|
39 enable_social_login = FieldProperty(ISecurityManager['enable_social_login']) |
|
40 authomatic_secret = FieldProperty(ISecurityManager['authomatic_secret']) |
|
41 social_login_use_popup = FieldProperty(ISecurityManager['social_login_use_popup']) |
|
42 open_registration = FieldProperty(ISecurityManager['open_registration']) |
|
43 users_folder = FieldProperty(ISecurityManager['users_folder']) |
|
44 |
|
45 authentication_plugins_names = FieldProperty(ISecurityManager['authentication_plugins_names']) |
|
46 directory_plugins_names = FieldProperty(ISecurityManager['directory_plugins_names']) |
|
47 |
|
48 @property |
|
49 def credentials_plugins_names(self): |
|
50 request = check_request() |
|
51 policy = request.registry.queryUtility(IAuthenticationPolicy) |
|
52 return policy.credentials_names |
|
53 |
|
54 def __setitem__(self, key, value): |
|
55 super(SecurityManager, self).__setitem__(key, value) |
|
56 if IAuthenticationPlugin.providedBy(value): |
|
57 self.authentication_plugins_names += (key,) |
|
58 if IDirectoryPlugin.providedBy(value): |
|
59 self.directory_plugins_names += (key,) |
|
60 |
|
61 def __delitem__(self, key): |
|
62 super(SecurityManager, self).__delitem__(key) |
|
63 if key in self.authentication_plugins_names: |
|
64 del self.authentication_plugins_names[self.authentication_plugins_names.index(key)] |
|
65 if key in self.directory_plugins_names: |
|
66 del self.directory_plugins_names[self.directory_plugins_names.index(key)] |
|
67 |
|
68 def get_plugin(self, name): |
|
69 if name in self.credentials_plugins_names: |
|
70 return query_utility(ICredentialsPlugin, name=name) |
|
71 elif name: |
|
72 return self.get(name) |
|
73 |
|
74 def get_credentials_plugins(self, request=None): |
|
75 if request is None: |
|
76 request = check_request() |
|
77 policy = request.registry.queryUtility(IAuthenticationPolicy) |
|
78 for plugin in policy.credentials_plugins: |
|
79 if plugin is not None: |
|
80 yield plugin |
|
81 |
|
82 def get_authentication_plugins(self): |
|
83 for name in self.authentication_plugins_names or (): |
|
84 plugin = self.get(name) |
|
85 if IAuthenticationPlugin.providedBy(plugin): |
|
86 yield plugin |
|
87 |
|
88 def get_directory_plugins(self): |
|
89 for name in self.directory_plugins_names or (): |
|
90 plugin = self.get(name) |
|
91 if IDirectoryPlugin.providedBy(plugin): |
|
92 yield plugin |
|
93 |
|
94 # IAuthenticationInfo interface methods |
|
95 def extract_credentials(self, request, **kwargs): |
|
96 for plugin in self.get_credentials_plugins(): |
|
97 credentials = plugin.extract_credentials(request, **kwargs) |
|
98 if credentials: |
|
99 return credentials |
|
100 |
|
101 def authenticate(self, credentials, request): |
|
102 for plugin in self.get_authentication_plugins(): |
|
103 principal_id = plugin.authenticate(credentials, request) |
|
104 if principal_id is not None: |
|
105 request.registry.notify(AuthenticatedPrincipalEvent(plugin.prefix, principal_id)) |
|
106 return principal_id |
|
107 |
|
108 def authenticated_userid(self, request): |
|
109 credentials = self.extract_credentials(request) |
|
110 if credentials is None: |
|
111 return None |
|
112 principal = self.authenticate(credentials, request) |
|
113 if principal is not None: |
|
114 principal = self.get_principal(principal.id) |
|
115 if principal is not None: |
|
116 return principal.id |
|
117 |
|
118 def effective_principals(self, principal_id): |
|
119 principals = set() |
|
120 for plugin in self.get_directory_plugins(): |
|
121 principals |= set(plugin.get_all_principals(principal_id)) |
|
122 return principals |
|
123 |
|
124 # IDirectoryPlugin interface methods |
|
125 def get_principal(self, principal_id): |
|
126 if not principal_id: |
|
127 return UnknownPrincipal |
|
128 for plugin in self.get_directory_plugins(): |
|
129 principal = plugin.get_principal(principal_id) |
|
130 if principal is not None: |
|
131 return principal |
|
132 return MissingPrincipal(id=principal_id) |
|
133 |
|
134 def find_principals(self, query): |
|
135 principals = set() |
|
136 for plugin in self.get_directory_plugins(): |
|
137 principals |= set(plugin.find_principals(query) or set()) |
|
138 return sorted(principals, key=lambda x: x.title) |
|
139 |
|
140 |
|
141 @implementer(IAuthenticationPolicy) |
|
142 class PyAMSAuthenticationPolicy(object): |
|
143 """PyAMS authentication policy |
|
144 |
|
145 This authentication policy relies on a registered ISecurityManager utility. |
|
146 Use same authentication ticket as AuthTktAuthenticationPolicy. |
|
147 |
|
148 ``credentials`` is the list of credentials extraction utilities which can be |
|
149 used to get credentials. |
|
150 |
|
151 See `pyramid.authentication.AuthTktAuthenticationPolicy` to get description |
|
152 of other constructor arguments. |
|
153 """ |
|
154 |
|
155 def __init__(self, secret, |
|
156 credentials=('http', 'session'), |
|
157 cookie_name='auth_ticket', |
|
158 secure=False, |
|
159 include_ip=False, |
|
160 timeout=None, |
|
161 reissue_time=None, |
|
162 max_age=None, |
|
163 path="/", |
|
164 http_only=False, |
|
165 wild_domain=True, |
|
166 hashalg='sha256', |
|
167 parent_domain=False, |
|
168 domain=None): |
|
169 self.credentials_names = credentials |
|
170 self.cookie = AuthTktCookieHelper(secret, |
|
171 cookie_name=cookie_name, |
|
172 secure=secure, |
|
173 include_ip=include_ip, |
|
174 timeout=timeout, |
|
175 reissue_time=reissue_time, |
|
176 max_age=max_age, |
|
177 http_only=http_only, |
|
178 path=path, |
|
179 wild_domain=wild_domain, |
|
180 hashalg=hashalg, |
|
181 parent_domain=parent_domain, |
|
182 domain=domain) |
|
183 |
|
184 @reify |
|
185 def credentials_plugins(self): |
|
186 return [query_utility(ICredentialsPlugin, name=name) |
|
187 for name in self.credentials_names] |
|
188 |
|
189 def _get_security_manager(self, request): |
|
190 return query_utility(ISecurityManager) |
|
191 |
|
192 def unauthenticated_userid(self, request): |
|
193 result = self.cookie.identify(request) |
|
194 if result: |
|
195 return result['userid'] |
|
196 for plugin in self.credentials_plugins: |
|
197 if plugin is not None: |
|
198 credentials = plugin.extract_credentials(request) |
|
199 if credentials is not None: |
|
200 return credentials.id |
|
201 |
|
202 def authenticated_userid(self, request): |
|
203 principal_id = self.unauthenticated_userid(request) |
|
204 if principal_id: |
|
205 return principal_id |
|
206 manager = self._get_security_manager(request) |
|
207 if manager is not None: |
|
208 return manager.authenticated_userid(request) |
|
209 |
|
210 def effective_principals(self, request): |
|
211 principals = {Everyone} |
|
212 principal_id = self.unauthenticated_userid(request) |
|
213 if principal_id: |
|
214 principals.add(Authenticated) |
|
215 principals.add(principal_id) |
|
216 manager = self._get_security_manager(request) |
|
217 if manager is not None: |
|
218 principals |= set(manager.effective_principals(principal_id)) |
|
219 print(principals) |
|
220 return principals |
|
221 |
|
222 def remember(self, request, principal, **kw): |
|
223 return self.cookie.remember(request, principal, **kw) |
|
224 |
|
225 def forget(self, request): |
|
226 return self.cookie.forget(request) |
|
227 |
|
228 |
|
229 def get_principal(request): |
|
230 """Get principal associated with given request""" |
|
231 manager = query_utility(ISecurityManager) |
|
232 if manager is not None: |
|
233 principal_id = request.authenticated_userid |
|
234 if principal_id: |
|
235 return manager.get_principal(principal_id) |
|
236 else: |
|
237 return UnknownPrincipal |