1 | """NDG Security |
---|
2 | |
---|
3 | Client interface to Session Manager for WSGI based applications |
---|
4 | |
---|
5 | NERC DataGrid Project |
---|
6 | """ |
---|
7 | __author__ = "P J Kershaw" |
---|
8 | __date__ = "27/11/08" |
---|
9 | __copyright__ = "(C) 2009 Science and Technology Facilities Council" |
---|
10 | __license__ = "BSD - see LICENSE file in top-level directory" |
---|
11 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
12 | __revision__ = "$Id$" |
---|
13 | import logging |
---|
14 | log = logging.getLogger(__name__) |
---|
15 | |
---|
16 | import sys |
---|
17 | import os |
---|
18 | |
---|
19 | from ndg.security.server.wsgi.utils.clientbase import WSGIClientBase |
---|
20 | from ndg.security.server.wsgi.utils.attributeauthorityclient import \ |
---|
21 | WSGIAttributeAuthorityClient |
---|
22 | |
---|
23 | # Session Manager Authentication interface ... |
---|
24 | from ndg.security.server.sessionmanager import AuthNServiceError, \ |
---|
25 | AuthNServiceInvalidCredentials, AuthNServiceRetrieveError, \ |
---|
26 | AuthNServiceInitError, AuthNServiceConfigError |
---|
27 | |
---|
28 | # Session Manager SOAP client interface |
---|
29 | from ndg.security.common.sessionmanager import SessionManagerClient |
---|
30 | |
---|
31 | # Import exception types from Session Manager and Session Manager client to |
---|
32 | # give caller some capability to trap errors |
---|
33 | # Session Manager server side exceptions ... |
---|
34 | from ndg.security.server.sessionmanager import SessionManagerError, \ |
---|
35 | UserSessionExpired, UserSessionNotBeforeTimeError, \ |
---|
36 | InvalidUserSession, CredentialWalletAttributeRequestDenied |
---|
37 | |
---|
38 | from ndg.security.server.sessionmanager import SessionNotFound as \ |
---|
39 | _SrvSessionNotFound |
---|
40 | |
---|
41 | # ... and client side exceptions ... |
---|
42 | from ndg.security.common.sessionmanager import SessionNotFound as \ |
---|
43 | _ClntSessionNotFound |
---|
44 | |
---|
45 | from ndg.security.common.sessionmanager import SessionExpired as \ |
---|
46 | _ClntSessionExpired |
---|
47 | |
---|
48 | from ndg.security.common.sessionmanager import InvalidSession as \ |
---|
49 | _ClntInvalidSession |
---|
50 | |
---|
51 | from ndg.security.common.sessionmanager import AttributeRequestDenied as \ |
---|
52 | _ClntAttributeRequestDenied |
---|
53 | |
---|
54 | from ndg.security.common.sessionmanager import InvalidSessionManagerClientCtx,\ |
---|
55 | SessionManagerClientError, SessionCertTimeError |
---|
56 | |
---|
57 | SessionNotFound = (_SrvSessionNotFound, _ClntSessionNotFound) |
---|
58 | |
---|
59 | # Combine client and server session not before time error exceptions to |
---|
60 | # enable easier exception handling for a WSGISessionManagerClient caller. |
---|
61 | # See SessionNotFound.__doc__ for more details of reasoning |
---|
62 | SessionNotBeforeTimeError = (UserSessionNotBeforeTimeError, |
---|
63 | SessionCertTimeError) |
---|
64 | |
---|
65 | # Combine client and server session expired exceptions to enable easier |
---|
66 | # exception handling for a WSGISessionManagerClient caller. See |
---|
67 | # SessionNotFound.__doc__ for more details of reasoning |
---|
68 | SessionExpired = (UserSessionExpired, _ClntSessionExpired) |
---|
69 | |
---|
70 | # Combine client and server invalid session exceptions to enable easier |
---|
71 | # exception handling for a WSGISessionManagerClient caller. See |
---|
72 | # SessionNotFound.__doc__ for more details of reasoning""" |
---|
73 | InvalidSession = (InvalidUserSession, _ClntInvalidSession) |
---|
74 | |
---|
75 | # Combine client and server invalid session exceptions to enable easier |
---|
76 | # exception handling for a WSGISessionManagerClient caller. See |
---|
77 | # SessionNotFound.__doc__ for more details of reasoning |
---|
78 | AttributeRequestDenied = (CredentialWalletAttributeRequestDenied, |
---|
79 | _ClntAttributeRequestDenied) |
---|
80 | |
---|
81 | # End of server/client side exception combinations |
---|
82 | |
---|
83 | |
---|
84 | class WSGISessionManagerClientError(Exception): |
---|
85 | """Base class exception for WSGI Session Manager client errors""" |
---|
86 | |
---|
87 | class WSGISessionManagerClientConfigError(WSGISessionManagerClientError): |
---|
88 | """Configuration error for WSGI Session Manager Client""" |
---|
89 | |
---|
90 | class WSGISessionManagerClient(WSGIClientBase): |
---|
91 | """Client interface to Session Manager for WSGI based applications |
---|
92 | |
---|
93 | This class wraps the SOAP based web service client and alternate access to |
---|
94 | a Session Manager instance in the same code stack available via an environ |
---|
95 | keyword |
---|
96 | |
---|
97 | @type defaultEnvironKeyName: basestring |
---|
98 | @cvar defaultEnvironKeyName: default WSGI environ keyword name for |
---|
99 | reference to a local Session Manager instance. Override with the |
---|
100 | environKeyName keyword to __init__ |
---|
101 | |
---|
102 | @type attributeAuthorityEnvironKeyName: basestring |
---|
103 | @cvar attributeAuthorityEnvironKeyName: default WSGI environ keyword name |
---|
104 | for reference to a local Attribute Authority instance used in calls to |
---|
105 | getAttCert(). Override with the attributeAuthorityEnvironKeyName keyword |
---|
106 | to __init__ |
---|
107 | """ |
---|
108 | defaultEnvironKeyName = "ndg.security.server.wsgi.sessionManagerFilter" |
---|
109 | attributeAuthorityEnvironKeyName = \ |
---|
110 | WSGIAttributeAuthorityClient.defaultEnvironKeyName |
---|
111 | |
---|
112 | _getLocalClient = lambda self:self._environ[ |
---|
113 | self.environKeyName].serviceSOAPBinding.sm |
---|
114 | |
---|
115 | localClient = property(fget=_getLocalClient, |
---|
116 | doc="local session manager instance") |
---|
117 | |
---|
118 | |
---|
119 | def __init__(self, |
---|
120 | environKeyName=None, |
---|
121 | attributeAuthorityEnvironKeyName=None, |
---|
122 | environ={}, |
---|
123 | **clientKw): |
---|
124 | """Initialise an interface to a Session Manager accessible either via a |
---|
125 | keyword to a WSGI environ dictionary or via a web service call |
---|
126 | |
---|
127 | @type environKeyName: basestring or None |
---|
128 | @param environKeyName: dict key reference to service object to be |
---|
129 | invoked. This may be set later using the environKeyName property |
---|
130 | or may be omitted altogether if the service is to be invoked via a |
---|
131 | web service call |
---|
132 | @type environ: dict |
---|
133 | @param environ: WSGI environment dictionary containing a reference to |
---|
134 | the service object. This may not be known at instantiation of this |
---|
135 | class. environ is not required if the service is to be invoked over |
---|
136 | a web service interface |
---|
137 | @type clientKw: dict |
---|
138 | @param clientKw: custom keywords to instantiate a web service client |
---|
139 | interface. Derived classes are responsible for instantiating this |
---|
140 | from an extended version of this __init__ method. |
---|
141 | """ |
---|
142 | |
---|
143 | log.debug("WSGISessionManagerClient.__init__ ...") |
---|
144 | |
---|
145 | self.environKeyName = environKeyName or \ |
---|
146 | WSGISessionManagerClient.defaultEnvironKeyName |
---|
147 | |
---|
148 | self._attributeAuthorityEnvironKeyName = \ |
---|
149 | attributeAuthorityEnvironKeyName or \ |
---|
150 | WSGISessionManagerClient.attributeAuthorityEnvironKeyName |
---|
151 | |
---|
152 | # Standard WSGI environment dict |
---|
153 | self._environ = environ |
---|
154 | |
---|
155 | if clientKw.get('uri'): |
---|
156 | self.wsClient = SessionManagerClient(**clientKw) |
---|
157 | else: |
---|
158 | self.wsClient = None |
---|
159 | |
---|
160 | |
---|
161 | def connect(self, username, **kw): |
---|
162 | """Request a new user session from the Session Manager |
---|
163 | |
---|
164 | @type username: string |
---|
165 | @param username: the username of the user to connect |
---|
166 | """ |
---|
167 | |
---|
168 | if self.localClientInEnviron: |
---|
169 | log.debug("Connecting to local Session Manager instance") |
---|
170 | if 'username' in kw: |
---|
171 | raise TypeError("connect() got an unexpected keyword argument " |
---|
172 | "'username'") |
---|
173 | |
---|
174 | # Connect to local instance |
---|
175 | res = self.localClient.connect(username=username, **kw) |
---|
176 | |
---|
177 | elif self.wsClient is None: |
---|
178 | raise WSGISessionManagerClientConfigError("No reference to a " |
---|
179 | "local Session Manager is set and no SOAP client " |
---|
180 | "to a remote service has been initialized") |
---|
181 | else: |
---|
182 | log.debug("Connecting to remote Session Manager service") |
---|
183 | |
---|
184 | # Filter out keywords which apply to a Session Manager local |
---|
185 | # instance call |
---|
186 | kw.pop('userX509Cert', None) |
---|
187 | |
---|
188 | # Make connection to remote service |
---|
189 | res = self.wsClient.connect(username, **kw) |
---|
190 | |
---|
191 | # Convert from unicode because unicode causes problems with |
---|
192 | # M2Crypto private key load |
---|
193 | res = tuple([isinstance(i,unicode) and str(i) or i for i in res]) |
---|
194 | |
---|
195 | return res |
---|
196 | |
---|
197 | |
---|
198 | def disconnect(self, **kw): |
---|
199 | """Delete an existing user session from the Session Manager |
---|
200 | |
---|
201 | @type **kw: dict |
---|
202 | @param **kw: disconnect keywords applicable to |
---|
203 | ndg.security.server.sessionmanager.SessionManager.getSessionStatus and |
---|
204 | ndg.security.common.sessionmanager.SessionManagerClient.getSessionStatus |
---|
205 | the SOAP client""" |
---|
206 | |
---|
207 | # Modify keywords according to correct interface for server side / |
---|
208 | # SOAP client |
---|
209 | if self.localClientInEnviron: |
---|
210 | if 'userDN' in kw: |
---|
211 | log.warning('Removing keyword "userDN": this is not supported ' |
---|
212 | 'for calls to ndg.security.server.sessionmanager.' |
---|
213 | 'SessionManager.deleteUserSession') |
---|
214 | kw.pop('userX509Cert', None) |
---|
215 | |
---|
216 | self.localClient.deleteUserSession(**kw) |
---|
217 | |
---|
218 | elif self.wsClient is None: |
---|
219 | raise WSGISessionManagerClientConfigError("No reference to a " |
---|
220 | "local Session Manager is set and no SOAP client " |
---|
221 | "to a remote service has been initialized") |
---|
222 | else: |
---|
223 | if 'userX509Cert' in kw: |
---|
224 | kw['userDN'] = kw.pop('userX509Cert').dn |
---|
225 | |
---|
226 | self.wsClient.disconnect(**kw) |
---|
227 | |
---|
228 | |
---|
229 | def getSessionStatus(self, **kw): |
---|
230 | """Check for the existence of a session with a given |
---|
231 | session ID / user certificate Distinguished Name |
---|
232 | |
---|
233 | @type **kw: dict |
---|
234 | @param **kw: disconnect keywords applicable to |
---|
235 | ndg.security.server.sessionmanager.SessionManager.getSessionStatus and |
---|
236 | ndg.security.common.sessionmanager.SessionManagerClient.getSessionStatus |
---|
237 | the SOAP client""" |
---|
238 | |
---|
239 | if self.localClientInEnviron: |
---|
240 | return self.localClient.getSessionStatus(**kw) |
---|
241 | |
---|
242 | elif self.wsClient is None: |
---|
243 | raise WSGISessionManagerClientConfigError("No reference to a " |
---|
244 | "local Session Manager is set and no SOAP client " |
---|
245 | "to a remote service has been initialized") |
---|
246 | else: |
---|
247 | return self.wsClient.getSessionStatus(**kw) |
---|
248 | |
---|
249 | |
---|
250 | |
---|
251 | def getAttCert(self, **kw): |
---|
252 | """Request NDG Session Manager to retrieve an Attribute |
---|
253 | Certificate from the given Attribute Authority and cache it in the |
---|
254 | user's credential wallet held by the session manager. |
---|
255 | |
---|
256 | @type **kw: dict |
---|
257 | @param **kw: disconnect keywords applicable to |
---|
258 | ndg.security.server.sessionmanager.SessionManager.getAttCert and |
---|
259 | ndg.security.common.sessionmanager.SessionManagerClient.getAttCert |
---|
260 | the SOAP client |
---|
261 | """ |
---|
262 | |
---|
263 | if self.localClientInEnviron: |
---|
264 | # Connect to local instance of Session Manager - next check for |
---|
265 | # an Attribute Authority URI or instance running locally |
---|
266 | if kw.get('attributeAuthorityURI') is None and \ |
---|
267 | kw.get('attributeAuthority') is None: |
---|
268 | wsgiAttributeAuthorityClient = WSGIAttributeAuthorityClient( |
---|
269 | environ=self._environ, |
---|
270 | environKeyName=self._attributeAuthorityEnvironKeyName) |
---|
271 | |
---|
272 | if wsgiAttributeAuthorityClient.localClientInEnviron: |
---|
273 | kw['attributeAuthority'] = wsgiAttributeAuthorityClient.ref |
---|
274 | else: |
---|
275 | raise WSGISessionManagerClientConfigError( |
---|
276 | "No Attribute Authority URI or server object has been " |
---|
277 | "set and no reference is available in environ") |
---|
278 | |
---|
279 | return self.localClient.getAttCert(**kw) |
---|
280 | |
---|
281 | elif self.wsClient is None: |
---|
282 | raise WSGISessionManagerClientConfigError("No reference to a " |
---|
283 | "local Session Manager is set and no SOAP client " |
---|
284 | "to a remote service has been initialized") |
---|
285 | else: |
---|
286 | # Filter out keywords which apply to a Session Manager local |
---|
287 | # instance call |
---|
288 | if 'username' in kw: |
---|
289 | kw.pop('username') |
---|
290 | log.warning('Trying call via SOAP interface: ' |
---|
291 | 'removing the "username" keyword ' |
---|
292 | 'ndg.security.common.sessionmanager.' |
---|
293 | 'SessionManagerClient.getAttCert doesn\'t support ' |
---|
294 | 'this keyword') |
---|
295 | |
---|
296 | if 'refreshAttCert' in kw: |
---|
297 | kw.pop('refreshAttCert') |
---|
298 | log.warning('Trying call via SOAP interface: ' |
---|
299 | 'removing the "refreshAttCert" keyword ' |
---|
300 | 'ndg.security.common.sessionmanager.' |
---|
301 | 'SessionManagerClient.getAttCert doesn\'t support ' |
---|
302 | 'this keyword') |
---|
303 | |
---|
304 | if 'attCertRefreshElapse' in kw: |
---|
305 | kw.pop('attCertRefreshElapse') |
---|
306 | log.warning('Trying call via SOAP interface: ' |
---|
307 | 'removing the "attCertRefreshElapse" keyword ' |
---|
308 | 'ndg.security.common.sessionmanager.' |
---|
309 | 'SessionManagerClient.getAttCert doesn\'t support ' |
---|
310 | 'this keyword') |
---|
311 | |
---|
312 | return self.wsClient.getAttCert(**kw) |
---|