1 | #!/usr/bin/env python |
---|
2 | """NDG Security client - client interface classes to Session Manager |
---|
3 | |
---|
4 | Make requests for authentication and authorisation |
---|
5 | |
---|
6 | NERC Data Grid Project |
---|
7 | |
---|
8 | P J Kershaw 24/04/06 |
---|
9 | |
---|
10 | Copyright (C) 2006 CCLRC & NERC |
---|
11 | |
---|
12 | This software may be distributed under the terms of the Q Public License, |
---|
13 | version 1.0 or later. |
---|
14 | """ |
---|
15 | __all__ = ['SessionMgr_services', 'SessionMgr_services_types'] |
---|
16 | |
---|
17 | import sys |
---|
18 | import os |
---|
19 | |
---|
20 | # Handle retrieval of public key cert for Session Manager/Attribute Authority |
---|
21 | # at remote location |
---|
22 | import tempfile |
---|
23 | import urllib |
---|
24 | |
---|
25 | from ndg.security.common.SessionCookie import SessionCookie |
---|
26 | from ndg.security.common.wsSecurity import SignatureHandler |
---|
27 | from ndg.security.common.X509 import * |
---|
28 | from ndg.security.common.AttCert import AttCertParse |
---|
29 | from SessionMgr_services import SessionMgrServiceLocator |
---|
30 | |
---|
31 | |
---|
32 | #_____________________________________________________________________________ |
---|
33 | class SessionMgrClientError(Exception): |
---|
34 | """Exception handling for SessionMgrClient class""" |
---|
35 | |
---|
36 | #_____________________________________________________________________________ |
---|
37 | class AttributeRequestDenied(Exception): |
---|
38 | """Raise when a getAttCert call to the Attribute Authority is denied""" |
---|
39 | |
---|
40 | #_____________________________________________________________________________ |
---|
41 | class SessionMgrClient(object): |
---|
42 | """Client interface to Session Manager Web Service""" |
---|
43 | |
---|
44 | #_________________________________________________________________________ |
---|
45 | def __init__(self, uri=None, tracefile=None, **signatureHandlerKw): |
---|
46 | """ |
---|
47 | @type uri: string |
---|
48 | @keyword uri: URI for Session Manager WS. Setting it will set the |
---|
49 | Service Proxy |
---|
50 | |
---|
51 | @type tracefile: file stream type |
---|
52 | @param tracefile: set to file object such as sys.stderr to give extra |
---|
53 | WS debug information |
---|
54 | |
---|
55 | @type signatureHandlerKw: dict |
---|
56 | @param signatureHandlerKw: keywords to configure signature handler""" |
---|
57 | |
---|
58 | self.__srv = None |
---|
59 | self.__uri = None |
---|
60 | |
---|
61 | self.__srvCertTempFile = None |
---|
62 | |
---|
63 | |
---|
64 | if uri: |
---|
65 | self.__setURI(uri) |
---|
66 | |
---|
67 | # WS-Security Signature handler |
---|
68 | self.__signatureHandler = SignatureHandler(**signatureHandlerKw) |
---|
69 | |
---|
70 | self.__tracefile = tracefile |
---|
71 | |
---|
72 | |
---|
73 | # Instantiate Session Manager WS ZSI client |
---|
74 | if self.__uri: |
---|
75 | self.initService() |
---|
76 | |
---|
77 | |
---|
78 | #_________________________________________________________________________ |
---|
79 | def __setURI(self, uri): |
---|
80 | "Set URI property method" |
---|
81 | |
---|
82 | if not isinstance(uri, basestring): |
---|
83 | raise SessionMgrClientError, \ |
---|
84 | "Session Manager WSDL URI must be a valid string" |
---|
85 | |
---|
86 | self.__uri = uri |
---|
87 | |
---|
88 | uri = property(fset=__setURI, doc="Set Session Manager URI") |
---|
89 | |
---|
90 | |
---|
91 | #_________________________________________________________________________ |
---|
92 | def __getSignatureHandler(self): |
---|
93 | "Get SignatureHandler object property method" |
---|
94 | return self.__signatureHandler |
---|
95 | |
---|
96 | signatureHandler = property(fget=__getSignatureHandler, |
---|
97 | doc="SignatureHandler object") |
---|
98 | |
---|
99 | |
---|
100 | #_________________________________________________________________________ |
---|
101 | def __getSrvX509Cert(self): |
---|
102 | """Retrieve the X.509 certificate from file or if not available, from |
---|
103 | the Session Manager service""" |
---|
104 | |
---|
105 | # Don't proceed unless URI was set - user may have set public key via |
---|
106 | # srvCertFilePath instead |
---|
107 | if self.__srvCertFilePath is not None: |
---|
108 | return |
---|
109 | |
---|
110 | try: |
---|
111 | self.__srvCertTempFile = tempfile.NamedTemporaryFile() |
---|
112 | |
---|
113 | cert = self.getX509Cert() |
---|
114 | open(self.__srvCertTempFile.name, "w").write(cert) |
---|
115 | |
---|
116 | self.__srvCertFilePath = self.__srvCertTempFile.name |
---|
117 | |
---|
118 | except IOError, (errNo, errMsg): |
---|
119 | raise SessionMgrClientError, \ |
---|
120 | "Writing X.509 certificate to temp \"%s\": %s" % \ |
---|
121 | (self.__srvCertTempFile.name, errMsg) |
---|
122 | except Exception, e: |
---|
123 | raise SessionMgrClientError, "Retrieving Session Manager " + \ |
---|
124 | "X.509 certificate: %s" % str(e) |
---|
125 | |
---|
126 | |
---|
127 | #_________________________________________________________________________ |
---|
128 | def initService(self, uri=None): |
---|
129 | """Set the WS client for the Session Manager""" |
---|
130 | if uri: |
---|
131 | self.__setURI(uri) |
---|
132 | |
---|
133 | # WS-Security Signature handler object is passed to binding |
---|
134 | try: |
---|
135 | locator = SessionMgrServiceLocator() |
---|
136 | self.__srv = locator.getSessionMgr(self.__uri, |
---|
137 | sig_handler=self.__signatureHandler, |
---|
138 | tracefile=self.__tracefile) |
---|
139 | except HTTPResponse, e: |
---|
140 | raise SessionMgrClientError, \ |
---|
141 | "Initialising Service for \"%s\": %s %s" % \ |
---|
142 | (self.__uri, e.status, e.reason) |
---|
143 | |
---|
144 | except Exception, e: |
---|
145 | raise SessionMgrClientError, \ |
---|
146 | "Initialising Service for \"%s\": %s" % (self.__uri, str(e)) |
---|
147 | |
---|
148 | |
---|
149 | #_________________________________________________________________________ |
---|
150 | def addUser(self, |
---|
151 | username, |
---|
152 | passphrase=None, |
---|
153 | passphraseFilePath=None, |
---|
154 | clntPriKeyPwd=None): |
---|
155 | """Register a new user |
---|
156 | |
---|
157 | username: the username for the new user |
---|
158 | passphrase: user's pass-phrase |
---|
159 | passphraseFilePath: a file containing the user's pass-phrase. |
---|
160 | Use this as an alternative to passphrase keyword |
---|
161 | clntPriKeyPwd: pass-phrase if any for the client's private |
---|
162 | key used to decrypt response from |
---|
163 | Session Manager |
---|
164 | """ |
---|
165 | |
---|
166 | if passphrase is None: |
---|
167 | try: |
---|
168 | passphrase = open(passphraseFilePath).read().strip() |
---|
169 | |
---|
170 | except Exception, e: |
---|
171 | raise SessionMgrClientError, "Pass-phrase not defined: " + \ |
---|
172 | str(e) |
---|
173 | |
---|
174 | |
---|
175 | # Make request for new user |
---|
176 | try: |
---|
177 | self.__srv.addUser(username, passphrase) |
---|
178 | |
---|
179 | except Exception, e: |
---|
180 | raise SessionMgrClientError, "Adding new user: " + str(e) |
---|
181 | |
---|
182 | |
---|
183 | #_________________________________________________________________________ |
---|
184 | def connect(self, |
---|
185 | username, |
---|
186 | passphrase=None, |
---|
187 | passphraseFilePath=None, |
---|
188 | getCookie=False, |
---|
189 | createServerSess=True): |
---|
190 | """Request a new user session from the Session Manager |
---|
191 | |
---|
192 | @type username: string |
---|
193 | @param username: the username of the user to connect |
---|
194 | |
---|
195 | @type passphrase: string |
---|
196 | @keyword passphrase: user's pass-phrase |
---|
197 | |
---|
198 | @type passphraseFilePath: string |
---|
199 | @keyword passphraseFilePath: a file containing the user's pass-phrase. |
---|
200 | Use this as an alternative to passphrase keyword. |
---|
201 | |
---|
202 | @type getCookie: bool |
---|
203 | @keyword getCookie: If set to True, return a cookie to be set in |
---|
204 | a web browser client. |
---|
205 | |
---|
206 | @type createServerSess: bool |
---|
207 | @keyword createServerSess: If set to True, the SessionMgr will create |
---|
208 | and manage a session for the user but note, this flag is ignored and |
---|
209 | set to True if getCookie is set. For command line case, where |
---|
210 | getCookie is False, it's possible to choose to have a client or server |
---|
211 | side session using this keyword. |
---|
212 | |
---|
213 | @rtype: tuple |
---|
214 | @return proxy cert, proxy private key, user cert and cookie all as |
---|
215 | strings but cookie will be None if the getCookie keyword is False""" |
---|
216 | |
---|
217 | if passphrase is None: |
---|
218 | try: |
---|
219 | passphrase = open(passphraseFilePath).read().strip() |
---|
220 | |
---|
221 | except Exception, e: |
---|
222 | raise SessionMgrClientError, "Pass-phrase not defined: " + \ |
---|
223 | str(e) |
---|
224 | |
---|
225 | # Make connection |
---|
226 | try: |
---|
227 | userID = self.__srv.connect(username, |
---|
228 | passphrase, |
---|
229 | createServerSess, |
---|
230 | getCookie) |
---|
231 | return userID |
---|
232 | |
---|
233 | except Exception, e: |
---|
234 | raise SessionMgrClientError, \ |
---|
235 | "Connecting to Session Manager: " + str(e) |
---|
236 | |
---|
237 | |
---|
238 | #_________________________________________________________________________ |
---|
239 | def disconnect(self, |
---|
240 | userCert=None, |
---|
241 | sessCookie=None, |
---|
242 | sessID=None, |
---|
243 | encrSessionMgrURI=None): |
---|
244 | """Delete an existing user session from the Session Manager |
---|
245 | |
---|
246 | disconnect([sessCookie=s]|[sessID=i, encrSessionMgrURI=e]| |
---|
247 | [userCert=u][key=arg, ...]) |
---|
248 | |
---|
249 | @type userCert: |
---|
250 | @keyword userCert: proxy certificate - use as ID instead of a cookie |
---|
251 | in the case of a command line client. |
---|
252 | |
---|
253 | @type sessCookie: ndg.security.common.SessionCookie or string |
---|
254 | @keyword sessCookie: session cookie returned from call to connect() |
---|
255 | for a browser client. |
---|
256 | |
---|
257 | @type sessID: string |
---|
258 | @keyword sessID: session ID. Input this as well as encrSessionMgrURI |
---|
259 | as an alternative to sessCookie in the case of a browser client. |
---|
260 | |
---|
261 | @type encrSessionMgrURI: string |
---|
262 | @keyword encrSessionMgrURI: encrypted Session Manager URI.""" |
---|
263 | |
---|
264 | # Checking authentication details: either a proxy cert, |
---|
265 | # session cookie, or session ID/encrypted Session Manager URI |
---|
266 | # combination |
---|
267 | if sessCookie: |
---|
268 | if isinstance(sessCookie, basestring): |
---|
269 | try: |
---|
270 | sessCookie = SessionCookie(sessCookie) |
---|
271 | except Exception, e: |
---|
272 | raise SessionMgrClientError, \ |
---|
273 | "Error parsing session cookie: " + str(e) |
---|
274 | |
---|
275 | sessID = sessCookie.sessionID |
---|
276 | encrSessionMgrURI = sessCookie.encrSessionMgrURI |
---|
277 | |
---|
278 | elif not sessID and not encrSessionMgrURI: |
---|
279 | raise SessionMgrClientError, '"sessCookie or "sessID" and ' + \ |
---|
280 | '"encrSessionMgrURI" keywords must be set' |
---|
281 | |
---|
282 | # Make connection |
---|
283 | try: |
---|
284 | self.__srv.disconnect(userCert, sessID, encrSessionMgrURI) |
---|
285 | |
---|
286 | except Exception, e: |
---|
287 | raise SessionMgrClientError, \ |
---|
288 | "Disconnecting from Session Manager: " + str(e) |
---|
289 | |
---|
290 | |
---|
291 | #_________________________________________________________________________ |
---|
292 | def getAttCert(self, |
---|
293 | proxyCert=None, |
---|
294 | sessCookie=None, |
---|
295 | sessID=None, |
---|
296 | encrSessionMgrURI=None, |
---|
297 | attAuthorityURI=None, |
---|
298 | attAuthorityCert=None, |
---|
299 | reqRole=None, |
---|
300 | mapFromTrustedHosts=True, |
---|
301 | rtnExtAttCertList=False, |
---|
302 | extAttCertList=[], |
---|
303 | extTrustedHostList=[]): |
---|
304 | """Request NDG Session Manager Web Service to retrieve an Attribute |
---|
305 | Certificate from the given Attribute Authority and cache it in the |
---|
306 | user's credential wallet held by the session manager. |
---|
307 | |
---|
308 | getAttCert([sessCookie=s]|[sessID=i, encrSessionMgrURI=e]| |
---|
309 | [proxyCert=p][key=arg, ...]) |
---|
310 | |
---|
311 | proxyCert: proxy certificate - use as ID instead of |
---|
312 | a cookie in the case of a command line client. |
---|
313 | sessCookie: session cookie returned from call to connect() |
---|
314 | for a browser client. Input as a string or |
---|
315 | SimpleCookie type. |
---|
316 | sessID: session ID. Input this as well as |
---|
317 | encrSessionMgrURI as an alternative to |
---|
318 | sessCookie in the case of a browser client. |
---|
319 | encrSessionMgrURI: encrypted Session Manager URI. |
---|
320 | attAuthorityURI: URI for Attribute Authority WS. |
---|
321 | attAuthorityCert: The Session Manager uses the Public key of the |
---|
322 | Attribute Authority to encrypt requests to it. |
---|
323 | reqRole: The required role for access to a data set. |
---|
324 | This can be left out in which case the |
---|
325 | Attribute Authority just returns whatever |
---|
326 | Attribute Certificate it has for the user |
---|
327 | mapFromTrustedHosts: Allow a mapped Attribute Certificate to be |
---|
328 | created from a user certificate from another |
---|
329 | trusted host. |
---|
330 | rtnExtAttCertList: Set this flag True so that if authorisation is |
---|
331 | denied, a list of potential attribute |
---|
332 | certificates for mapping may be returned. |
---|
333 | extAttCertList: A list of Attribute Certificates from other |
---|
334 | trusted hosts from which the target Attribute |
---|
335 | Authority can make a mapped certificate |
---|
336 | extTrustedHostList: A list of trusted hosts that can be used to |
---|
337 | get Attribute Certificates for making a mapped |
---|
338 | AC. |
---|
339 | """ |
---|
340 | |
---|
341 | # Checking authentication details: either a proxy cert, |
---|
342 | # session cookie, or session ID/encrypted Session Manager URI |
---|
343 | # combination |
---|
344 | if sessCookie: |
---|
345 | if isinstance(sessCookie, basestring): |
---|
346 | try: |
---|
347 | sessCookie = SessionCookie(sessCookie) |
---|
348 | except Exception, e: |
---|
349 | raise SessionMgrClientError, \ |
---|
350 | "Error parsing session cookie: " + str(e) |
---|
351 | |
---|
352 | sessID = sessCookie.sessionID |
---|
353 | encrSessionMgrURI = sessCookie.encrSessionMgrURI |
---|
354 | |
---|
355 | elif not sessID and not encrSessionMgrURI and not proxyCert: |
---|
356 | raise SessionMgrClientError, \ |
---|
357 | '"proxyCert" or "sessCookie or "sessID" and ' + \ |
---|
358 | '"encrSessionMgrURI" keywords must be set' |
---|
359 | |
---|
360 | |
---|
361 | # Make request |
---|
362 | try: |
---|
363 | attCert, msg, extAttCertList = self.__srv.getAttCert(proxyCert, |
---|
364 | sessID, |
---|
365 | encrSessionMgrURI, |
---|
366 | attAuthorityURI, |
---|
367 | attAuthorityCert, |
---|
368 | reqRole, |
---|
369 | mapFromTrustedHosts, |
---|
370 | rtnExtAttCertList, |
---|
371 | extAttCertList, |
---|
372 | extTrustedHostList) |
---|
373 | except Exception, e: |
---|
374 | raise SessionMgrClientError, \ |
---|
375 | "Attribute Certificate request: " + str(e) |
---|
376 | if not attCert: |
---|
377 | raise AttributeRequestDenied, msg |
---|
378 | |
---|
379 | return attCert, extAttCertList |
---|
380 | |
---|
381 | |
---|
382 | #_________________________________________________________________________ |
---|
383 | def getX509Cert(self): |
---|
384 | """Retrieve the public key of the Session Manager""" |
---|
385 | |
---|
386 | try: |
---|
387 | resp = self.__srv.getX509Cert() |
---|
388 | return resp |
---|
389 | |
---|
390 | except Exception, e: |
---|
391 | raise SessionMgrClientError, "Retrieving X.509 certificate: " + \ |
---|
392 | str(e) |
---|
393 | |
---|