def test_missing_mandatory_attribute(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(
         sample_requests.missing_issue_instant_attr)
     self.assertEqual(len(errors), 1)
     self.assertIn("The attribute 'IssueInstant' is required but missing.",
                   errors[0].message)
 def test_invalid_attribute_format(self):
     # See: https://github.com/italia/spid-testenv2/issues/63
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(sample_requests.invalid_id_attr)
     self.assertEqual(len(errors), 1)
     self.assertIn("is not a valid value of the atomic type 'xs:ID'",
                   errors[0].message)
 def test_multiple_errors(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(sample_requests.multiple_errors)
     self.assertEqual(len(errors), 2)
     self.assertIn("is not a valid value of the atomic type 'xs:ID'",
                   errors[0].message)
     self.assertIn("The attribute 'Version' is required but missing.",
                   errors[1].message)
 def test_unexpected_element(self):
     # See: https://github.com/italia/spid-testenv2/issues/79
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(sample_requests.unexpected_element)
     self.assertEqual(len(errors), 1)
     self.assertIn(
         "Element '{urn:oasis:names:tc:SAML:2.0:assertion}AuthnContextClassRef': "
         "This element is not expected. Expected is one of ( {urn:oasis:names:tc:SAML:2.0:assertion}"
         "Conditions, {urn:oasis:names:tc:SAML:2.0:protocol}RequestedAuthnContext, {urn:oasis:names:tc:SAML:2.0:protocol}Scoping ).",
         errors[0].message)
 def test_invalid_comparison_attribute(self):
     # https://github.com/italia/spid-testenv2/issues/97
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(
         sample_requests.invalid_comparison_attr)
     self.assertEqual(len(errors), 2)
     self.assertIn(
         "The value 'invalid' is not an element of the set "
         "{'exact', 'minimum', 'maximum', 'better'}", errors[0].message)
     self.assertIn("'invalid' is not a valid value of the atomic type",
                   errors[1].message)
Ejemplo n.º 6
0
 def __init__(self, *args, **kwargs):
     from testenv.parser import XMLValidator
     self.xml_validator = XMLValidator()
     self.schema = None
Ejemplo n.º 7
0
class SpidParser(object):
    """
    Parser for spid messages
    """
    def __init__(self, *args, **kwargs):
        from testenv.parser import XMLValidator
        self.xml_validator = XMLValidator()
        self.schema = None

    def get_schema(self, action, binding, **kwargs):
        """
        :param binding:
        """
        _schema = None
        receivers = kwargs.get('receivers')
        issuer = kwargs.get('issuer')
        if action == 'login':
            required_signature = False
            if binding == BINDING_HTTP_POST:
                required_signature = True
            elif binding == BINDING_HTTP_REDIRECT:
                required_signature = False
            attribute_consuming_service_indexes = kwargs.get(
                'attribute_consuming_service_indexes')
            assertion_consumer_service_indexes = kwargs.get(
                'assertion_consumer_service_indexes')
            _schema = Elem(
                name='auth_request',
                tag='samlp:AuthnRequest',
                attributes=[
                    Attr('id'),
                    Attr('version', default='2.0'),
                    TimestampAttr('issue_instant',
                                  func=check_utc_date,
                                  val_converter=str_to_time),
                    Attr('destination', default=receivers),
                    Attr('force_authn', required=False),
                    Attr('attribute_consuming_service_index',
                         default=attribute_consuming_service_indexes,
                         required=False),
                    Or(
                        Attr('assertion_consumer_service_index',
                             default=assertion_consumer_service_indexes,
                             required=False),
                        And(
                            Attr('assertion_consumer_service_url',
                                 required=False),
                            Attr('protocol_binding',
                                 default=BINDING_HTTP_POST,
                                 required=False)))
                ],
                children=[
                    Elem('subject',
                         tag='saml:Subject',
                         required=False,
                         attributes=[
                             Attr('format', default=NAMEID_FORMAT_ENTITY),
                             Attr('name_qualifier')
                         ]),
                    Elem(
                        'issuer',
                        tag='saml:Issuer',
                        attributes=[
                            Attr('format', default=NAMEID_FORMAT_ENTITY),
                            Attr('name_qualifier', default=issuer),
                            Attr('text', default=issuer)
                        ],
                    ),
                    Elem('name_id_policy',
                         tag='samlp:NameIDPolicy',
                         attributes=[
                             Attr('allow_create', absent=True, required=False),
                             Attr('format', default=NAMEID_FORMAT_TRANSIENT)
                         ]),
                    Elem('conditions',
                         tag='saml:Conditions',
                         required=False,
                         attributes=[
                             Attr('not_before', func=check_utc_date),
                             Attr('not_on_or_after', func=check_utc_date)
                         ]),
                    Elem('requested_authn_context',
                         tag='saml:RequestedAuthnContext',
                         attributes=[
                             Attr('comparison', default=COMPARISONS),
                         ],
                         children=[
                             Elem('authn_context_class_ref',
                                  tag='saml:AuthnContextClassRef',
                                  attributes=[
                                      Attr('text', default=SPID_LEVELS)
                                  ])
                         ]),
                    Elem(
                        'signature',
                        tag='ds:Signature',
                        required=required_signature,
                    ),
                    Elem('scoping',
                         tag='saml2p:Scoping',
                         required=False,
                         attributes=[Attr('proxy_count', default=[0])]),
                ])
        elif action == 'logout':
            _schema = Elem(name='logout_request',
                           tag='samlp:LogoutRequest',
                           attributes=[
                               Attr('id'),
                               Attr('version', default='2.0'),
                               Attr('issue_instant', func=check_utc_date),
                               Attr('destination', default=receivers),
                           ],
                           children=[
                               Elem(
                                   'issuer',
                                   tag='saml:Issuer',
                                   attributes=[
                                       Attr('format',
                                            default=NAMEID_FORMAT_ENTITY),
                                       Attr('name_qualifier', default=issuer),
                                       Attr('text', default=issuer)
                                   ],
                               ),
                               Elem('name_id',
                                    tag='saml:NameID',
                                    attributes=[
                                        Attr('name_qualifier'),
                                        Attr('format',
                                             default=NAMEID_FORMAT_TRANSIENT)
                                    ]),
                               Elem(
                                   'session_index',
                                   tag='samlp:SessionIndex',
                               ),
                           ])
        return _schema

    def parse(self, obj, action, binding, schema=None, **kwargs):
        """
        :param obj: pysaml2 object
        :param binding:
        :param schema: custom schema (None by default)
        """

        errors = {}
        # Validate xml against its XSD schema
        validation_errors = self.xml_validator.validate_request(obj.xmlstr)
        if validation_errors:
            errors['validation_errors'] = validation_errors
        # Validate xml against SPID rules
        _schema = self.get_schema(action, binding, **kwargs)\
            if schema is None else schema
        self.observer = Observer()
        self.observer.attach(_schema)
        validated = _schema.validate(obj.message)
        spid_errors = self.observer.evaluate()
        if spid_errors:
            errors['spid_errors'] = spid_errors
        return validated, errors
 def test_duplicate_attribute(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request(
         sample_requests.duplicate_version_attr)
     self.assertEqual(len(errors), 1)
     self.assertIn('Attribute Version redefined', errors[0].message)
 def test_invalid_xml(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request('<a></b>')
     self.assertEqual(len(errors), 1)
     self.assertIn('Opening and ending tag mismatch: a line 1 and b',
                   errors[0].message)
 def test_not_xml(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request('{"this": "is JSON"}')
     self.assertEqual(len(errors), 1)
     self.assertIn("Start tag expected, '<' not found", errors[0].message)
 def test_empty_request(self):
     validator = XMLValidator(translator=FakeTranslator())
     errors = validator.validate_request('')
     self.assertEqual(len(errors), 1)
     self.assertIn('Document is empty', errors[0].message)
 def test_valid_requests(self):
     validator = XMLValidator(translator=FakeTranslator())
     for request in sample_requests.valid:
         errors = validator.validate_request(request)
         self.assertEqual(errors, [])