Example #1
0
 def __init__(self, app):
     '''
     Add reference to next WSGI middleware/app and create a SAML 
     authorisation decision query client interface
     '''
     self._app = app
     self._client_binding = AuthzDecisionQuerySslSOAPBinding()
     self._client_query = AuthzDecisionQueryFactory.create()
     self.__session = None
     self.__authzServiceURI = None
     self.__sessionKey = None
     self.__cacheDecisions = False
     self.__localPdp = None
     self.__localPolicyFilePath = None
     self._ignore_file_list_pat = []
Example #2
0
 def __init__(self, app):
     '''
     Add reference to next WSGI middleware/app and create a SAML 
     authorisation decision query client interface
     '''
     self._app = app
     self._client_binding = AuthzDecisionQuerySslSOAPBinding()
     self._client_query = AuthzDecisionQueryFactory.create()
     self.__session = None
     self.__authzServiceURI = None
     self.__sessionKey = None
     self.__cacheDecisions = False
     self.__localPdp = None
     self.__localPolicyFilePath = None
     self._ignore_file_list_pat = []
Example #3
0
 def test02_from_kw(self):
     authz_query = AuthzDecisionQueryFactory.from_kw(
                                                 prefix='authz_q.',
                                                 **self.config)
     
     self.assertEqual(authz_query.subject.nameID.format, 
                      self.config['authz_q.subject.nameID.format'], 
                      'Parameter is %r, expected %r' % (
                       authz_query.subject.nameID.format, 
                       self.config['authz_q.subject.nameID.format']))
     
     self.assertEqual(authz_query.issuer.value, 
                      self.config['authz_q.issuer.value'], 
                      'Parameter is %r, expected %r' % (
                      authz_query.issuer.value, 
                      self.config['authz_q.issuer.value']))
Example #4
0
    def initialise(self, prefix='', **kw):
        '''Initialise object from keyword settings
        
        :type prefix: basestring
        :param prefix: prefix for configuration items
        :type kw: dict        
        :param kw: configuration settings
        dictionary
        :raise SamlPepFilterConfigError: missing option setting(s)
        '''
        # Parse other options
        for name in SamlPepFilter.PARAM_NAMES:
            paramName = prefix + name
            value = kw.get(paramName)
            
            if value is not None:
                setattr(self, name, value)
                
            # All but the local policy settings are manadatory
            elif name not in self.__class__.OPTIONAL_PARAM_NAMES:
                raise SamlPepFilterConfigError('Missing option %r' % paramName)

        # Parse authorisation decision query options - first the bindings i.e.
        # the connection specific settings
        query_binding_prefix = prefix + \
                    self.__class__.AUTHZ_DECISION_QUERY_BINDING_PARAMS_PREFIX
        self.client_binding.parseKeywords(prefix=query_binding_prefix, **kw)
        
        # ... next set constants to do with the authorisation decision queries
        # that will be made.  Settings such as the resource URI and principle
        # (user being queried for) are set on a call by call basis
        query_prefix = prefix + \
                    self.__class__.AUTHZ_DECISION_QUERY_PARAMS_PREFIX
        self.client_query = AuthzDecisionQueryFactory.from_kw(
                                                        prefix=query_prefix,
                                                        **kw)

        # Initialise the local PDP  
        if self.localPolicyFilePath:
            self.__localPdp = PDP.fromPolicySource(self.localPolicyFilePath, 
                                                   XacmlPolicyReaderFactory)
Example #5
0
    def initialise(self, prefix='', **kw):
        '''Initialise object from keyword settings
        
        :type prefix: basestring
        :param prefix: prefix for configuration items
        :type kw: dict        
        :param kw: configuration settings
        dictionary
        :raise SamlPepFilterConfigError: missing option setting(s)
        '''
        # Parse other options
        for name in SamlPepFilter.PARAM_NAMES:
            paramName = prefix + name
            value = kw.get(paramName)

            if value is not None:
                setattr(self, name, value)

            # All but the local policy settings are manadatory
            elif name not in self.__class__.OPTIONAL_PARAM_NAMES:
                raise SamlPepFilterConfigError('Missing option %r' % paramName)

        # Parse authorisation decision query options - first the bindings i.e.
        # the connection specific settings
        query_binding_prefix = prefix + \
                    self.__class__.AUTHZ_DECISION_QUERY_BINDING_PARAMS_PREFIX
        self.client_binding.parseKeywords(prefix=query_binding_prefix, **kw)

        # ... next set constants to do with the authorisation decision queries
        # that will be made.  Settings such as the resource URI and principle
        # (user being queried for) are set on a call by call basis
        query_prefix = prefix + \
                    self.__class__.AUTHZ_DECISION_QUERY_PARAMS_PREFIX
        self.client_query = AuthzDecisionQueryFactory.from_kw(
            prefix=query_prefix, **kw)

        # Initialise the local PDP
        if self.localPolicyFilePath:
            self.__localPdp = PDP.fromPolicySource(self.localPolicyFilePath,
                                                   XacmlPolicyReaderFactory)
Example #6
0
    def enforce(self, environ, start_response):
        """Get access control decision from PDP(s) and enforce the decision
        
        :type environ: dict
        :param environ: WSGI environment variables dictionary
        :type start_response: function
        :param start_response: standard WSGI start response function
        :rtype: iterable
        :return: response
        """
        request = webob.Request(environ)
        requestURI = request.url
        # Nb. user may not be logged in hence REMOTE_USER is not set
        remote_user = request.remote_user or ''

        # Apply local PDP if set
        if not self.is_applicable_request(requestURI):
            # The local PDP has returned a decision that the requested URI is
            # not applicable and so the authorisation service need not be
            # invoked.  This step is an efficiency measure to avoid multiple
            # callouts to the authorisation service for resources which
            # obviously don't need any restrictions
            return self._app(environ, start_response)

        # Check for cached decision
        if self.cacheDecisions:
            assertions = self._retrieveCachedAssertions(requestURI)
        else:
            assertions = None

        noCachedAssertion = assertions is None or len(assertions) == 0
        if noCachedAssertion:
            # No stored decision in cache, invoke the authorisation service

            # Make a new query object
            query = AuthzDecisionQueryFactory.create()

            # Copy constant settings.  These constants were set at
            # initialisation
            query.subject.nameID.format = \
                                        self.client_query.subject.nameID.format
            query.issuer.value = self.client_query.issuer.value
            query.issuer.format = self.client_query.issuer.format

            # Set dynamic settings particular to this individual request
            query.subject.nameID.value = remote_user
            query.resource = request.url

            try:
                samlAuthzResponse = self.client_binding.send(
                    query, uri=self.authzServiceURI)

            except (UrlLib2SOAPClientError, URLError) as e:
                import traceback

                if isinstance(e, UrlLib2SOAPClientError):
                    log.error(
                        "Error, HTTP %s response from authorisation "
                        "service %r requesting access to %r: %s",
                        e.urllib2Response.code, self.authzServiceURI,
                        requestURI, traceback.format_exc())
                else:
                    log.error(
                        "Error, calling authorisation service %r "
                        "requesting access to %r: %s", self.authzServiceURI,
                        requestURI, traceback.format_exc())

                response = webob.Response()
                response.status = httplib.FORBIDDEN
                response.body = ('An error occurred retrieving an access '
                                 'decision for %r for user %r' %
                                 (requestURI, remote_user))
                response.content_type = 'text/plain'
                return response(environ, start_response)

            assertions = samlAuthzResponse.assertions

            # Record the result in the user's session to enable later
            # interrogation by any result handler Middleware
            self.save_result_ctx(query, samlAuthzResponse)

        # Set HTTP 403 Forbidden response if any of the decisions returned are
        # deny or indeterminate status
        failDecisions = (
            DecisionType.DENY,  #@UndefinedVariable
            DecisionType.INDETERMINATE)  #@UndefinedVariable

        # Review decision statement(s) in assertions and enforce the decision
        assertion = None
        for assertion in assertions:
            for authzDecisionStatement in assertion.authzDecisionStatements:
                if authzDecisionStatement.decision.value in failDecisions:
                    response = webob.Response()

                    if not remote_user:
                        # Access failed and the user is not logged in
                        response.status = httplib.UNAUTHORIZED
                    else:
                        # The user is logged in but not authorised
                        response.status = httplib.FORBIDDEN

                    response.body = 'Access denied to %r for user %r' % (
                        requestURI, remote_user)
                    response.content_type = 'text/plain'
                    log.info(response.body)
                    return response(environ, start_response)

        if assertion is None:
            log.error(
                "No assertions set in authorisation decision response "
                "from %r", self.authzServiceURI)

            response = webob.Response()
            response.status = httplib.FORBIDDEN
            response.body = ('An error occurred retrieving an access decision '
                             'for %r for user %r' % (requestURI, remote_user))
            response.content_type = 'text/plain'
            log.info(response.body)
            return response(environ, start_response)

        # Cache assertion if flag is set and it's one that's been freshly
        # obtained from an authorisation decision query rather than one
        # retrieved from the cache
        if self.cacheDecisions and noCachedAssertion:
            self._cacheAssertions(request.url, [assertion])

        # If got through to here then all is well, call next WSGI middleware/app
        return self._app(environ, start_response)
Example #7
0
 def test01_create(self):
     authz_query = AuthzDecisionQueryFactory.create()
     self.assertIsNotNone(authz_query.subject, 'query subject is none')
     self.assertIsNotNone(authz_query.issuer, 'query issuer is none')
Example #8
0
    def enforce(self, environ, start_response):
        """Get access control decision from PDP(s) and enforce the decision
        
        :type environ: dict
        :param environ: WSGI environment variables dictionary
        :type start_response: function
        :param start_response: standard WSGI start response function
        :rtype: iterable
        :return: response
        """
        request = webob.Request(environ)
        requestURI = request.url
        # Nb. user may not be logged in hence REMOTE_USER is not set
        remote_user = request.remote_user or ''
        
        # Apply local PDP if set
        if not self.is_applicable_request(requestURI):
            # The local PDP has returned a decision that the requested URI is
            # not applicable and so the authorisation service need not be 
            # invoked.  This step is an efficiency measure to avoid multiple
            # callouts to the authorisation service for resources which 
            # obviously don't need any restrictions 
            return self._app(environ, start_response)
            
        # Check for cached decision
        if self.cacheDecisions:
            assertions = self._retrieveCachedAssertions(requestURI)
        else:
            assertions = None  
             
        noCachedAssertion = assertions is None or len(assertions) == 0
        if noCachedAssertion:
            # No stored decision in cache, invoke the authorisation service
            
            # Make a new query object   
            query = AuthzDecisionQueryFactory.create()
            
            # Copy constant settings.  These constants were set at 
            # initialisation
            query.subject.nameID.format = \
                                        self.client_query.subject.nameID.format
            query.issuer.value = self.client_query.issuer.value
            query.issuer.format = self.client_query.issuer.format
           
            # Set dynamic settings particular to this individual request 
            query.subject.nameID.value = remote_user
            query.resource = request.url
            
            try:
                samlAuthzResponse = self.client_binding.send(query,
                                                     uri=self.authzServiceURI)
                
            except (UrlLib2SOAPClientError, URLError) as e:
                import traceback
                
                if isinstance(e, UrlLib2SOAPClientError):
                    log.error("Error, HTTP %s response from authorisation "
                              "service %r requesting access to %r: %s", 
                              e.urllib2Response.code,
                              self.authzServiceURI, 
                              requestURI,
                              traceback.format_exc())
                else:
                    log.error("Error, calling authorisation service %r "
                              "requesting access to %r: %s", 
                              self.authzServiceURI, 
                              requestURI,
                              traceback.format_exc()) 
                    
                response = webob.Response()
                response.status = httplib.FORBIDDEN
                response.body = ('An error occurred retrieving an access '
                                 'decision for %r for user %r' % 
                                 (requestURI, remote_user))
                response.content_type = 'text/plain'
                return response(environ, start_response)
                         
            assertions = samlAuthzResponse.assertions
            
            # Record the result in the user's session to enable later 
            # interrogation by any result handler Middleware
            self.save_result_ctx(query, samlAuthzResponse)
        
        
        # Set HTTP 403 Forbidden response if any of the decisions returned are
        # deny or indeterminate status
        failDecisions = (DecisionType.DENY, #@UndefinedVariable
                         DecisionType.INDETERMINATE) #@UndefinedVariable
        
        # Review decision statement(s) in assertions and enforce the decision
        assertion = None
        for assertion in assertions:
            for authzDecisionStatement in assertion.authzDecisionStatements:
                if authzDecisionStatement.decision.value in failDecisions:
                    response = webob.Response()
                    
                    if not remote_user:
                        # Access failed and the user is not logged in
                        response.status = httplib.UNAUTHORIZED
                    else:
                        # The user is logged in but not authorised
                        response.status = httplib.FORBIDDEN
                        
                    response.body = 'Access denied to %r for user %r' % (
                                                                 requestURI,
                                                                 remote_user)
                    response.content_type = 'text/plain'
                    log.info(response.body)
                    return response(environ, start_response)

        if assertion is None:
            log.error("No assertions set in authorisation decision response "
                      "from %r", self.authzServiceURI)
            
            response = webob.Response()
            response.status = httplib.FORBIDDEN
            response.body = ('An error occurred retrieving an access decision '
                             'for %r for user %r' % (requestURI, remote_user))
            response.content_type = 'text/plain'
            log.info(response.body)
            return response(environ, start_response)     
               
        # Cache assertion if flag is set and it's one that's been freshly 
        # obtained from an authorisation decision query rather than one 
        # retrieved from the cache
        if self.cacheDecisions and noCachedAssertion:
            self._cacheAssertions(request.url, [assertion])
            
        # If got through to here then all is well, call next WSGI middleware/app
        return self._app(environ, start_response)
Example #9
0
def get_authz_decision(environ, url, remote_user):
    saml_trusted_ca_dir = environ.get('saml_trusted_ca_dir', '')
    authz_service_uri = environ.get('authz_service_uri', '')

    client_binding = AuthzDecisionQuerySslSOAPBinding()
    client_binding.sslCACertDir = saml_trusted_ca_dir
    client_binding.clockSkewTolerance = 1  # 1 second tolerance

    # Make a new query object
    query = AuthzDecisionQueryFactory.create()

    # Copy constant settings. These constants were set at
    # initialisation
    query.subject = Subject()
    query.subject.nameID = NameID()
    query.subject.nameID.format = 'urn:esg:openid'

    query.issuer = Issuer()
    query.issuer.format = Issuer.X509_SUBJECT
    query.issuer.value = 'O=NDG, OU=Security, CN=localhost'

    # Set dynamic settings particular to this individual request
    query.subject.nameID.value = remote_user
    query.resource = url

    try:
        saml_authz_response = client_binding.send(query, uri=authz_service_uri)

    except (UrlLib2SOAPClientError, URLError) as e:
        import traceback

        if isinstance(e, UrlLib2SOAPClientError):
            logger.error(
                "Error, HTTP %s response from authorisation "
                "service %r requesting access to %r: %s",
                e.urllib2Response.code, authz_service_uri, url,
                traceback.format_exc())
        else:
            logger.error(
                "Error, calling authorisation service %r "
                "requesting access to %r: %s", authz_service_uri, url,
                traceback.format_exc())

    assertions = saml_authz_response.assertions

    # Set HTTP 403 Forbidden response if any of the decisions returned are
    # deny or indeterminate status
    fail_decisions = (
        DecisionType.DENY,  #@UndefinedVariable
        DecisionType.INDETERMINATE)  #@UndefinedVariable

    # Review decision statement(s) in assertions and enforce the decision
    assertion = None
    for assertion in assertions:
        for authz_decision_statement in assertion.authzDecisionStatements:
            assertion = authz_decision_statement.decision.value
            if assertion in fail_decisions:
                break

    if assertion is None:
        logger.error("No assertions set in authorisation decision response "
                     "from {0}".format(authz_service_uri))

    return assertion