1 """SAML 2.0 bindings module implements SOAP binding for attribute query
2
3 NERC DataGrid Project
4 """
5 __author__ = "P J Kershaw"
6 __date__ = "02/09/09"
7 __copyright__ = "(C) 2009 Science and Technology Facilities Council"
8 __license__ = "http://www.apache.org/licenses/LICENSE-2.0"
9 __contact__ = "Philip.Kershaw@stfc.ac.uk"
10 __revision__ = '$Id: __init__.py 8049 2012-03-28 15:57:38Z pjkersha $'
11 import logging
12 log = logging.getLogger(__name__)
13
14 from os import path
15 from ConfigParser import ConfigParser, SafeConfigParser
16
17 from ndg.saml.common import SAMLObject
18
19 from ndg.saml.utils.factory import importModuleObject
20 from ndg.soap import SOAPEnvelopeBase
21 from ndg.soap.etree import SOAPEnvelope
22 from ndg.soap.client import (UrlLib2SOAPClient, UrlLib2SOAPRequest)
23
24 from ndg.saml.saml2.binding.soap import SOAPBindingInvalidResponse
25
26
27 _isIterable = lambda obj: getattr(obj, '__iter__', False)
31 '''Client SAML SOAP Binding'''
32 SOAP_ACTION = 'http://www.oasis-open.org/committees/security'
33
34 REQUEST_ENVELOPE_CLASS_OPTNAME = 'requestEnvelopeClass'
35 RESPONSE_ENVELOPE_CLASS_OPTNAME = 'responseEnvelopeClass'
36 SERIALISE_OPTNAME = 'serialise'
37 DESERIALISE_OPTNAME = 'deserialise'
38
39 CONFIG_FILE_OPTNAMES = (
40 REQUEST_ENVELOPE_CLASS_OPTNAME,
41 RESPONSE_ENVELOPE_CLASS_OPTNAME,
42 SERIALISE_OPTNAME,
43 DESERIALISE_OPTNAME
44 )
45
46 __PRIVATE_ATTR_PREFIX = "__"
47 __slots__ = tuple([__PRIVATE_ATTR_PREFIX + i
48 for i in CONFIG_FILE_OPTNAMES + ("client",)])
49 del i
50
51 isIterable = staticmethod(_isIterable)
52
84
87
97
98 serialise = property(_getSerialise, _setSerialise,
99 doc="callable to serialise request into XML type")
100
103
113
114
115 deserialise = property(_getDeserialise,
116 _setDeserialise,
117 doc="callable to de-serialise response from XML "
118 "type")
119
122
135
136 requestEnvelopeClass = property(_getRequestEnvelopeClass,
137 _setRequestEnvelopeClass,
138 doc="SOAP Envelope Request Class")
139
142
144 if not isinstance(value, UrlLib2SOAPClient):
145 raise TypeError('Expecting %r for "client"; got %r' %
146 (UrlLib2SOAPClient, type(value)))
147 self.__client = value
148
149 client = property(_getClient, _setClient,
150 doc="SOAP Client object")
151
152 - def send(self, samlObj, uri=None, request=None):
153 '''Make an request/query to a remote SAML service
154
155 @type samlObj: saml.common.SAMLObject
156 @param samlObj: SAML query/request object
157 @type uri: basestring
158 @param uri: uri of service. May be omitted if set from request.url
159 @type request: ndg.security.common.soap.UrlLib2SOAPRequest
160 @param request: SOAP request object to which query will be attached
161 defaults to ndg.security.common.soap.client.UrlLib2SOAPRequest
162 '''
163 if self.serialise is None:
164 raise AttributeError('No "serialise" method set to serialise the '
165 'request')
166
167 if self.deserialise is None:
168 raise AttributeError('No "deserialise" method set to deserialise '
169 'the response')
170
171 if not isinstance(samlObj, SAMLObject):
172 raise TypeError('Expecting %r for input attribute query; got %r'
173 % (SAMLObject, type(samlObj)))
174
175 if request is None:
176 request = UrlLib2SOAPRequest()
177 request.envelope = self.requestEnvelopeClass()
178 request.envelope.create()
179
180 if uri is not None:
181 request.url = uri
182
183 samlElem = self.serialise(samlObj)
184
185
186 request.envelope.body.elem.append(samlElem)
187
188 response = self.client.send(request)
189
190 if len(response.envelope.body.elem) != 1:
191 raise SOAPBindingInvalidResponse("Expecting single child element "
192 "is SOAP body")
193
194 response = self.deserialise(response.envelope.body.elem[0])
195
196 return response
197
198 @classmethod
200 '''Alternative constructor makes object from config file settings
201 @type cfg: basestring / ConfigParser derived type
202 @param cfg: configuration file path or ConfigParser type object
203 @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding or derived type
204 @return: new instance of this class
205 '''
206 obj = cls()
207 obj.parseConfig(cfg, **kw)
208
209 return obj
210
211 - def parseConfig(self, cfg, prefix='', section='DEFAULT'):
212 '''Read config file settings
213 @type cfg: basestring /ConfigParser derived type
214 @param cfg: configuration file path or ConfigParser type object
215 @type prefix: basestring
216 @param prefix: prefix for option names e.g. "attributeQuery."
217 @type section: baestring
218 @param section: configuration file section from which to extract
219 parameters.
220 '''
221 if isinstance(cfg, basestring):
222 cfgFilePath = path.expandvars(cfg)
223 hereDir = path.dirname(cfgFilePath)
224 _cfg = SafeConfigParser(defaults=dict(here=hereDir))
225 _cfg.optionxform = str
226
227 _cfg.read(cfgFilePath)
228
229 elif isinstance(cfg, ConfigParser):
230 _cfg = cfg
231 else:
232 raise AttributeError('Expecting basestring or ConfigParser type '
233 'for "cfg" attribute; got %r type' % type(cfg))
234
235
236
237 kw = dict(_cfg.items(section))
238 if 'prefix' not in kw and prefix:
239 kw['prefix'] = prefix
240
241 self.parseKeywords(**kw)
242
244 """Update object from input keywords
245
246 @type prefix: basestring
247 @param prefix: if a prefix is given, only update self from kw items
248 where keyword starts with this prefix
249 @type kw: dict
250 @param kw: items corresponding to class instance variables to
251 update. Keyword names must match their equivalent class instance
252 variable names. However, they may prefixed with <prefix>
253 """
254 prefixLen = len(prefix)
255 for optName, val in kw.items():
256 if prefix:
257
258 if optName.startswith(prefix):
259 setattr(self, optName[prefixLen:], val)
260 else:
261
262 setattr(self, optName, val)
263
264 @classmethod
266 """Create a new instance initialising instance variables from the
267 keyword inputs
268 @type prefix: basestring
269 @param prefix: if a prefix is given, only update self from kw items
270 where keyword starts with this prefix
271 @type kw: dict
272 @param kw: items corresponding to class instance variables to
273 update. Keyword names must match their equivalent class instance
274 variable names. However, they may prefixed with <prefix>
275 @return: new instance of this class
276 @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding or derived type
277 """
278 obj = cls()
279 obj.fromKeywords(prefix=prefix, **kw)
280
281 return obj
282
301
303 '''Explicit implementation needed with __slots__'''
304 _dict = {}
305 for attrName in SOAPBinding.__slots__:
306
307
308 if attrName.startswith('__'):
309 attrName = "_SOAPBinding" + attrName
310
311 _dict[attrName] = getattr(self, attrName)
312
313 return _dict
314
316 '''Explicit implementation needed with __slots__'''
317 for attr, val in attrDict.items():
318 setattr(self, attr, val)
319