source: TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/attributequery.py @ 6578

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg-security/TI12-security/trunk/NDGSecurity/python/ndg_security_common/ndg/security/common/saml_utils/binding/soap/attributequery.py@6578
Revision 6578, 6.5 KB checked in by pjkersha, 11 years ago (diff)
  • Important fix for SOAP client used with SAML SOAP binding: set text/xml content type.
  • Refactored SAML SOAP binding query clients.
Line 
1"""SAML 2.0 bindings module implements SOAP binding for attribute query
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "02/09/09"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__license__ = "BSD - see LICENSE file in top-level directory"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = '$Id: $'
11import re
12import logging
13log = logging.getLogger(__name__)
14
15from M2Crypto.m2urllib2 import HTTPSHandler
16
17from saml.saml2.core import Attribute, AttributeQuery
18
19from ndg.security.common.utils import TypedList
20from ndg.security.common.saml_utils.binding.soap.subjectquery import (
21                                                    SubjectQuerySOAPBinding,
22                                                    SubjectQueryResponseError)
23
24# Prevent whole module breaking if this is not available - it's only needed for
25# AttributeQuerySslSOAPBinding
26try:
27    from ndg.security.common.utils.m2crypto import SSLContextProxy
28    _sslContextProxySupport = True
29   
30except ImportError:
31    _sslContextProxySupport = False
32
33
34class AttributeQueryResponseError(SubjectQueryResponseError):
35    """SAML Response error from Attribute Query"""
36   
37
38class AttributeQuerySOAPBinding(SubjectQuerySOAPBinding): 
39    """SAML Attribute Query SOAP Binding
40    """
41    QUERY_ATTRIBUTES_ATTRNAME = 'queryAttributes'
42    LEN_QUERY_ATTRIBUTES_ATTRNAME = len(QUERY_ATTRIBUTES_ATTRNAME)
43    QUERY_ATTRIBUTES_PAT = re.compile(',\s*')
44   
45    __PRIVATE_ATTR_PREFIX = "__"
46    __slots__ = (__PRIVATE_ATTR_PREFIX + QUERY_ATTRIBUTES_ATTRNAME,)
47
48    SERIALISE_KW = 'serialise'
49    DESERIALISE_KW = 'deserialise'
50    QUERY_TYPE = AttributeQuery
51   
52    def __init__(self, **kw):
53        '''Create SOAP Client for SAML Attribute Query'''
54       
55        # Default to ElementTree based serialisation/deserialisation
56        if AttributeQuerySOAPBinding.SERIALISE_KW not in kw:
57            from saml.xml.etree import AttributeQueryElementTree
58            kw[AttributeQuerySOAPBinding.SERIALISE_KW
59               ] = AttributeQueryElementTree.toXML
60               
61        if AttributeQuerySOAPBinding.DESERIALISE_KW not in kw:
62            from saml.xml.etree import ResponseElementTree
63            kw[AttributeQuerySOAPBinding.DESERIALISE_KW
64               ] = ResponseElementTree.fromXML
65       
66        super(AttributeQuerySOAPBinding, self).__init__(**kw)
67        self.__queryAttributes = TypedList(Attribute)
68           
69    def __setattr__(self, name, value):
70        """Enable setting of SAML query attribute objects via a comma separated
71        string suitable for use reading from an ini file. 
72        """
73        try:
74            super(AttributeQuerySOAPBinding, self).__setattr__(name, value)
75           
76        except AttributeError:
77            if name.startswith(
78                        AttributeQuerySOAPBinding.QUERY_ATTRIBUTES_ATTRNAME):
79                # Special handler for parsing string format settings
80                if not isinstance(value, basestring):
81                    raise TypeError('Expecting string format for special '
82                                    '%r attribute; got %r instead' %
83                                    (name, type(value)))
84                   
85                pat = AttributeQuerySOAPBinding.QUERY_ATTRIBUTES_PAT
86                attribute = Attribute()
87               
88                (attribute.name, 
89                 attribute.friendlyName, 
90                 attribute.nameFormat) = pat.split(value)
91                 
92                self.queryAttributes.append(attribute)
93            else:
94                raise
95             
96    def _getQueryAttributes(self):
97        return self.__queryAttributes
98
99    def _setQueryAttributes(self, value):
100        if not isinstance(value, TypedList) and value.elementType != Attribute:
101            raise TypeError('Expecting TypedList(Attribute) type for '
102                            '"queryAttributes"; got %r instead' % type(value)) 
103       
104        self.__queryAttributes = value
105   
106    queryAttributes = property(_getQueryAttributes, 
107                               _setQueryAttributes, 
108                               doc="List of attributes to query from the "
109                                   "Attribute Authority")
110#
111#    def _createQuery(self):
112#        """ Create a SAML attribute query"""
113#        attributeQuery = super(AttributeQuerySOAPBinding, self)._createQuery(
114#                                                                AttributeQuery)
115#        # Add list of attributes to query                     
116#        for attribute in self.queryAttributes:
117#            attributeQuery.attributes.append(attribute)
118#           
119#        return attributeQuery
120
121   
122class AttributeQuerySslSOAPBinding(AttributeQuerySOAPBinding):
123    """Specialisation of AttributeQuerySOAPbinding taking in the setting of
124    SSL parameters for mutual authentication
125    """
126    SSL_CONTEXT_PROXY_SUPPORT = _sslContextProxySupport
127    __slots__ = ('__sslCtxProxy',)
128   
129    def __init__(self, **kw):
130        if not AttributeQuerySslSOAPBinding.SSL_CONTEXT_PROXY_SUPPORT:
131            raise ImportError("ndg.security.common.utils.m2crypto import "
132                              "failed - missing M2Crypto package?")
133       
134        # Miss out default HTTPSHandler and set in send() instead
135        if 'handlers' in kw:
136            raise TypeError("__init__() got an unexpected keyword argument "
137                            "'handlers'")
138           
139        super(AttributeQuerySslSOAPBinding, self).__init__(handlers=(), **kw)
140        self.__sslCtxProxy = SSLContextProxy()
141
142    def send(self, **kw):
143        """Override base class implementation to pass explicit SSL Context
144        """
145        httpsHandler = HTTPSHandler(ssl_context=self.sslCtxProxy.createCtx())
146        self.client.openerDirector.add_handler(httpsHandler)
147        return super(AttributeQuerySslSOAPBinding, self).send(**kw)
148       
149    @property
150    def sslCtxProxy(self):
151        """SSL Context Proxy object used for setting up an SSL Context for
152        queries
153        """
154        return self.__sslCtxProxy
155           
156    def __setattr__(self, name, value):
157        """Enable setting of SSLContextProxy attributes as if they were
158        attributes of this class.  This is intended as a convenience for
159        making settings parameters read from a config file
160        """
161        try:
162            super(AttributeQuerySslSOAPBinding, self).__setattr__(name, value)
163           
164        except AttributeError, e:
165            # Coerce into setting SSL Context Proxy attributes
166            try:
167                setattr(self.sslCtxProxy, name, value)
168            except:
169                raise e
Note: See TracBrowser for help on using the repository browser.