Ignore:
Timestamp:
06/08/10 09:49:47 (10 years ago)
Author:
pjkersha
Message:

Incomplete - task 2: XACML-Security Integration

  • Working WSGI Authorisation filter with connection to SAML/XACML based Authorisation Service - unit tests: ndg.security.test.unit.wsgi.authz.test_authz
  • It may need some optimisation to avoid too many WS callouts to the Authorisation Service - perhaps add a local PDP to the authorisation filter to filter out some requests going over the wire e.g. requests for web page CSS or graphics content.
  • The XACML policy file has some big additions to it to support the various test conditions in ndg.security.test.unit.wsgi.authz.test_authz. These should be ported back to the ndg_xacml package unit tests.
  • Next major task: remove temp fix in XACML Context handler - instead of using hardwired roles for the user alter it so that the PDP makes a request back to the PIP (Policy Enforcement Point) to grab additional attributes. The PIP will call to Attibute Service(s) to pull any additional attributes needed/
Location:
TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/config/authorisationservice/policy.xml

    r7257 r7287  
    66    RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides"> 
    77    <Description> 
    8         NDG XACML example for unit tests: allow access for resource URIs  
    9         matching given regular expressions.  The subject must have at least one 
    10         of a set of named attributes allocated  
     8        Example for NDG Security unit tests: allow access for resource URIs  
     9        defined in the rules.  All other URIs are blocked from access 
     10         
     11        See ndg.security.test.unit.wsgi.authz.test_authz to see the various  
     12        rules tested out 
    1113    </Description> 
    1214     
     
    7880    <Rule RuleId="urn:ndg:security:secured-uri-rule" Effect="Permit"> 
    7981        <!--  
    80             Rule target(s) define which requests apply to the particular rule 
     82            Secure a URI path and all sub-paths using a regular expression to  
     83            define a URI pattern 
    8184        --> 
    8285        <Target> 
     
    99102             
    100103            The user must have at least one of the roles set - in this 
    101             case 'urn:siteA:security:authz:1.0:attr:staff' 
     104            case 'staff' 
    102105        --> 
    103106        <Condition> 
     
    118121            <Resources> 
    119122                <Resource> 
    120                     <ResourceMatch MatchId="urn:oasis:names:tc:xacml:2.0:function:anyURI-regexp-match"> 
     123                    <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:anyURI-equal"> 
    121124                        <ResourceAttributeDesignator 
    122                             AttributeId="urn:siteA:security:authz:1.0:attr:resourceURI" 
     125                            AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" 
    123126                            DataType="http://www.w3.org/2001/XMLSchema#anyURI"/> 
    124                         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">^http://localhost/test_accessGrantedToSecuredURI</AttributeValue> 
     127                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">http://localhost/test_accessGrantedToSecuredURI</AttributeValue> 
     128                    </ResourceMatch> 
     129                </Resource> 
     130            </Resources> 
     131        </Target> 
     132        <Condition> 
     133            <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of"> 
     134                <SubjectAttributeDesignator  
     135                    AttributeId="urn:ndg:security:authz:1.0:attr"  
     136                    DataType="http://www.w3.org/2001/XMLSchema#string"/> 
     137                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-bag"> 
     138                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">staff</AttributeValue> 
     139                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">administrator</AttributeValue> 
     140                </Apply> 
     141            </Apply> 
     142        </Condition> 
     143    </Rule> 
     144    <Rule RuleId="Access Granted to secured URI Rule modified for special admin query argument" Effect="Permit"> 
     145        <!--  
     146            This rule is a modified version of the above to allow for a real use 
     147            case where adding a special query argument grants extra privileges 
     148            associated with an administrator 
     149        --> 
     150        <Target> 
     151            <Resources> 
     152                <Resource> 
     153                    <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:anyURI-equal"> 
     154                        <ResourceAttributeDesignator 
     155                            AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" 
     156                            DataType="http://www.w3.org/2001/XMLSchema#anyURI"/> 
     157                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#anyURI">http://localhost/test_accessGrantedToSecuredURI?admin=1</AttributeValue> 
    125158                    </ResourceMatch> 
    126159                </Resource> 
     
    128161            <Subjects> 
    129162                <Subject> 
    130                     <SubjectMatch> 
     163                    <SubjectMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal"> 
    131164                        <SubjectAttributeDesignator  
    132165                            AttributeId="urn:ndg:security:authz:1.0:attr"  
    133166                            DataType="http://www.w3.org/2001/XMLSchema#string"/> 
    134                         <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">keepout</AttributeValue> 
     167                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">administrator</AttributeValue> 
    135168                    </SubjectMatch> 
    136169                </Subject> 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/pep-result-handler-test.ini

    r7077 r7287  
    1919 
    2020[filter:AuthZFilter] 
    21 paste.filter_app_factory=ndg.security.server.wsgi.authz:SAMLAuthorizationMiddleware.filter_app_factory 
     21paste.filter_app_factory=ndg.security.server.wsgi.authz:AuthorisationFilter.filter_app_factory 
    2222prefix = authz. 
    23 policy.filePath = %(here)s/saml-policy.xml 
    2423 
    25 authz.pepResultHandler = ndg.security.server.wsgi.authz.result_handler.redirect.HTTPRedirectPEPResultHandlerMiddleware 
    26 authz.pepResultHandler.redirectURI = /nowhere 
     24# This result handler responds with a redirect request to the client if access 
     25# denied to the original requested URI 
     26authz.resultHandler = ndg.security.server.wsgi.authz.result_handler.redirect.HTTPRedirectPEPResultHandlerMiddleware 
     27authz.resultHandler.redirectURI = /test_accessGrantedToSecuredURI 
    2728 
    28 # Settings for Policy Information Point used by the Policy Decision Point to 
    29 # retrieve subject attributes from the Attribute Authority associated with the 
    30 # resource to be accessed 
     29# Settings for the Policy Enforcement Point  
     30authz.pep.sessionKey = beaker.session.ndg.security 
     31authz.pep.authzServiceURI = https://localhost:9443/authorisation-service 
    3132 
    3233# If omitted, DN of SSL Cert is used 
    33 pip.attributeQuery.issuerName =  
    34 pip.attributeQuery.clockSkewTolerance = 0. 
    35 pip.attributeQuery.queryAttributes.0 = urn:siteA:security:authz:1.0:attr, , http://www.w3.org/2001/XMLSchema#string 
    36 pip.attributeQuery.sslCACertDir=%(testConfigDir)s/ca 
    37 pip.attributeQuery.sslCertFilePath=%(testConfigDir)s/pki/test.crt 
    38 pip.attributeQuery.sslPriKeyFilePath=%(testConfigDir)s/pki/test.key 
     34authz.pep.authzDecisionQuery.issuerName = /O=NDG/OU=BADC/CN=test 
     35authz.pep.authzDecisionQuery.issuerFormat = urn:oasis:names:tc:SAML:1.1:nameid-format:x509SubjectName 
     36authz.pep.authzDecisionQuery.subjectIdFormat = urn:esg:openid 
     37authz.pep.authzDecisionQuery.clockSkewTolerance = 0. 
     38authz.pep.authzDecisionQuery.sslCACertDir=%(testConfigDir)s/ca 
     39authz.pep.authzDecisionQuery.sslCertFilePath=%(testConfigDir)s/pki/test.crt 
     40authz.pep.authzDecisionQuery.sslPriKeyFilePath=%(testConfigDir)s/pki/test.key 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/saml-test.ini

    r7168 r7287  
    1313 
    1414[pipeline:main] 
    15 pipeline = AuthZFilter TestApp 
     15pipeline = PolicyEnforcementPointFilter TestApp 
    1616 
    1717[app:TestApp] 
    1818paste.app_factory = ndg.security.test.unit.wsgi.authz.test_authz:TestAuthZMiddleware 
    1919 
    20 [filter:AuthZFilter] 
    21 paste.filter_app_factory=ndg.security.server.wsgi.authz.pep:SamlPepMiddleware.filter_app_factory 
     20[filter:PolicyEnforcementPointFilter] 
     21paste.filter_app_factory=ndg.security.server.wsgi.authz.pep:SamlPepFilter.filter_app_factory 
    2222prefix = pep. 
    2323pep.sessionKey = beaker.session.ndg.security 
    2424pep.authzServiceURI = https://localhost:9443/authorisation-service 
    25  
    26 pep.pepResultHandler = ndg.security.test.unit.wsgi.authz.test_authz.RedirectFollowingAccessDenied 
    2725 
    2826# Settings for Policy Information Point used by the Policy Decision Point to 
  • TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/unit/wsgi/authz/test_authz.py

    r7257 r7287  
    2929from ndg.security.server.wsgi.authz.result_handler.redirect import \ 
    3030    HTTPRedirectPEPResultHandlerMiddleware 
    31 from ndg.security.server.wsgi.authz.pep import SamlPepMiddlewareConfigError 
     31from ndg.security.server.wsgi.authz.pep import SamlPepFilterConfigError 
    3232 
    3333 
     
    149149class TestAuthZMiddleware(object): 
    150150    '''Test Application for the Authentication handler to protect''' 
    151     response = "Test Authorization application" 
     151    RESPONSE = "Test Authorization application" 
    152152        
    153153    def __init__(self, app_conf, **local_conf): 
     
    177177        start_response(status, 
    178178                       [('Content-length',  
    179                          str(len(TestAuthZMiddleware.response))), 
     179                         str(len(TestAuthZMiddleware.RESPONSE))), 
    180180                        ('Content-type', 'text/plain')]) 
    181181         
    182         return [TestAuthZMiddleware.response] 
     182        return [TestAuthZMiddleware.RESPONSE + ' returned: ' + status] 
    183183 
    184184 
     
    188188    def save(self): 
    189189        pass 
    190   
    191      
    192 class SamlWSGIAuthZTestCase(BaseTestCase): 
     190 
     191 
     192class BaseAuthzFilterTestCase(BaseTestCase): 
     193    """Base class for NDG Security WSGI authorisation filters 
     194    """ 
    193195    INI_FILE = 'saml-test.ini' 
    194196    THIS_DIR = path.dirname(path.abspath(__file__)) 
     197    INI_FILEPATH = None # Set in __init__ to enable derived classes to alter 
    195198    SESSION_KEYNAME = 'beaker.session.ndg.security' 
    196199     
    197     def __init__(self, *args, **kwargs):        
     200    def __init__(self, *args, **kwargs):    
     201        """Test the authorisation filter using Paste fixture and set up  
     202        Authorisation and Attribute Services needed for making authorisation  
     203        decisions 
     204        """    
    198205        BaseTestCase.__init__(self, *args, **kwargs) 
    199206 
    200         wsgiapp = loadapp('config:'+SamlWSGIAuthZTestCase.INI_FILE,  
    201                           relative_to=SamlWSGIAuthZTestCase.THIS_DIR) 
     207        wsgiapp = loadapp('config:'+self.__class__.INI_FILE,  
     208                          relative_to=self.__class__.THIS_DIR) 
    202209        self.app = paste.fixture.TestApp(wsgiapp) 
    203210         
     211        self.__class__.INI_FILEPATH = os.path.join(self.__class__.THIS_DIR,  
     212                                                   self.__class__.INI_FILE) 
     213         
    204214        self.startSiteAAttributeAuthority(withSSL=True, 
    205             port=SamlWSGIAuthZTestCase.SITEA_SSL_ATTRIBUTEAUTHORITY_PORTNUM) 
    206          
    207         self.startAuthorisationService() 
     215            port=self.__class__.SITEA_SSL_ATTRIBUTEAUTHORITY_PORTNUM) 
     216         
     217        self.startAuthorisationService()   
     218           
     219           
     220class SamlPepFilterTestCase(BaseAuthzFilterTestCase): 
     221    """Test SAML based Policy Enforcement Filter.  This has a SAML authorisation 
     222    decision query interface to call to a remote authorisation service""" 
    208223 
    209224    def test01CatchNoBeakerSessionFound(self): 
     
    211226        # PEPFilterConfigError is raised if no beaker.session is set in  
    212227        # environ 
    213         self.assertRaises(SamlPepMiddlewareConfigError, self.app.get,  
     228        self.assertRaises(SamlPepFilterConfigError, self.app.get,  
    214229                          '/test_200') 
    215230        
     
    223238        response = self.app.get('/test_200', 
    224239                                extra_environ=extra_environ) 
     240        print response 
    225241 
    226242    def test03Catch401WithLoggedIn(self): 
     
    238254                                extra_environ=extra_environ, 
    239255                                status=401) 
     256        print response 
    240257 
    241258    def test04Catch403WithLoggedIn(self): 
     
    246263        extra_environ = { 
    247264            self.__class__.SESSION_KEYNAME: 
    248                 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 
     265                BeakerSessionStub(username=SamlPepFilterTestCase.OPENID_URI), 
    249266            'REMOTE_USER': self.__class__.OPENID_URI 
    250267        } 
     
    252269                                extra_environ=extra_environ, 
    253270                                status=403) 
     271        print response 
    254272 
    255273    def test05Catch401WithNotLoggedInAndSecuredURI(self): 
    256         # AuthZ middleware grants access because the URI requested is has no 
     274        # AuthZ middleware grants access because the URI requested has no 
    257275        # subject restriction set in the policy rule 
    258276         
     
    263281                                extra_environ=extra_environ, 
    264282                                status=401) 
     283        print response 
    265284         
    266285    def test06AccessDeniedForSecuredURI(self): 
     
    270289        extra_environ = { 
    271290            self.__class__.SESSION_KEYNAME: 
    272                 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 
     291                BeakerSessionStub(username=SamlPepFilterTestCase.OPENID_URI), 
    273292            'REMOTE_USER': self.__class__.OPENID_URI 
    274293        } 
     
    278297                                status=403) 
    279298        print response 
    280         self.assert_("Insufficient privileges to access the " 
    281                      "resource" in response) 
    282299 
    283300    def test07AccessGrantedForSecuredURI(self): 
     
    287304        extra_environ = { 
    288305            self.__class__.SESSION_KEYNAME: 
    289                 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 
     306                BeakerSessionStub(username=SamlPepFilterTestCase.OPENID_URI), 
    290307            'REMOTE_USER': self.__class__.OPENID_URI 
    291308        } 
     
    294311                                extra_environ=extra_environ, 
    295312                                status=200) 
    296         self.assert_(TestAuthZMiddleware.response in response) 
    297         print response 
    298  
    299     def test08AccessDeniedForAdminQueryArg(self): 
     313        self.assert_(TestAuthZMiddleware.RESPONSE in response) 
     314        print response 
     315 
     316 
     317class PEPResultHandlerTestCase(BaseAuthzFilterTestCase): 
     318    """Test Authorisation Filter - this contains the PEP filter and a result 
     319    handler which enables customisation of behaviour on 403 Forbidden responses 
     320    """ 
     321    INI_FILE = 'pep-result-handler-test.ini' 
     322    AUTHZ_FILTER_SECTION = 'filter:AuthZFilter' 
     323    AUTHZ_RESULT_HANDLER_REDIRECT_URI_OPTNAME = 'authz.resultHandler.redirectURI' 
     324     
     325    def __init__(self, *arg, **kw): 
     326        BaseAuthzFilterTestCase.__init__(self, *arg, **kw) 
     327         
     328        cfgParser = SafeConfigParser() 
     329        cfgParser.read(self.__class__.INI_FILEPATH) 
     330         
     331        self.redirectURI = cfgParser.get(self.__class__.AUTHZ_FILTER_SECTION, 
     332                    self.__class__.AUTHZ_RESULT_HANDLER_REDIRECT_URI_OPTNAME) 
     333         
     334    def test01RedirectPEPResultHandlerMiddleware(self): 
     335        # User is logged in but doesn't have the required credentials for  
     336        extra_environ = { 
     337            self.__class__.SESSION_KEYNAME: 
     338                        BeakerSessionStub(username=self.__class__.OPENID_URI), 
     339            'REMOTE_USER': self.__class__.OPENID_URI 
     340        } 
     341         
     342        # Expecting result handler to be invoked overriding the 403 response 
     343        response = self.app.get('/test_accessDeniedToSecuredURI', 
     344                                extra_environ=extra_environ, 
     345                                status=302) 
     346        print("Result handler has intercepted the 403 Forbidden response " 
     347              "from the PEP and set this redirect response instead: %s" % 
     348              response) 
     349        self.assert_(response.header_dict.get('location') == self.redirectURI) 
     350 
     351    def test02RedirectFollowingAccessDeniedForAdminQueryArg(self): 
    300352         
    301353        # User is logged in but doesn't have the required credentials for  
     
    303355        extra_environ = { 
    304356            self.__class__.SESSION_KEYNAME: 
    305                 BeakerSessionStub(username=SamlWSGIAuthZTestCase.OPENID_URI), 
     357                BeakerSessionStub(username=SamlPepFilterTestCase.OPENID_URI), 
    306358            'REMOTE_USER': self.__class__.OPENID_URI 
    307359        } 
     
    312364        # into play the PEP result handler defined in this module, 
    313365        # RedirectFollowingAccessDenied.  This class reinvokes the request 
    314         # but without the admin query argument.  Access is then granted for 
    315         # the redirected request 
     366        # but without the admin query argument (see the ini file for what this 
     367        # location is.  Access is then granted because the user has access 
     368        # rights for the new location. 
    316369        response = self.app.get('/test_accessGrantedToSecuredURI', 
    317370                                params={'admin': 1}, 
    318371                                extra_environ=extra_environ, 
    319372                                status=302) 
    320         try: 
    321             redirectResponse = response.follow(extra_environ=extra_environ) 
    322         except paste.fixture.AppError, e: 
    323             self.failIf(TestAuthZMiddleware.response not in response) 
    324         print response 
    325  
    326  
    327 class PEPResultHandlerTestCase(BaseTestCase): 
    328     INI_FILE = 'pep-result-handler-test.ini' 
    329     THIS_DIR = os.path.dirname(os.path.abspath(__file__)) 
    330     INI_FILEPATH = path.join(THIS_DIR, INI_FILE) 
    331     SESSION_KEYNAME = 'beaker.session.ndg.security' 
    332      
    333     def __init__(self, *arg, **kw): 
    334         BaseTestCase.__init__(self, *arg, **kw) 
    335          
    336         here_dir = os.path.dirname(os.path.abspath(__file__)) 
    337         wsgiapp = loadapp('config:'+self.__class__.INI_FILE,  
    338                           relative_to=self.__class__.THIS_DIR) 
    339         self.app = paste.fixture.TestApp(wsgiapp) 
    340          
    341         cfg = SafeConfigParser(dict(here=self.__class__.THIS_DIR)) 
    342         cfg.read(self.__class__.INI_FILEPATH) 
    343         self.redirectURI = cfg.get('filter:AuthZFilter',  
    344                                    'authz.pepResultHandler.redirectURI') 
    345          
    346         self.startSiteAAttributeAuthority(withSSL=True, 
    347             port=SamlWSGIAuthZTestCase.SITEA_SSL_ATTRIBUTEAUTHORITY_PORTNUM) 
    348  
    349          
    350     def testRedirectPEPResultHandlerMiddleware(self): 
    351         # User is logged in but doesn't have the required credentials for  
    352         # access 
    353         raise NotImplementedError('TODO: fix recursion error') 
    354 #        extra_environ = { 
    355 #            self.__class__.SESSION_KEYNAME: 
    356 #                        BeakerSessionStub(username=self.__class__.OPENID_URI) 
    357 #        } 
    358 #         
    359 #        # Expecting redirect response to specified redirect URI 
    360 #        response = self.app.get('/test_accessDeniedToSecuredURI', 
    361 #                                extra_environ=extra_environ, 
    362 #                                status=302) 
    363 #        print(response) 
    364 #        self.assert_(response.header_dict.get('location') == self.redirectURI) 
     373 
     374        print("Redirect Handler has interrupted the 403 Denied response and " 
     375              "added this redirect response instead: %s" % response) 
     376         
     377        # Follow the redirect - the policy should allow access to the new  
     378        # location  
     379        redirectResponse = response.follow(extra_environ=extra_environ, 
     380                                           status=200) 
     381        print("Following the redirect to location %r gives this response: %s" % 
     382              (response.header_dict.get('location'), redirectResponse)) 
     383         
    365384         
    366385if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.