Changeset 5663


Ignore:
Timestamp:
24/08/09 16:41:18 (10 years ago)
Author:
pjkersha
Message:
  • Working unit tests for SAML Attribute Query WSGI interface with SOAP binding
  • integrated with Attribute Authority via AttributeAuthorityMiddleware?.
Location:
TI12-security/trunk/python
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/ndg_security_common/ndg/security/common/utils/etree.py

    r5596 r5663  
    5555        self.prefix = prefix 
    5656 
     57    def __eq__(self, qname): 
     58        """Enable equality check for QName 
     59        @type qname: ndg.security.common.utils.etree.QName 
     60        @param qname: Qualified Name to compare with self  
     61        """ 
     62        if not isinstance(qname, QName): 
     63            raise TypeError('Expecting %r; got %r' % (QName, type(qname))) 
     64                             
     65        return (self.prefix, self.namespaceURI, self.localPart) == \ 
     66               (qname.prefix, qname.namespaceURI, qname.localPart) 
     67 
     68    def __ne__(self, qname): 
     69        """Enable equality check for QName 
     70        @type qname: ndg.security.common.utils.etree.QName 
     71        @param qname: Qualified Name to compare with self  
     72        """ 
     73        return not self.__eq__(qname) 
     74                
    5775    def _getPrefix(self): 
    5876        return self.__prefix 
  • TI12-security/trunk/python/ndg_security_saml/saml/common/__init__.py

    r5648 r5663  
    8585             
    8686    def __ne__(self, version): 
    87         return self.__eq__(version) 
     87        return not self.__eq__(version) 
    8888             
    8989    def __gt__(self, version):                 
     
    130130        @rtype: tuple 
    131131        @return: SAML version tuple""" 
    132         return tuple(version.split(".")) 
     132        return tuple([int(i) for i in version.split(".")]) 
  • TI12-security/trunk/python/ndg_security_saml/saml/common/xml.py

    r5599 r5663  
    238238    # URI for SAML 2 SOAP binding. 
    239239    SAML2_SOAP11_BINDING_URI = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" 
     240   
     241     
     242class QName(object): 
     243    """XML Qualified Name"""  
     244 
     245    def __init__(self, namespaceURI, localPart, prefix): 
     246        self.namespaceURI = namespaceURI 
     247        self.localPart = localPart 
     248        self.prefix = prefix 
     249     
     250    def _getPrefix(self): 
     251        return self.__prefix 
     252 
     253    def _setPrefix(self, value): 
     254        if not isinstance(value, basestring): 
     255            raise TypeError('Expected string type for "prefix"; got %r' % 
     256                            type(value)) 
     257        self.__prefix = value 
     258     
     259    prefix = property(_getPrefix, _setPrefix, None, "Namespace Prefix") 
     260 
     261    def _getLocalPart(self): 
     262        return self.__localPart 
     263     
     264    def _setLocalPart(self, value): 
     265        if not isinstance(value, basestring): 
     266            raise TypeError('Expected string type for "localPart"; got %r' % 
     267                            type(value)) 
     268        self.__localPart = value 
     269         
     270    localPart = property(_getLocalPart, _setLocalPart, None, "LocalPart") 
     271 
     272    def _getNamespaceURI(self): 
     273        return self.__namespaceURI 
     274 
     275    def _setNamespaceURI(self, value): 
     276        if not isinstance(value, basestring): 
     277            raise TypeError('Expected string type for "namespaceURI"; got %r' % 
     278                            type(value)) 
     279        self.__namespaceURI = value 
     280   
     281    namespaceURI = property(_getNamespaceURI, _setNamespaceURI, None,  
     282                            "Namespace URI") 
     283 
     284    def __eq__(self, qname): 
     285        """Enable equality check for QName 
     286        @type qname: saml.common.xml.QName 
     287        @param qname: Qualified Name to compare with self  
     288        """ 
     289        if not isinstance(qname, QName): 
     290            raise TypeError('Expecting %r; got %r' % (QName, type(qname))) 
     291                             
     292        return (self.prefix, self.namespaceURI, self.localPart) == \ 
     293               (qname.prefix, qname.namespaceURI, qname.localPart) 
     294 
     295    def __ne__(self, qname): 
     296        """Enable equality check for QName 
     297        @type qname: saml.common.xml.QName 
     298        @param qname: Qualified Name to compare with self  
     299        """ 
     300        return not self.__eq__(qname) 
  • TI12-security/trunk/python/ndg_security_saml/saml/saml2/core.py

    r5620 r5663  
    3232 
    3333from saml.common import SAMLObject, SAMLVersion 
    34 from saml.common.xml import SAMLConstants 
    35 from saml.xml import QName 
     34from saml.common.xml import SAMLConstants, QName 
    3635from saml.utils import TypedList 
    3736 
  • TI12-security/trunk/python/ndg_security_saml/saml/xml/__init__.py

    r5620 r5663  
    9393    pass 
    9494 
    95 class XMLTypeParseError(Exception): 
     95class XMLTypeParseError(XMLTypeError): 
    9696    pass 
    97          
    98      
    99 class QName(object): 
    100     """XML Qualified Name"""  
    10197 
    102     def __init__(self, namespaceURI, localPart, prefix): 
    103         self.namespaceURI = namespaceURI 
    104         self.localPart = localPart 
    105         self.prefix = prefix 
    106      
    107     def _getPrefix(self): 
    108         return self.__prefix 
    109  
    110     def _setPrefix(self, value): 
    111         if not isinstance(value, basestring): 
    112             raise TypeError('Expected string type for "prefix"; got %r' % 
    113                             type(value)) 
    114         self.__prefix = value 
    115      
    116     prefix = property(_getPrefix, _setPrefix, None, "Namespace Prefix") 
    117  
    118     def _getLocalPart(self): 
    119         return self.__localPart 
    120      
    121     def _setLocalPart(self, value): 
    122         if not isinstance(value, basestring): 
    123             raise TypeError('Expected string type for "localPart"; got %r' % 
    124                             type(value)) 
    125         self.__localPart = value 
    126          
    127     localPart = property(_getLocalPart, _setLocalPart, None, "LocalPart") 
    128  
    129     def _getNamespaceURI(self): 
    130         return self.__namespaceURI 
    131  
    132     def _setNamespaceURI(self, value): 
    133         if not isinstance(value, basestring): 
    134             raise TypeError('Expected string type for "namespaceURI"; got %r' % 
    135                             type(value)) 
    136         self.__namespaceURI = value 
    137    
    138     namespaceURI = property(_getNamespaceURI, _setNamespaceURI, None,  
    139                             "Namespace URI") 
     98class UnknownAttrProfile(XMLTypeError): 
     99    """Raise from Attribute Value factory if attribute type is not recognised 
     100    """ 
  • TI12-security/trunk/python/ndg_security_saml/saml/xml/etree.py

    r5637 r5663  
    3939 
    4040from saml.saml2.core import SAMLObject, Attribute, AttributeStatement, \ 
    41     AuthnStatement, AuthzDecisionStatement, \ 
    42     Assertion, Conditions, AttributeValue, AttributeQuery, Subject, NameID, \ 
    43     Issuer, SAMLVersion, Response, Status, StatusCode, Advice, \ 
    44     XSStringAttributeValue, XSGroupRoleAttributeValue 
     41    AuthnStatement, AuthzDecisionStatement, Assertion, Conditions, \ 
     42    AttributeValue, AttributeQuery, Subject, NameID, Issuer, Response, \ 
     43    Status, StatusCode, Advice, XSStringAttributeValue, \ 
     44    XSGroupRoleAttributeValue 
     45from saml.common import SAMLVersion 
    4546from saml.common.xml import SAMLConstants 
     47from saml.common.xml import QName as GenericQName 
    4648from saml.xml import XMLTypeParseError 
    4749from saml.utils import SAMLDateTime 
    48 from saml.xml import QName as GenericQName 
    4950 
    5051 
     
    112113                            "Namespace URI'") 
    113114 
     115    def __eq__(self, qname): 
     116        """Enable equality check for QName.  Note that prefixes don't need to 
     117        match 
     118         
     119        @type qname: ndg.security.common.utils.etree.QName 
     120        @param qname: Qualified Name to compare with self  
     121        """ 
     122        if not isinstance(qname, QName): 
     123            raise TypeError('Expecting %r; got %r' % (QName, type(qname))) 
     124                    
     125        # Nb. prefixes don't need to agree!          
     126        return (self.namespaceURI, self.localPart) == \ 
     127               (qname.namespaceURI, qname.localPart) 
     128 
     129    def __ne__(self, qname): 
     130        """Enable equality check for QName.  Note that prefixes don't need to 
     131        match 
     132         
     133        @type qname: ndg.security.common.utils.etree.QName 
     134        @param qname: Qualified Name to compare with self  
     135        """ 
     136        return not self.__eq__(qname) 
     137 
    114138    @classmethod 
    115139    def fromGeneric(cls, genericQName): 
    116         '''Cast the generic QName type in saml.xml to the ElementTree specific 
    117         implementation''' 
     140        '''Cast the generic QName type in saml.common.xml to the  
     141        ElementTree specific implementation''' 
    118142        if not isinstance(genericQName, GenericQName): 
    119143            raise TypeError("Expecting %r for QName, got %r" % (GenericQName, 
     
    921945            XMLTypeClass = self.__toXMLTypeMap.get(input.__class__) 
    922946            if XMLTypeClass is None: 
    923                 raise TypeError("no matching XMLType class representation " 
    924                                 "for SAML class %r" % input.__class__) 
     947                raise UnknownAttrProfile("no matching XMLType class " 
     948                                         "representation for class %r" %  
     949                                         input.__class__) 
    925950                 
    926951        elif ElementTree.iselement(input): 
     
    938963            nXMLTypeClasses = len(XMLTypeClasses) 
    939964            if nXMLTypeClasses == 0: 
    940                 raise TypeError("no matching XMLType class representation " 
    941                                 "for SAML AttributeValue type %r" % input) 
     965                raise UnknownAttrProfile("no matching XMLType class " 
     966                                         "representation for SAML " 
     967                                         "AttributeValue type %r" % input) 
    942968            elif nXMLTypeClasses > 1: 
    943969                raise TypeError("Multiple XMLType classes %r matched for " 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/attributeauthority.py

    r5656 r5663  
    10581058        samlResponse.issuer = Issuer() 
    10591059         
     1060        # Initialise to success status but reset on error 
    10601061        samlResponse.status = Status() 
    10611062        samlResponse.status.statusCode = StatusCode() 
    1062         samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI         
     1063        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI 
     1064         
     1065        # Nb. SAML 2.0 spec says issuer format must be omitted 
     1066        samlResponse.issuer.value = self.issuer 
     1067         
     1068        samlResponse.inResponseTo = attributeQuery.id 
    10631069         
    10641070        # Attribute Query validation ... 
     
    10941100         
    10951101        elif attributeQuery.issuer.format != "urn:esg:issuer": 
    1096             log.error('SAML Attribute Query subject format is "%r"; expecting ' 
    1097                       '"%s"' % (attributeQuery.subject.nameID.format, 
     1102            log.error('SAML Attribute Query issuer format is "%r"; expecting ' 
     1103                      '"%s"' % (attributeQuery.issuer.format, 
    10981104                                "urn:esg:issuer")) 
    10991105            samlResponse.status.statusCode.value = StatusCode.REQUESTER_URI 
    1100             samlResponse.status.statusMessage = \ 
    1101                                 "Issuer Name ID format is not recognised" 
     1106            samlResponse.status.statusMessage="Issuer format is not recognised" 
    11021107            return samlResponse 
    11031108         
     
    11121117                                        attributeQuery.subject.nameID.value, 
    11131118                                        requestedAttributeNames, 
    1114                                         attributeQuery.subject.nameID.value) 
     1119                                        attributeQuery.issuer.value) 
     1120        except InvalidUserId, e: 
     1121            log.exception(e) 
     1122            samlResponse.status.statusCode.value = \ 
     1123                                        StatusCode.UNKNOWN_PRINCIPAL_URI 
     1124            return samlResponse 
     1125             
     1126        except UserIdNotKnown, e: 
     1127            log.exception(e) 
     1128            samlResponse.status.statusCode.value = \ 
     1129                                        StatusCode.UNKNOWN_PRINCIPAL_URI 
     1130            return samlResponse 
     1131             
     1132        except InvalidRequestorId, e: 
     1133            log.exception(e) 
     1134            samlResponse.status.statusCode.value = StatusCode.REQUEST_DENIED_URI 
     1135            return samlResponse 
     1136             
     1137        except AttributeReleaseDenied, e: 
     1138            log.exception(e) 
     1139            samlResponse.status.statusCode.value = \ 
     1140                                        StatusCode.INVALID_ATTR_NAME_VALUE_URI 
     1141            return samlResponse 
     1142             
     1143        except AttributeNotKnownError, e: 
     1144            log.exception(e) 
     1145            samlResponse.status.statusCode.value = \ 
     1146                                        StatusCode.INVALID_ATTR_NAME_VALUE_URI 
     1147            return samlResponse 
     1148             
    11151149        except Exception, e: 
    1116             # TODO: exception handling for requested attributes not allowed  
    1117             # etc. 
    1118             pass 
    1119          
    1120         # Nb. SAML 2.0 spec says issuer format must be omitted 
    1121         samlResponse.issuer.value = self.issuer 
    1122          
    1123         samlResponse.inResponseTo = attributeQuery.id 
     1150            log.exception("Unexpected error calling Attribute Interface " 
     1151                          "for subject [%s] and query issuer [%s]" % 
     1152                          (attributeQuery.subject.nameID.value, 
     1153                           attributeQuery.issuer.value)) 
     1154             
     1155            # SAML spec says application server should set a HTTP 500 Internal 
     1156            # Server error in this case 
     1157            raise  
    11241158         
    11251159        # Create a new assertion to hold the attributes to be returned 
     
    11421176        attributeStatement = AttributeStatement() 
    11431177         
    1144         for attrName, val in attributes.items(): 
    1145             samlAttribute = Attribute() 
    1146             samlAttribute.name = attrName 
    1147             samlAttribute.nameFormat = samlAttribute.nameFormat 
    1148             samlAttribute.friendlyName = samlAttribute.friendlyName 
    1149              
    1150             samlAttributeValue = XSStringAttributeValue() 
    1151             samlAttributeValue.value = val 
    1152             samlAttribute.attributeValues.append(samlAttributeValue) 
     1178        for samlAttribute in attributes: 
    11531179            attributeStatement.attributes.append(samlAttribute) 
    11541180  
     
    15461572        fileLog.setFormatter(formatter)             
    15471573        self.addHandler(fileLog) 
    1548                         
     1574  
     1575                       
    15491576class AttributeInterfaceError(Exception): 
    15501577    """Exception handling for NDG Attribute Authority User Roles interface 
    15511578    class.""" 
    15521579 
    1553  
     1580                        
     1581class AttributeReleaseDenied(AttributeInterfaceError): 
     1582    """Requestor was denied release of the requested attributes""" 
     1583 
     1584                        
     1585class AttributeNotKnownError(AttributeInterfaceError): 
     1586    """Requested attribute names are not known to this authority""" 
     1587 
     1588 
     1589class InvalidRequestorId(AttributeInterfaceError): 
     1590    """Requestor is not known or not allowed to request attributes""" 
     1591     
     1592 
     1593class UserIdNotKnown(AttributeInterfaceError):  
     1594    """User ID passed to getAttributes is not known to the authority""" 
     1595     
     1596     
     1597class InvalidUserId(AttributeInterfaceError): 
     1598    """User Id passed to getAttributes is invalid""" 
     1599     
     1600       
    15541601class AttributeInterface(object): 
    15551602    """An abstract base class to define the user roles interface to an 
     
    15791626        @param userId: user identity e.g. user Distinguished Name 
    15801627        @rtype: list 
    1581         @return: list of roles for the given user ID""" 
     1628        @return: list of roles for the given user ID 
     1629        @raise AttributeInterfaceError: an error occured requesting  
     1630        attributes 
     1631        """ 
    15821632        raise NotImplementedError(self.getRoles.__doc__) 
    15831633  
     
    15951645        @type requestorId: basestring 
    15961646        @param requestorId: identity of the agent making the attribute query 
    1597         @rtype: dict 
    1598         @return: list of attribute name, value pairs for the given user ID""" 
     1647        @rtype: saml.saml2.core.Attribute 
     1648        @return: list of attributes for the given user ID 
     1649        @raise AttributeInterfaceError: an error occured requesting  
     1650        attributes 
     1651        @raise AttributeReleaseDeniedError: Requestor was denied release of the 
     1652        requested attributes 
     1653        @raise AttributeNotKnownError: Requested attribute names are not known  
     1654        to this authority 
     1655        """ 
    15991656        raise NotImplementedError(self.getAttributes.__doc__) 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/attributeauthority.py

    r5644 r5663  
    2020    '''WSGI to add an NDG Security Attribute Authority in the environ 
    2121    ''' 
    22     DEFAULT_KEYNAME = 'ndg.security.server.attributeauthority' 
    23     ENVIRON_KEYNAME_PARAMNAME = 'environKeyName' 
     22    DEFAULT_KEYNAME = 'ndg.security.server.wsgi.attributeauthority' 
     23    ENVIRON_KEYNAME_CFG_OPTNAME = 'environKeyName' 
     24     
     25    DEFAULT_ATTR_QUERY_IFACE_KEYNAME = \ 
     26        'ndg.security.server.wsgi.attributeauthority.attributeQuery' 
     27    ENVIRON_KEYNAME_ATTR_QUERY_IFACE_CFG_OPT_NAME = \ 
     28        'environKeyNameAttributeQueryInterface' 
    2429         
    25     def __init__(self, app, global_conf, prefix='attributeauthority.', 
    26                  **app_conf): 
     30    def __init__(self, app): 
    2731        '''Set-up an Attribute Authority instance 
     32        ''' 
     33        # Stop in debugger at beginning of SOAP stub if environment variable  
     34        # is set 
     35        self.__debug = bool(os.environ.get('NDGSEC_INT_DEBUG')) 
     36        if self.__debug: 
     37            import pdb 
     38            pdb.set_trace() 
     39         
     40        self._app = app 
     41        self.__aa = None 
     42        self.__attributeQuery = None 
     43        self.__keyName = None 
     44        self.__attributeQueryKeyName = None 
     45 
     46    @classmethod 
     47    def filter_app_factory(cls, app, global_conf, prefix='attributeauthority.', 
     48                           **app_conf): 
     49        """Set-up Attribute authority middleware using a Paste app factory  
     50        pattern.  Overloaded base class method to enable custom settings from  
     51        app_conf 
    2852         
    2953        @type app: callable following WSGI interface 
     
    3660        @param app_conf: PasteDeploy application specific configuration  
    3761        dictionary 
    38         ''' 
    39         # Stop in debugger at beginning of SOAP stub if environment variable  
    40         # is set 
    41         self.__debug = bool(os.environ.get('NDGSEC_INT_DEBUG')) 
    42         if self.__debug: 
    43             import pdb 
    44             pdb.set_trace() 
     62        """ 
     63        app = AttributeAuthorityMiddleware(app) 
    4564         
    46         self._app = app 
    47  
    4865        # Set key name for attribute authority set in environ 
    4966        environKeyOptName = prefix + \ 
    50                         AttributeAuthorityMiddleware.ENVIRON_KEYNAME_PARAMNAME 
    51         self.__keyName = app_conf.pop(environKeyOptName, 
    52                                 AttributeAuthorityMiddleware.DEFAULT_KEYNAME) 
     67                    AttributeAuthorityMiddleware.ENVIRON_KEYNAME_CFG_OPTNAME 
     68                     
     69        app_conf.pop(environKeyOptName, 
     70                     AttributeAuthorityMiddleware.DEFAULT_KEYNAME) 
     71 
     72        attrQueryIfaceEnvironKeyOptName = prefix + \ 
     73            AttributeAuthorityMiddleware.\ 
     74            ENVIRON_KEYNAME_ATTR_QUERY_IFACE_CFG_OPT_NAME 
     75             
     76        app.attributeQueryKeyName = app_conf.pop( 
     77            attrQueryIfaceEnvironKeyOptName, 
     78            AttributeAuthorityMiddleware.DEFAULT_ATTR_QUERY_IFACE_KEYNAME) 
    5379         
    54         self.__aa = AttributeAuthority.fromProperties(propPrefix=prefix, 
    55                                                       **app_conf) 
     80        app.aa = AttributeAuthority.fromProperties(propPrefix=prefix, 
     81                                                   **app_conf) 
     82        app.attributeQuery = app.aa.samlAttributeQueryFactory() 
     83         
     84        return app 
    5685        
    5786    def __call__(self, environ, start_response): 
     
    6796        ''' 
    6897        environ[self.keyName] = self.aa 
     98        environ[self.attributeQueryKeyName] = self.attributeQuery 
    6999        return self._app(environ, start_response) 
    70100     
     
    76106            raise TypeError('Expecting %r for "aa" attribute; got %r' % 
    77107                            (AttributeAuthority, type(val))) 
     108        self.__aa = val 
    78109             
    79110    aa = property(fget=_get_aa, 
     
    94125                       doc="Key name used to index Attribute Authority in " 
    95126                           "environ dictionary") 
     127 
     128    def _get_attributeQueryKeyName(self): 
     129        return self.__attributeQueryKeyName 
     130 
     131    def _set_attributeQueryKeyName(self, val): 
     132        if not isinstance(val, basestring): 
     133            raise TypeError('Expecting %r for "attributeQueryKeyName" ' 
     134                            'attribute; got %r' % (basestring, type(val))) 
     135        self.__attributeQueryKeyName = val 
     136         
     137    attributeQueryKeyName = property(fget=_get_attributeQueryKeyName,  
     138                                     fset=_set_attributeQueryKeyName,  
     139                                     doc="Key name used to index Attribute " 
     140                                         "Authority SAML attribute query " 
     141                                         "function in environ dictionary") 
     142     
     143    def _get_attributeQuery(self): 
     144        return self.__attributeQuery 
     145 
     146    def _set_attributeQuery(self, val): 
     147        if not callable(val): 
     148            raise TypeError('Expecting a callable for "attributeQuery" ' 
     149                            'attribute; got %r' % type(val)) 
     150        self.__attributeQuery = val 
     151         
     152    attributeQuery = property(fget=_get_attributeQuery,  
     153                              fset=_set_attributeQuery,  
     154                              doc="Attribute Authority SAML attribute query " 
     155                                  "function") 
  • TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/saml/__init__.py

    r5658 r5663  
    2020    StatusCode 
    2121     
    22 from saml.xml import SAMLConstants 
     22from saml.common.xml import SAMLConstants 
    2323from saml.xml.etree import AssertionElementTree, AttributeQueryElementTree, \ 
    24     ResponseElementTree, XSGroupRoleAttributeValueElementTree 
     24    ResponseElementTree, XSGroupRoleAttributeValueElementTree, QName 
    2525 
    2626from ndg.security.common.soap.etree import SOAPEnvelope 
    27 from ndg.security.common.utils.etree import QName, prettyPrint 
     27from ndg.security.common.utils.etree import prettyPrint 
    2828from ndg.security.server.wsgi import NDGSecurityPathFilter 
    2929from ndg.security.server.wsgi.soap import SOAPMiddleware 
     
    6969        dictionary 
    7070        ''' 
     71        NDGSecurityPathFilter.__init__(self, app, None) 
     72         
    7173        self._app = app 
    7274        self.__queryInterfaceKeyName = None 
     
    154156        
    155157        attributeQueryElem = soapRequest.body.elem[0] 
    156         attributeQuery = AttributeQueryElementTree.fromXML(attributeQueryElem) 
    157          
    158         # Check for Query Interface in environ 
    159         queryInterface = environ.get(self.queryInterfaceKeyName) 
    160         if queryInterface is None: 
    161             raise SOAPAttributeInterfaceMiddlewareConfigError( 
     158         
     159        try: 
     160            attributeQuery = AttributeQueryElementTree.fromXML( 
     161                                                            attributeQueryElem) 
     162        except UnknownAttrProfile, e: 
     163            log.exception("Parsing incoming attribute query: " % e) 
     164            samlResponse = self._makeErrorResponse( 
     165                                        StatusCode.UNKNOWN_ATTR_PROFILE_URI) 
     166        else:    
     167            # Check for Query Interface in environ 
     168            queryInterface = environ.get(self.queryInterfaceKeyName) 
     169            if queryInterface is None: 
     170                raise SOAPAttributeInterfaceMiddlewareConfigError( 
    162171                                'No query interface "%s" key found in environ'% 
    163172                                self.queryInterfaceKeyName) 
    164          
    165         # Call query interface         
    166         samlResponse = queryInterface(attributeQuery) 
     173             
     174            # Call query interface         
     175            samlResponse = queryInterface(attributeQuery) 
    167176         
    168177        # Add mapping for ESG Group/Role Attribute Value to enable ElementTree 
     
    200209                                                        "request SOAP " 
    201210                                                        "Envelope body") 
    202          
    203         return QName(soapBody.elem.tag) == AttributeQuery.DEFAULT_ELEMENT_NAME 
     211             
     212        inputQName = QName(soapBody.elem[0].tag)     
     213        attributeQueryQName = QName.fromGeneric( 
     214                                        AttributeQuery.DEFAULT_ELEMENT_NAME) 
     215        return inputQName == attributeQueryQName 
     216 
     217    def _makeErrorResponse(self, code): 
     218        """Convenience method for making a basic response following an error 
     219        """ 
     220        samlResponse = Response() 
     221         
     222        samlResponse.issueInstant = datetime.utcnow()             
     223        samlResponse.id = str(uuid4()) 
     224         
     225        # Initialise to success status but reset on error 
     226        samlResponse.status = Status() 
     227        samlResponse.status.statusCode = StatusCode() 
     228        samlResponse.status.statusCode.value = code 
     229         
     230        return samlResponse 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/config/attributeauthority/sitea/siteAUserRoles.py

    r5182 r5663  
    1212 
    1313 
    14 from ndg.security.server.attributeauthority import AttributeInterface 
     14from ndg.security.server.attributeauthority import AttributeInterface, \ 
     15    InvalidRequestorId, AttributeNotKnownError, AttributeReleaseDenied, \ 
     16    UserIdNotKnown 
     17from saml.common.xml import SAMLConstants 
     18from saml.saml2.core import Attribute, XSStringAttributeValue 
    1519 
    1620 
    1721class TestUserRoles(AttributeInterface): 
    1822    """Test User Roles class dynamic import for Attribute Authority""" 
     23     
     24    SAML_ATTRIBUTE_NAMES = ( 
     25        'urn:esg:email:address', 
     26        'urn:esg:first:name',  
     27        'urn:esg:last:name' 
     28    ) 
     29    SAML_ATTRIBUTE_VALUES = ( 
     30        'p.kershaw@somewhere.ac.uk', 
     31        'Philip', 
     32        'Kershaw' 
     33    ) 
     34    SAML_ATTRIBUTE_FRIENDLY_NAMES = ( 
     35        "emailAddress", 
     36        "FirstName", 
     37        "LastName" 
     38    ) 
     39    SAML_ATTRIBUTE_FORMATS = (SAMLConstants.XSD_NS+"#"+\ 
     40                            XSStringAttributeValue.TYPE_LOCAL_NAME,) * 3 
     41    SAML_ATTRIBUTES = [] 
     42     
     43    for name, val, format, friendlyName in zip(SAML_ATTRIBUTE_NAMES, 
     44                                               SAML_ATTRIBUTE_VALUES, 
     45                                               SAML_ATTRIBUTE_FORMATS, 
     46                                               SAML_ATTRIBUTE_FRIENDLY_NAMES): 
     47        SAML_ATTRIBUTES.append(Attribute()) 
     48        SAML_ATTRIBUTES[-1].name = name 
     49        SAML_ATTRIBUTES[-1].nameFormat = format 
     50        SAML_ATTRIBUTES[-1].friendlyName = friendlyName 
     51        SAML_ATTRIBUTES[-1].attributeValues.append(XSStringAttributeValue()) 
     52        SAML_ATTRIBUTES[-1].attributeValues[-1].value = val 
    1953 
     54    del name, val, format, friendlyName 
     55     
     56    VALID_USER_IDS = ("https://openid.localhost/philip.kershaw",) 
     57    VALID_REQUESTOR_IDS = ("Site A", "Site B") 
     58    INSUFFICIENT_PRIVILEGES_REQUESTOR_ID = "Site B" 
     59     
    2060    def __init__(self, propertiesFilePath=None): 
    2161        pass 
    22  
    23  
    24     def userIsRegistered(self, userId): 
    25         return True 
    26  
    2762 
    2863    def getRoles(self, userId): 
     
    3368            'urn:siteA:security:authz:1.0:attr:coapec' 
    3469        ]  
     70 
     71    def getAttributes(self, userId, requestedAttributeNames, requestorId): 
     72        '''Test Attribute Authority SAML Attribute Query interface''' 
     73         
     74        if userId not in TestUserRoles.VALID_USER_IDS: 
     75            raise UserIdNotKnown('User Id "%s" is not known to this authority' 
     76                                 % userId) 
     77             
     78        if requestorId not in TestUserRoles.VALID_REQUESTOR_IDS: 
     79            raise InvalidRequestorId('Requestor identity "%s" is invalid' % 
     80                                     requestorId) 
     81         
     82        unknownAttrNames = [attrName for attrName in requestedAttributeNames 
     83                            if attrName not in  
     84                            TestUserRoles.SAML_ATTRIBUTE_NAMES] 
     85         
     86        if len(unknownAttrNames) > 0: 
     87            raise AttributeNotKnownError("Unknown attributes requested: %r" % 
     88                                         unknownAttrNames) 
     89             
     90        if requestorId == TestUserRoles.INSUFFICIENT_PRIVILEGES_REQUESTOR_ID: 
     91            raise AttributeReleaseDenied("Attribute release denied for the " 
     92                                         'requestor "%s"' % requestorId) 
     93         
     94        return TestUserRoles.SAML_ATTRIBUTES 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/test.ini

    r5656 r5663  
    66[DEFAULT] 
    77testConfigDir = ../../../config 
     8port = 5000 
     9baseURI = localhost:%(port)s 
     10 
    811[server:main] 
    912use = egg:Paste#http 
    1013host = 0.0.0.0 
    11 port = 5000 
     14port = %(port)s 
    1215 
    1316[pipeline:main] 
     
    1821 
    1922[filter:SAMLSoapAttributeInterfaceFilter] 
    20 paste.filter_app_factory = ndg.security.server.wsgi.saml:SOAPAttributeInterfaceMiddleware 
     23paste.filter_app_factory = ndg.security.server.wsgi.saml:SOAPAttributeInterfaceMiddleware.filter_app_factory 
    2124prefix = saml. 
     25saml.pathMatchList = /attributeauthority/saml 
    2226saml.queryInterfaceKeyName = attributeQueryInterface 
    2327 
     
    2832# This filter is a container for a binding to a SOAP based interface to the 
    2933# Attribute Authority 
    30 paste.filter_app_factory = ndg.security.server.wsgi.zsi:SOAPBindingMiddleware 
     34paste.filter_app_factory = ndg.security.server.wsgi.attributeauthority:AttributeAuthorityMiddleware.filter_app_factory 
    3135 
    32 # Provide an identifier for this filter so that main WSGI app  
    33 # CombinedServicesWSGI Session Manager filter can call this Attribute Authority 
    34 # directly 
    35 referencedFilters = filter:wsseSignatureVerificationFilter 
     36prefix = attributeAuthority. 
    3637 
    37 # Path from URL for Attribute Authority in this Paste deployment 
    38 path = /AttributeAuthority 
    39  
    40 # External endpoint for this Attribute Authority - must agree with setting used 
    41 # to invoke this service set in: 
    42 # * serverapp.py  
    43 # * or port in [server:main] if calling with paster serve securityservices.ini 
    44 # * or something else e.g. proxied through Apache? 
    45 # This setting is used by Attribute Authority clients in this WSGI stack to see 
    46 # if a request is being made to the local service or to another Attribute  
    47 # Authority running elsewhere 
    48 publishedURI = %(baseURI)s%(path)s 
    49  
    50 # Enable ?wsdl query argument to list the WSDL content 
    51 enableWSDLQuery = True 
    52 charset = utf-8 
    53 filterID = %(__name__)s 
    54  
    55 # Use this ZSI generated SOAP service interface class to handle i/o for this 
    56 # filter 
    57 ServiceSOAPBindingClass = ndg.security.server.zsi.attributeauthority.AttributeAuthorityWS 
    58  
    59 # SOAP Binding Class specific keywords are in this section identified by this 
    60 # prefix: 
    61 ServiceSOAPBindingPropPrefix = attributeAuthority 
    62  
    63 attributeAuthority.wsseSignatureVerificationFilterID = filter:wsseSignatureVerificationFilter 
     38attributeAuthority.environKeyName: attributeauthority 
     39attributeAuthority.environKeyNameAttributeQueryInterface: attributeQueryInterface 
    6440 
    6541# Attribute Authority settings 
     
    9066# Settings for custom AttributeInterface derived class to get user roles for given  
    9167# user ID 
    92 #attributeAuthority.attributeInterface.modFilePath: %(testConfigDir)s/attributeauthority/sitea 
    93 attributeAuthority.attributeInterface.modName: ndg.security.test.integration.authz.attributeinterface 
     68attributeAuthority.attributeInterface.modFilePath: %(testConfigDir)s/attributeauthority/sitea 
     69attributeAuthority.attributeInterface.modName: ndg.security.test.config.attributeauthority.sitea.siteAUserRoles 
    9470attributeAuthority.attributeInterface.className: TestUserRoles 
    9571 
  • TI12-security/trunk/python/ndg_security_test/ndg/security/test/unit/wsgi/saml/test_soapattributeinterface.py

    r5656 r5663  
    2020from paste.deploy import loadapp 
    2121 
    22 from ndg.security.server.wsgi.saml import SOAPAttributeInterfaceMiddleware 
    23 from ndg.security.server.wsgi.attributeauthority import \ 
    24     AttributeAuthorityMiddleware 
    25  
    2622from cStringIO import StringIO 
    2723from xml.etree import ElementTree 
    2824 
    2925from ndg.security.common.soap.etree import SOAPEnvelope 
    30 from ndg.security.common.utils.etree import QName, prettyPrint 
     26from ndg.security.common.utils.etree import prettyPrint 
    3127 
    3228from saml.saml2.core import Response, Assertion, Attribute, AttributeValue, \ 
     
    4036     
    4137class TestApp(object): 
    42     def __init__(self, app, global_conf, **app_conf): 
     38    def __init__(self, global_conf, **app_conf): 
    4339        pass 
    4440     
     
    4945                        ('Content-type', 'text/plain')]) 
    5046                             
    51         return response 
     47        return [response] 
    5248 
    5349 
     
    6157        unittest.TestCase.__init__(self, *args, **kwargs) 
    6258 
    63     def test01AttributeQuery(self): 
     59    def _createAttributeQuery(self,  
     60                        issuer="Site A", 
     61                        subject="https://openid.localhost/philip.kershaw"): 
    6462        attributeQuery = AttributeQuery() 
    6563        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20) 
     
    6866         
    6967        attributeQuery.issuer = Issuer() 
    70         attributeQuery.issuer.format = Issuer.X509_SUBJECT 
    71         attributeQuery.issuer.value = \ 
    72                         "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk" 
    73                          
     68        attributeQuery.issuer.format = "urn:esg:issuer" 
     69        attributeQuery.issuer.value = issuer 
    7470                         
    7571        attributeQuery.subject = Subject()   
    7672        attributeQuery.subject.nameID = NameID() 
    7773        attributeQuery.subject.nameID.format = "urn:esg:openid" 
    78         attributeQuery.subject.nameID.value = \ 
    79                                     "https://openid.localhost/philip.kershaw" 
     74        attributeQuery.subject.nameID.value = subject 
     75                                     
    8076         
    8177        # special case handling for 'FirstName' attribute 
     
    10298        emailAddressAttribute.friendlyName = "emailAddress" 
    10399 
    104         attributeQuery.attributes.append(emailAddressAttribute)                                    
    105          
     100        attributeQuery.attributes.append(emailAddressAttribute)   
     101 
     102        return attributeQuery 
     103     
     104    def _makeRequest(self, attributeQuery=None, **kw): 
     105        """Convenience method to construct queries for tests""" 
     106         
     107        if attributeQuery is None: 
     108            attributeQuery = self._createAttributeQuery(**kw) 
     109             
    106110        elem = AttributeQueryElementTree.toXML(attributeQuery) 
    107111        soapRequest = SOAPEnvelope() 
     
    111115        request = soapRequest.serialize() 
    112116         
    113         header = { 
    114             'soapAction': "http://www.oasis-open.org/committees/security", 
    115             'Content-length': str(len(request)), 
    116             'Content-type': 'text/xml' 
    117         } 
    118         response = self.app.post('/attributeauthority',  
    119                                  params=request,  
    120                                  headers=header,  
    121                                  status=200) 
    122         print("Response status=%d" % response.status) 
    123  
     117        return request 
     118     
     119    def _getSAMLResponse(self, responseBody): 
     120        """Deserialise response string into ElementTree element""" 
    124121        soapResponse = SOAPEnvelope() 
    125122         
    126123        responseStream = StringIO() 
    127         responseStream.write(response.body) 
     124        responseStream.write(responseBody) 
    128125        responseStream.seek(0) 
    129126         
     
    135132         
    136133        response = ResponseElementTree.fromXML(soapResponse.body.elem[0]) 
    137         self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI) 
    138         self.assert_(response.inResponseTo == attributeQuery.id) 
    139         self.assert_(response.assertions[0].subject.nameID.value == \ 
     134         
     135        return response 
     136     
     137    def test01ValidQuery(self): 
     138        attributeQuery = self._createAttributeQuery() 
     139        request = self._makeRequest(attributeQuery=attributeQuery) 
     140         
     141        header = { 
     142            'soapAction': "http://www.oasis-open.org/committees/security", 
     143            'Content-length': str(len(request)), 
     144            'Content-type': 'text/xml' 
     145        } 
     146        response = self.app.post('/attributeauthority/saml',  
     147                                 params=request,  
     148                                 headers=header,  
     149                                 status=200) 
     150        print("Response status=%d" % response.status) 
     151        samlResponse = self._getSAMLResponse(response.body) 
     152 
     153        self.assert_(samlResponse.status.statusCode.value == \ 
     154                     StatusCode.SUCCESS_URI) 
     155        self.assert_(samlResponse.inResponseTo == attributeQuery.id) 
     156        self.assert_(samlResponse.assertions[0].subject.nameID.value == \ 
    140157                     attributeQuery.subject.nameID.value) 
    141158 
    142  
     159    def test02AttributeReleaseDenied(self): 
     160        request = self._makeRequest(issuer="Site B") 
     161         
     162        header = { 
     163            'soapAction': "http://www.oasis-open.org/committees/security", 
     164            'Content-length': str(len(request)), 
     165            'Content-type': 'text/xml' 
     166        } 
     167         
     168        response = self.app.post('/attributeauthority/saml',  
     169                                 params=request,  
     170                                 headers=header,  
     171                                 status=200) 
     172         
     173        print("Response status=%d" % response.status) 
     174         
     175        samlResponse = self._getSAMLResponse(response.body) 
     176 
     177        self.assert_(samlResponse.status.statusCode.value == \ 
     178                     StatusCode.INVALID_ATTR_NAME_VALUE_URI) 
     179 
     180    def test03InvalidAttributesRequested(self): 
     181        attributeQuery = self._createAttributeQuery() 
     182         
     183        # Add an unsupported Attribute name 
     184        attribute = Attribute() 
     185        attribute.name = "urn:my:attribute" 
     186        attribute.nameFormat = XMLConstants.XSD_NS+"#"+\ 
     187                                    XSStringAttributeValue.TYPE_LOCAL_NAME 
     188        attribute.friendlyName = "myAttribute" 
     189        attributeQuery.attributes.append(attribute)      
     190         
     191        request = self._makeRequest(attributeQuery=attributeQuery) 
     192            
     193        header = { 
     194            'soapAction': "http://www.oasis-open.org/committees/security", 
     195            'Content-length': str(len(request)), 
     196            'Content-type': 'text/xml' 
     197        } 
     198        
     199        response = self.app.post('/attributeauthority/saml',  
     200                                 params=request,  
     201                                 headers=header,  
     202                                 status=200) 
     203         
     204        print("Response status=%d" % response.status) 
     205         
     206        samlResponse = self._getSAMLResponse(response.body) 
     207 
     208        self.assert_(samlResponse.status.statusCode.value == \ 
     209                     StatusCode.INVALID_ATTR_NAME_VALUE_URI) 
     210         
     211    def test04InvalidIssuer(self): 
     212        request = self._makeRequest(issuer="My Attribute Query Issuer") 
     213         
     214        header = { 
     215            'soapAction': "http://www.oasis-open.org/committees/security", 
     216            'Content-length': str(len(request)), 
     217            'Content-type': 'text/xml' 
     218        } 
     219        
     220        response = self.app.post('/attributeauthority/saml',  
     221                                 params=request,  
     222                                 headers=header,  
     223                                 status=200) 
     224         
     225        print("Response status=%d" % response.status) 
     226         
     227        samlResponse = self._getSAMLResponse(response.body) 
     228 
     229        self.assert_(samlResponse.status.statusCode.value == \ 
     230                     StatusCode.REQUEST_DENIED_URI) 
     231 
     232    def test05UnknownPrincipal(self): 
     233        request = self._makeRequest(subject="Joe.Bloggs") 
     234         
     235        header = { 
     236            'soapAction': "http://www.oasis-open.org/committees/security", 
     237            'Content-length': str(len(request)), 
     238            'Content-type': 'text/xml' 
     239        } 
     240         
     241        response = self.app.post('/attributeauthority/saml',  
     242                                 params=request,  
     243                                 headers=header,  
     244                                 status=200) 
     245         
     246        print("Response status=%d" % response.status) 
     247         
     248        samlResponse = self._getSAMLResponse(response.body) 
     249 
     250        self.assert_(samlResponse.status.statusCode.value == \ 
     251                     StatusCode.UNKNOWN_PRINCIPAL_URI) 
     252            
    143253if __name__ == "__main__": 
    144254    unittest.main() 
Note: See TracChangeset for help on using the changeset viewer.