Repository: zulip/zulip

Issue:
SAML: First call of /complete/saml/ fails, settings missing?
With the new Version 3.0 the first call of /complete/saml/ (callback from IDP) fails with the following error. It seems that OneLogin_Saml2_Response is called with a empty Settings-Object. But when i call "Login" again everything is working fine.

**ERROR**
2020-07-24 08:35:03.823 ERR  [django.request] Internal Server Error: /complete/saml/
Traceback (most recent call last):
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/social_django/utils.py", line 49, in wrapper
    return func(request, backend, *args, **kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/social_django/views.py", line 33, in complete
    *args, **kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/social_core/actions.py", line 45, in do_complete
    user = backend.complete(user=user, *args, **kwargs)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/social_core/backends/base.py", line 40, in complete
    return self.auth_complete(*args, **kwargs)
  File "./zproject/backends.py", line 1912, in auth_complete
    idp_name = self.get_issuing_idp(SAMLResponse)
  File "./zproject/backends.py", line 1793, in get_issuing_idp
    resp = OneLogin_Saml2_Response(settings={}, response=SAMLResponse)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/onelogin/saml2/response.py", line 49, in __init__
    self.decrypted_document = self.__decrypt_assertion(decrypted_document)
  File "/srv/zulip-venv-cache/99ce7a22e72bf7ac1fd2eb4428af7f7f69b4ac9a/zulip-py3-venv/lib/python3.7/site-packages/onelogin/saml2/response.py", line 823, in __decrypt_assertion
    key = self.__settings.get_sp_key()
AttributeError: 'dict' object has no attribute 'get_sp_key'


Discussion:
Hello @zulip/server-authentication members, this issue was labeled with the "area: authentication" label, so you may want to check it out!

<!-- areaLabelAddition -->

Resolving patch:
diff --git a/zproject/backends.py b/zproject/backends.py
--- a/zproject/backends.py
+++ b/zproject/backends.py
@@ -38,6 +38,7 @@
 from lxml.etree import XMLSyntaxError
 from onelogin.saml2.errors import OneLogin_Saml2_Error
 from onelogin.saml2.response import OneLogin_Saml2_Response
+from onelogin.saml2.settings import OneLogin_Saml2_Settings
 from requests import HTTPError
 from social_core.backends.apple import AppleIdAuth
 from social_core.backends.azuread import AzureADOAuth2
@@ -1780,8 +1781,7 @@ def get_data_from_redis(cls, key: str) -> Optional[Dict[str, Any]]:
 
         return data
 
-    @classmethod
-    def get_issuing_idp(cls, SAMLResponse: str) -> Optional[str]:
+    def get_issuing_idp(self, SAMLResponse: str) -> Optional[str]:
         """
         Given a SAMLResponse, returns which of the configured IdPs is declared as the issuer.
         This value MUST NOT be trusted as the true issuer!
@@ -1792,11 +1792,12 @@ def get_issuing_idp(cls, SAMLResponse: str) -> Optional[str]:
         of the configured IdPs' information to use for parsing and validating the response.
         """
         try:
-            resp = OneLogin_Saml2_Response(settings={}, response=SAMLResponse)
+            config = self.generate_saml_config()
+            saml_settings = OneLogin_Saml2_Settings(config, sp_validation_only=True)
+            resp = OneLogin_Saml2_Response(settings=saml_settings, response=SAMLResponse)
             issuers = resp.get_issuers()
-        except cls.SAMLRESPONSE_PARSING_EXCEPTIONS:
-            logger = logging.getLogger(f"zulip.auth.{cls.name}")
-            logger.info("Error while parsing SAMLResponse:", exc_info=True)
+        except self.SAMLRESPONSE_PARSING_EXCEPTIONS:
+            self.logger.info("Error while parsing SAMLResponse:", exc_info=True)
             return None
 
         for idp_name, idp_config in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS.items():


Test patch: