Esempio n. 1
0
    def build_query(query_class, user_identifier=None):
        """ Builds a SAML query. """

        query = query_class()

        query.version = SAMLVersion(SAMLVersion.VERSION_20)
        query.id = str(uuid4())
        query.issueInstant = datetime.utcnow()

        query.issuer = Issuer()
        query.issuer.format = Issuer.X509_SUBJECT
        query.issuer.value = ISSUER

        query.subject = Subject()
        query.subject.nameID = NameID()
        query.subject.nameID.format = NAMEID_FORMAT

        action = Action()
        action.value = Action.READ_ACTION
        query.actions.append(action)

        if user_identifier:
            query.subject.nameID.value = user_identifier

        return query
Esempio n. 2
0
    def _createAuthzDecisionQuery(
            self,
            issuer="/O=Site A/CN=PEP",
            subject="https://openid.localhost/philip.kershaw",
            resource=None,
            action=Action.HTTP_GET_ACTION,
            actionNs=Action.GHPP_NS_URI):
        query = AuthzDecisionQuery()
        query.version = SAMLVersion(SAMLVersion.VERSION_20)
        query.id = str(uuid4())
        query.issueInstant = datetime.utcnow()

        query.issuer = Issuer()
        query.issuer.format = Issuer.X509_SUBJECT
        query.issuer.value = issuer

        query.subject = Subject()
        query.subject.nameID = NameID()
        query.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        query.subject.nameID.value = subject

        if resource is None:
            query.resource = self.__class__.RESOURCE_URI
        else:
            query.resource = resource

        query.actions.append(Action())
        query.actions[0].namespace = actionNs
        query.actions[0].value = action

        return query
Esempio n. 3
0
def attribute_query(environ,
                    attribute_requests,
                    subject_id,
                    subject_id_format='urn:esg:openid'):
    """
    Make a query for a set of attributes
    Returns a dictionary of values for each attribute in the query
    
    @param attribute_requests: SAML Attribute instances
    @param subject_id: The ID of the subject, e.g. openid
    @param subject_id_format: optional format of the ID, default is openid
    """

    # Grab info from the environ
    saml_trusted_ca_dir = environ.get('saml_trusted_ca_dir', '')
    attr_service_uri = environ.get('attr_service_uri', '')

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

    # Make a new query object
    query = AttributeQuery()

    query.version = SAMLVersion(SAMLVersion.VERSION_20)
    query.id = str(uuid4())
    query.issueInstant = datetime.utcnow()

    query.issuer = Issuer()
    query.issuer.format = Issuer.X509_SUBJECT
    query.issuer.value = '/O=STFC/OU=SPBU/CN=test'

    query.subject = Subject()
    query.subject.nameID = NameID()
    query.subject.nameID.format = subject_id_format
    query.subject.nameID.value = subject_id

    # Specify what attributes to query for
    for attribute_request in attribute_requests:
        query.attributes.append(attribute_request)

    try:
        response = client_binding.send(query, uri=attr_service_uri)
        attribute_results = {}

        assertion = next(iter(response.assertions or []), None)
        if assertion:
            statement = next(iter(assertion.attributeStatements or []), None)
            if statement:
                for attribute in statement.attributes:
                    values = []
                    for attribute_value in attribute.attributeValues:
                        values.append(attribute_value.value)

                    attribute_results[attribute.name] = values

        return attribute_results

    except (RequestResponseError, Error) as e:
        logger.error("Error processing SOAP query for {0}: {1}".format(id, e))
    def _createAuthzDecisionQuery(self, issuer):
        """Constructs an XACMLAuthzDecisionQuery.
        """
        query = XACMLAuthzDecisionQuery()
        query.version = SAMLVersion(SAMLVersion.VERSION_20)
        query.id = str(uuid4())
        query.issueInstant = datetime.utcnow()

        query.issuer = Issuer()
        query.issuer.format = Issuer.X509_SUBJECT
        query.issuer.value = issuer

        return query
Esempio n. 5
0
        def authzDecisionQuery(query, response):
            now = datetime.utcnow()
            response.issueInstant = now

            # Make up a request ID that this response is responding to
            response.inResponseTo = query.id
            response.id = str(uuid4())
            response.version = SAMLVersion(SAMLVersion.VERSION_20)

            response.status = Status()
            response.status.statusCode = StatusCode()
            response.status.statusCode.value = StatusCode.SUCCESS_URI
            response.status.statusMessage = StatusMessage()
            response.status.statusMessage.value = \
                                                "Response created successfully"

            assertion = Assertion()
            assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
            assertion.id = str(uuid4())
            assertion.issueInstant = now

            authzDecisionStatement = AuthzDecisionStatement()
            authzDecisionStatement.decision = DecisionType.PERMIT
            authzDecisionStatement.resource = \
                TestAuthorisationServiceMiddleware.RESOURCE_URI
            authzDecisionStatement.actions.append(Action())
            authzDecisionStatement.actions[-1].namespace = Action.GHPP_NS_URI
            authzDecisionStatement.actions[-1].value = Action.HTTP_GET_ACTION
            assertion.authzDecisionStatements.append(authzDecisionStatement)

            # Add a conditions statement for a validity of 8 hours
            assertion.conditions = Conditions()
            assertion.conditions.notBefore = now
            assertion.conditions.notOnOrAfter = now + timedelta(seconds=60 *
                                                                60 * 8)

            assertion.subject = Subject()
            assertion.subject.nameID = NameID()
            assertion.subject.nameID.format = query.subject.nameID.format
            assertion.subject.nameID.value = query.subject.nameID.value

            assertion.issuer = Issuer()
            assertion.issuer.format = Issuer.X509_SUBJECT
            assertion.issuer.value = \
                                    TestAuthorisationServiceMiddleware.ISSUER_DN

            response.assertions.append(assertion)
            return response
Esempio n. 6
0
    def _createAttributeQuery(
            self,
            issuer="/O=Site A/CN=Authorisation Service",
            subject="https://openid.localhost/philip.kershaw"):
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()

        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = issuer

        attributeQuery.subject = Subject()
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = subject

        # special case handling for 'FirstName' attribute
        fnAttribute = Attribute()
        fnAttribute.name = ESGFSamlNamespaces.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)

        # special case handling for 'LastName' attribute
        lnAttribute = Attribute()
        lnAttribute.name = ESGFSamlNamespaces.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)

        # special case handling for 'LastName' attribute
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        emailAddressAttribute.friendlyName = "emailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)

        return attributeQuery
        def authzDecisionQuery(query, response):
            """Authorisation Decision Query interface called by the next 
            middleware in the stack the SAML SOAP Query interface middleware 
            instance
            (ndg.saml.saml2.binding.soap.server.wsgi.queryinterface.SOAPQueryInterfaceMiddleware)
            """
            now = datetime.utcnow()
            response.issueInstant = now

            # Make up a request ID that this response is responding to
            response.inResponseTo = query.id
            response.id = str(uuid4())
            response.version = SAMLVersion(SAMLVersion.VERSION_20)

            response.status = Status()
            response.status.statusCode = StatusCode()
            response.status.statusCode.value = StatusCode.SUCCESS_URI
            response.status.statusMessage = StatusMessage()
            response.status.statusMessage.value = \
                                                "Response created successfully"

            assertion = Assertion()
            assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
            assertion.id = str(uuid4())
            assertion.issueInstant = now

            authzDecisionStatement = AuthzDecisionStatement()

            # Make some simple logic to simulate a full access policy
            if query.resource == self.__class__.RESOURCE_URI:
                if query.actions[0].value == Action.HTTP_GET_ACTION:
                    authzDecisionStatement.decision = DecisionType.PERMIT
                else:
                    authzDecisionStatement.decision = DecisionType.DENY
            else:
                authzDecisionStatement.decision = DecisionType.INDETERMINATE

            authzDecisionStatement.resource = query.resource

            authzDecisionStatement.actions.append(Action())
            authzDecisionStatement.actions[-1].namespace = Action.GHPP_NS_URI
            authzDecisionStatement.actions[-1].value = Action.HTTP_GET_ACTION
            assertion.authzDecisionStatements.append(authzDecisionStatement)

            # Add a conditions statement for a validity of 8 hours
            assertion.conditions = Conditions()
            assertion.conditions.notBefore = now
            assertion.conditions.notOnOrAfter = now + timedelta(seconds=60 *
                                                                60 * 8)

            assertion.subject = Subject()
            assertion.subject.nameID = NameID()
            assertion.subject.nameID.format = query.subject.nameID.format
            assertion.subject.nameID.value = query.subject.nameID.value

            assertion.issuer = Issuer()
            assertion.issuer.format = Issuer.X509_SUBJECT
            assertion.issuer.value = \
                                    TestAuthorisationServiceMiddleware.ISSUER_DN

            response.assertions.append(assertion)
            return response
    def test05SamlAttributeQuery(self):
        if self.skipTests:
            return

        # Prepare a client query
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()

        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = '/O=ESG/OU=NCAR/CN=Gateway'

        attributeQuery.subject = Subject()
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = ESGFSamlNamespaces.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = TestUserDatabase.OPENID_URI

        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = "InvalidFormat"
        emailAddressAttribute.friendlyName = "EmailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)

        authzAttribute = Attribute()
        authzAttribute.name = TestUserDatabase.ATTRIBUTE_NAMES[0]
        authzAttribute.nameFormat = XSStringAttributeValue.DEFAULT_FORMAT
        authzAttribute.friendlyName = "authz"

        attributeQuery.attributes.append(authzAttribute)

        # Add the response - the interface will populate with an assertion as
        # appropriate
        samlResponse = Response()

        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()

        # Initialise to success status but reset on error
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusMessage = StatusMessage()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI

        # Nb. SAML 2.0 spec says issuer format must be omitted
        samlResponse.issuer.value = "CEDA"

        samlResponse.inResponseTo = attributeQuery.id

        # Set up the interface object

        # Define queries for SAML attribute names
        samlAttribute2SqlQuery = {
            ESGFSamlNamespaces.FIRSTNAME_ATTRNAME:
            self.__class__.SAML_FIRSTNAME_SQLQUERY,
            ESGFSamlNamespaces.LASTNAME_ATTRNAME:
            self.__class__.SAML_LASTNAME_SQLQUERY,
            ESGFSamlNamespaces.EMAILADDRESS_ATTRNAME:
            self.__class__.SAML_EMAILADDRESS_SQLQUERY,
            TestUserDatabase.ATTRIBUTE_NAMES[0]:
            self.__class__.SAML_ATTRIBUTES_SQLQUERY
        }

        attributeInterface = SQLAlchemyAttributeInterface(
            samlAttribute2SqlQuery=samlAttribute2SqlQuery)

        attributeInterface.connectionString = TestUserDatabase.DB_CONNECTION_STR

        attributeInterface.samlValidRequestorDNs = (
            '/O=STFC/OU=CEDA/CN=AuthorisationService',
            '/O=ESG/OU=NCAR/CN=Gateway')

        attributeInterface.setProperties(samlAssertionLifetime=28800.)

        attributeInterface.samlSubjectSqlQuery = (
            SQLAlchemyAttributeInterfaceTestCase.SAML_SUBJECT_SQLQUERY)

        # Make the query
        attributeInterface.getAttributes(attributeQuery, samlResponse)
    def getAttributes(self, attributeQuery, response):
        '''Test Attribute Authority SAML Attribute Query interface'''

        userId = attributeQuery.subject.nameID.value
        requestedAttributeNames = [
            attribute.name for attribute in attributeQuery.attributes
        ]
        if attributeQuery.issuer.format != Issuer.X509_SUBJECT:
            raise InvalidRequestorId(
                'Requestor issuer format "%s" is invalid' %
                attributeQuery.issuerFormat.value)

        requestorId = X500DN.fromString(attributeQuery.issuer.value)

        if userId not in TestUserRoles.VALID_USER_IDS:
            raise UserIdNotKnown('Subject Id "%s" is not known to this '
                                 'authority' % userId)

        if requestorId not in TestUserRoles.VALID_REQUESTOR_IDS:
            raise InvalidRequestorId('Requestor identity "%s" is invalid' %
                                     requestorId)

        unknownAttrNames = [
            attrName for attrName in requestedAttributeNames
            if attrName not in TestUserRoles.SAML_ATTRIBUTE_NAMES
        ]

        if len(unknownAttrNames) > 0:
            raise AttributeNotKnownError("Unknown attributes requested: %r" %
                                         unknownAttrNames)

        if requestorId == TestUserRoles.INSUFFICIENT_PRIVILEGES_REQUESTOR_ID:
            raise AttributeReleaseDenied("Attribute release denied for the "
                                         'requestor "%s"' % requestorId)

        # Create a new assertion to hold the attributes to be returned
        assertion = Assertion()

        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = response.issueInstant

        assertion.issuer = Issuer()
        assertion.issuer.value = response.issuer.value
        assertion.issuer.format = Issuer.X509_SUBJECT

        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = assertion.conditions.notBefore + \
            timedelta(seconds=TestUserRoles.SAML_ASSERTION_LIFETIME)

        assertion.subject = Subject()
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        attributeStatement = AttributeStatement()

        # Add test set of attributes
        for name in requestedAttributeNames:
            attributeFound = False
            for attribute in TestUserRoles.SAML_ATTRIBUTES:
                if attribute.name == name:
                    attributeFound = True
                    break

            if attributeFound:
                attributeStatement.attributes.append(attribute)
            else:
                raise AttributeNotKnownError(
                    "Unknown attribute requested: %s" % name)

        assertion.attributeStatements.append(attributeStatement)
        response.assertions.append(assertion)
Esempio n. 10
0
    def getAttributes(self, attributeQuery, response):
        """Attribute Authority SAML AttributeQuery

        @type attributeQuery: saml.saml2.core.AttributeQuery
        @param userId: query containing requested attributes
        @type response: saml.saml2.core.Response
        @param response: add an assertion with the list of attributes
        for the given subject ID in the query or set an error Status code and
        message
        @raise AttributeInterfaceError: an error occured requesting
        attributes
        @raise AttributeReleaseDeniedError: Requestor was denied release of the
        requested attributes
        @raise AttributeNotKnownError: Requested attribute names are not known
        to this authority
        """
        userId = attributeQuery.subject.nameID.value
        requestedAttributeNames = [
            attribute.name for attribute in attributeQuery.attributes
        ]

        requestorDN = X500DN.from_string(attributeQuery.issuer.value)

        if not self._queryDbForSamlSubject(userId):
            raise UserIdNotKnown('Subject Id "%s" is not known to this '
                                 'authority' % userId)

        if (len(self.samlValidRequestorDNs) > 0
                and requestorDN not in self.samlValidRequestorDNs):
            raise InvalidRequestorId('Requester identity "%s" is invalid' %
                                     requestorDN)

        unknownAttrNames = [
            attrName for attrName in requestedAttributeNames
            if attrName not in self.__samlAttribute2SqlQuery
        ]

        if len(unknownAttrNames) > 0:
            raise AttributeNotKnownError("Unknown attributes requested: %r" %
                                         unknownAttrNames)

        # Create a new assertion to hold the attributes to be returned
        assertion = Assertion()

        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = response.issueInstant

        # Assumes SAML response issuer details as set by -
        # ndg.security.server.wsgi.saml.SOAPQueryInterfaceMiddleware
        assertion.issuer = Issuer()
        assertion.issuer.value = response.issuer.value

        if response.issuer.format:
            assertion.issuer.format = response.issuer.format

        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = (assertion.conditions.notBefore +
                                             self.samlAssertionLifetime)

        assertion.subject = Subject()
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        attributeStatement = AttributeStatement()

        # Query the database for the requested attributes and return them
        # mapped to their attribute names as specified by the attributeNames
        # property
        for requestedAttribute in attributeQuery.attributes:
            attributeVals = self._queryDbForSamlAttributes(
                requestedAttribute.name, userId)

            # Make a new SAML attribute object to hold the values obtained
            attribute = Attribute()
            attribute.name = requestedAttribute.name
            attribute.nameFormat = requestedAttribute.nameFormat

            if requestedAttribute.friendlyName is not None:
                attribute.friendlyName = requestedAttribute.friendlyName

            # Call specific conversion utility to convert the retrieved field
            # to the correct SAML attribute value type
            try:
                field2SamlAttributeVal = self.__samlAttribute2SqlQuery[
                    requestedAttribute.name][-1]
            except (IndexError, TypeError) as e:
                raise AttributeInterfaceConfigError(
                    'Bad format for SAML '
                    'attribute to SQL query '
                    'look-up for attribute '
                    'name %r: %s' % (requestedAttribute.name, e))

            for val in attributeVals:
                attributeValue = field2SamlAttributeVal(val)
                attribute.attributeValues.append(attributeValue)

            attributeStatement.attributes.append(attribute)

        assertion.attributeStatements.append(attributeStatement)
        response.assertions.append(assertion)
Esempio n. 11
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