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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant