diff --git a/flask_saml2/sp/idphandler.py b/flask_saml2/sp/idphandler.py index 50788ad..7c640c1 100644 --- a/flask_saml2/sp/idphandler.py +++ b/flask_saml2/sp/idphandler.py @@ -257,8 +257,9 @@ def validate_response(self, response: ResponseParser): # Validate the AudienceRestriction elements, if they exist audiences = response._xpath(response.conditions, './saml:AudienceRestriction/saml:Audience') entity_id = self.sp.get_sp_entity_id() + adnc = [el.text for el in audiences] if len(audiences) and not any(el.text == entity_id for el in audiences): - raise CannotHandleAssertion("No valid AudienceRestriction found") + raise CannotHandleAssertion(f"No valid AudienceRestriction found: {adnc}, {entity_id}") def format_datetime(self, value: datetime.datetime) -> str: """ diff --git a/flask_saml2/sp/parser.py b/flask_saml2/sp/parser.py index 2dd63a0..e40046a 100644 --- a/flask_saml2/sp/parser.py +++ b/flask_saml2/sp/parser.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional +from typing import List, Mapping, Optional, Union from flask_saml2.types import XmlNode from flask_saml2.utils import cached_property @@ -51,10 +51,20 @@ def nameid_format(self) -> str: return self._xpath(self.subject, 'saml:NameID/@Format')[0] @cached_property - def attributes(self) -> Mapping[str, str]: + def attributes(self) -> Mapping[str, Union[str, List[str]]]: attributes = self._xpath(self.assertion, 'saml:AttributeStatement/saml:Attribute') - return {el.get('Name'): self._xpath(el, 'saml:AttributeValue')[0].text - for el in attributes} + ret = {} + for el in attributes: + name = el.get('Name') + attrs = self._xpath(el, 'saml:AttributeValue') + if len(attrs) == 1: + ret[name] = attrs[0].text + else: + vals = [] + for a in attrs: + vals.append(a.text) + ret[name] = vals + return ret @cached_property def conditions(self) -> Optional[XmlNode]: diff --git a/flask_saml2/sp/views.py b/flask_saml2/sp/views.py index 1c116b6..fd1c6ac 100644 --- a/flask_saml2/sp/views.py +++ b/flask_saml2/sp/views.py @@ -81,15 +81,21 @@ def post(self): saml_request = request.form['SAMLResponse'] relay_state = request.form['RelayState'] + errors = [] + for handler in self.sp.get_idp_handlers(): try: response = handler.get_response_parser(saml_request) auth_data = handler.get_auth_data(response) return self.sp.login_successful(auth_data, relay_state) - except CannotHandleAssertion: - continue + except CannotHandleAssertion as e: + errors.append(e) except UserNotAuthorized: return self.sp.render_template('flask_saml2_sp/user_not_authorized.html') + error_string = "" + for e in errors: + error_string = f"{error_string} - {e}
" + return f"Could not log in for various reasons, here is a list of errors encountered:
{error_string}" class Metadata(SAML2View):