Skip to content

Conversation

@BotoX
Copy link

@BotoX BotoX commented Oct 9, 2025

Description

Slight change in _filter_values to return an empty list instead of None

The feature or problem addressed by this PR

I saw the following error a couple of times on our SATOSA IdP which is taking part in the eduGAIN federation:

Traceback (most recent call last):
  File "/opt/SATOSA/src/satosa/base.py", line 269, in run
    resp = self._run_bound_endpoint(context, spec)
  File "/opt/SATOSA/src/satosa/base.py", line 193, in _run_bound_endpoint
    return spec(context)
  File "/opt/SATOSA/src/satosa/frontends/saml2.py", line 106, in handle_authn_request
    return self._handle_authn_request(context, binding_in, self.idp)
  File "/opt/SATOSA/src/satosa/frontends/saml2.py", line 281, in _handle_authn_request
    internal_req.attributes = self._get_approved_attributes(
  File "/opt/SATOSA/src/satosa/frontends/saml2.py", line 316, in _get_approved_attributes
    attribute_filter = list(idp_policy.restrict(all_attributes, sp_entity_id).keys())
  File "/opt/venvs/satosa/lib/python3.10/site-packages/saml2/assertion.py", line 565, in restrict
    return self.filter(
  File "/opt/venvs/satosa/lib/python3.10/site-packages/saml2/assertion.py", line 527, in filter
    subject_ava = filter_on_attributes(
  File "/opt/venvs/satosa/lib/python3.10/site-packages/saml2/assertion.py", line 118, in filter_on_attributes
    _apply_attr_value_restrictions(attr, res, True)
  File "/opt/venvs/satosa/lib/python3.10/site-packages/saml2/assertion.py", line 102, in _apply_attr_value_restrictions
    res[_fn].extend(_filter_values(ava[_fn], values))
TypeError: 'NoneType' object is not iterable
What your changes do and why you chose this solution

I grabbed the SAML request from the webserver log and a SATOSA in debug mode with Werkzeug to look at the state:

>>> print(attr)
{'__class__': 'urn:oasis:names:tc:SAML:2.0:metadata&RequestedAttribute', 'name': 'urn:mace:dir:attribute-def:eduPersonScopedAffiliation', 'name_format': 'urn:mace:shibboleth:1.0:attributeNamespace:uri', 'friendly_name': 'eduPersonScopedAffiliation', 'is_required': 'true'}
>>> print(res)
{'eduPersonScopedAffiliation': []}
>>> print(ava[_fn])
None
>>> print(ava)
{'orgAffiliation': None, 'transactionIdentifier': None, 'authContextParams': None, 'prid': None, 'pridPersistence': None, 'personalIdentityNumberBinding': None, 'eidasPersonIdentifier': None, 'birthName': None, 'eidasNaturalPersonAddress': None, 'userCertificate': None, 'userSignature': None, 'sad': None, 'authServerSignature': None, 'signMessageDigest': None, 'LegalPersonIdentifier': None, 'LegalAddress': None, 'LegalName': None, 'VATRegistration': None, 'TaxReference': None, 'BusinessCodes': None, 'LEI': None, 'EORI': None, 'SEED': None, 'SIC': None, 'D-2012-17-EUIdentifier': None, 'PersonIdentifier': None, 'FamilyName': None, 'FirstName': None, 'DateOfBirth': None, 'BirthName': None, 'PlaceOfBirth': None, 'CurrentAddress': None, 'Gender': None, 'eduCourseOffering': None, 'eduCourseMember': None, 'isMemberOf': None, 'eduPersonAffiliation': None, 'eduPersonNickname': None, 'eduPersonOrgDN': None, 'eduPersonOrgUnitDN': None, 'eduPersonPrimaryAffiliation': None, 'eduPersonPrincipalName': None, 'eduPersonEntitlement': None, 'eduPersonPrimaryOrgUnitDN': None, 'eduPersonScopedAffiliation': None, 'eduPersonTargetedID': None, 'eduPersonAssurance': None, 'eduPersonPrincipalNamePrior': None, 'eduPersonUniqueId': None, 'eduPersonOrcid': None, 'employeeHsaId': None, 'personalIdentityNumber': None, 'PVP-GID': None, 'PVP-BPK': None, 'PVP-OU-OKZ': None, 'PVP-VERSION': None, 'PVP-PRINCIPAL-NAME': None, 'PVP-PARTICIPANT-OKZ': None, 'PVP-ROLES': None, 'PVP-INVOICE-RECPT-ID': None, 'PVP-COST-CENTER-ID': None, 'PVP-CHARGE-CODE': None, 'PVP-OU-GV-OU-ID': None, 'PVP-FUNCTION': None, 'PVP-BIRTHDATE': None, 'PVP-PARTICIPANT-ID': None, 'uid': None, 'mail': None, 'ou': None, 'telephoneNumber': None, 'givenName': None, 'carLicense': None, 'departmentNumber': None, 'employeeNumber': None, 'employeeType': None, 'mailLocalAddress': None, 'preferredLanguage': None, 'userSMIMECertificate': None, 'userPKCS12': None, 'displayName': None, 'norEduOrgUniqueNumber': None, 'norEduOrgUnitUniqueNumber': None, 'norEduPersonBirthDate': None, 'norEduPersonLIN': None, 'norEduPersonNIN': None, 'norEduOrgAcronym': None, 'norEduOrgUniqueIdentifier': None, 'norEduOrgUnitUniqueIdentifier': None, 'federationFeideSchemaVersion': None, 'norEduPersonLegalName': None, 'norEduOrgSchemaVersion': None, 'norEduOrgNIN': None, 'osiHomeUrl': None, 'osiPreferredTZ': None, 'osiICardTimeLastUpdated': None, 'osiMiddleName': None, 'osiOtherEmail': None, 'osiOtherHomePhone': None, 'osiWorkURL': None, 'email': None, 'dateOfBirth': None, 'placeOfBirth': None, 'gender': None, 'countryOfCitizenship': None, 'countryOfResidence': None, 'subject-id': None, 'pairwise-id': None, 'schacMotherTongue': None, 'schacGender': None, 'schacDateOfBirth': None, 'schacPlaceOfBirth': None, 'schacCountryOfCitizenship': None, 'schacSn1': None, 'schacSn2': None, 'schacPersonalTitle': None, 'schacHomeOrganization': None, 'schacHomeOrganizationType': None, 'schacCountryOfResidence': None, 'schacUserPresenceID': None, 'schacPersonalPosition': None, 'schacPersonalUniqueCode': None, 'schacPersonalUniqueID': None, 'schacExpiryDate': None, 'schacUserPrivateAttribute': None, 'schacUserStatus': None, 'schacProjectMembership': None, 'schacProjectSpecificRole': None, 'sisLegalGuardianFor': None, 'sisSchoolGrade': None, 'dc': None, 'associatedDomain': None, 'co': None, 'jpegPhoto': None, 'EAAHash': None, 'EAAKey': None, 'labeledURI': None, 'knowledgeInformation': None, 'cn': None, 'sn': None, 'serialNumber': None, 'c': None, 'l': None, 'st': None, 'street': None, 'o': None, 'title': None, 'searchGuide': None, 'businessCategory': None, 'postalAddress': None, 'postalCode': None, 'postOfficeBox': None, 'physicalDeliveryOfficeName': None, 'telexNumber': None, 'teletexTerminalIdentifier': None, 'facsimileTelephoneNumber': None, 'x121Address': None, 'internationaliSDNNumber': None, 'registeredAddress': None, 'destinationIndicator': None, 'preferredDeliveryMethod': None, 'presentationAddress': None, 'supportedApplicationContext': None, 'member': None, 'owner': None, 'roleOccupant': None, 'cACertificate': None, 'authorityRevocationList': None, 'certificateRevocationList': None, 'crossCertificatePair': None, 'initials': None, 'generationQualifier': None, 'x500UniqueIdentifier': None, 'dnQualifier': None, 'enhancedSearchGuide': None, 'protocolInformation': None, 'uniqueMember': None, 'houseIdentifier': None, 'supportedAlgorithms': None, 'deltaRevocationList': None, 'dmdName': None, 'pseudonym': None, 'swissEduPersonUniqueID': None, 'swissEduPersonDateOfBirth': None, 'swissEduPersonGender': None, 'swissEduPersonHomeOrganization': None, 'swissEduPersonHomeOrganizationType': None, 'swissEduPersonStudyBranch1': None, 'swissEduPersonStudyBranch2': None, 'swissEduPersonStudyBranch3': None, 'swissEduPersonStudyLevel': None, 'swissEduPersonStaffCategory': None, 'swissEduPersonMatriculationNumber': None, 'swissEduPersonCardUID': None, 'swissEduID': None, 'swissLibraryPersonAffiliation': None, 'swissLibraryPersonResidence': None, 'voPersonApplicationUID': None, 'voPersonAuthorName': None, 'voPersonCertificateDN': None, 'voPersonCertificateIssuerDN': None, 'voPersonExternalID': None, 'voPersonID': None, 'voPersonPolicyAgreement': None, 'voPersonSoRID': None, 'voPersonStatus': None, 'voPersonAffiliation': None, 'voPersonExternalAffiliation': None, 'voPersonScopedAffiliation': None, 'voPersonApplicationPassword': None, 'voPersonVerifiedEmail': None, 'voPersonToken': None, 'SAPemployeeNumber': None, 'PMidentNr': None, 'obfuscatedID': None}
>>> print(_fn)
eduPersonScopedAffiliation
>>> print(values)
[]
>>> print(_filter_values(ava[_fn], values))
None

Returning None throws a TypeError: 'NoneType' object is not iterable here:
https://github.com/IdentityPython/pysaml2/blob/master/src/saml2/assertion.py#L98-L108

Returning an empty list does not throw an error and looks correct (empty list in, empty list out).
It also appears to allow access to the service: https://services.sheerid.com/Shibboleth/UK

Here's a part of the metadata:

      <md:AttributeConsumingService index="1">
        <md:ServiceName xml:lang="en">SheerID Verification Services</md:ServiceName>
        <md:ServiceDescription xml:lang="en">Student and Teacher Eligibility Verification Services for Global Brands</md:ServiceDescription>
        <md:RequestedAttribute FriendlyName="eduPersonScopedAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/>
        <md:RequestedAttribute FriendlyName="eduPersonScopedAffiliation" Name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" NameFormat="urn:mace:shibboleth:1.0:attributeNamespace:uri" isRequired="true"/>
        <md:RequestedAttribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"/>
        <md:RequestedAttribute FriendlyName="eduPersonTargetedID" Name="urn:mace:dir:attribute-def:eduPersonTargetedID" NameFormat="urn:mace:shibboleth:1.0:attributeNamespace:uri"/>
      </md:AttributeConsumingService>

Checklist

  • Checked that no other issues or pull requests exist for the same issue/change
  • Added tests covering the new functionality -> (I ran the existing tests and none failed)
  • Updated documentation OR the change is too minor to be documented
  • Updated CHANGELOG.md OR changes are insignificant
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant