Update Copyright header point to COPYING file
[cascardo/ipsilon.git] / ipsilon / providers / saml2 / logout.py
index 46aea6e..c715b90 100644 (file)
@@ -1,19 +1,4 @@
-# Copyright (C) 2015  Rob Crittenden <rcritten@redhat.com>
-#
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# Copyright (C) 2015 Ipsilon project Contributors, for license see COPYING
 
 from ipsilon.providers.common import ProviderPageBase
 from ipsilon.providers.common import InvalidRequest
@@ -80,15 +65,14 @@ class LogoutRequest(ProviderPageBase):
                 self.error('loading session failed: %s' % e)
                 raise cherrypy.HTTPError(400, 'Invalid logout session')
         else:
-            self.error('Logout attempt without being loggged in.')
-            raise cherrypy.HTTPError(400, 'Not logged in')
+            return self._not_logged_in(logout, message)
 
         try:
             logout.validateRequest()
         except lasso.ProfileSessionNotFoundError, e:
             self.error('Logout failed. No sessions for %s' %
                        logout.remoteProviderId)
-            raise cherrypy.HTTPError(400, 'Not logged in')
+            return self._not_logged_in(logout, message)
         except lasso.LogoutUnsupportedProfileError:
             self.error('Logout failed. Unsupported profile %s' %
                        logout.remoteProviderId)
@@ -156,11 +140,48 @@ class LogoutRequest(ProviderPageBase):
                             (user.name, user.fullname,
                              logout.remoteProviderId))
             else:
-                self.error('Logout attempt without being loggged in.')
-                raise cherrypy.HTTPError(400, 'Not logged in')
+                return self._not_logged_in(logout, message)
 
         return
 
+    def _not_logged_in(self, logout, message):
+        """
+        The user requested a logout but isn't logged in, or we can't
+        find a session for the user. Try to be nice and redirect them
+        back to the RelayState in the logout request.
+
+        We are only nice in the case of a valid logout request. If the
+        request is invalid (not signed, unknown SP, etc) then an
+        exception is raised.
+        """
+        self.error('Logout attempt without being logged in.')
+
+        if logout.msgRelayState is not None:
+            raise cherrypy.HTTPRedirect(logout.msgRelayState)
+
+        try:
+            logout.processRequestMsg(message)
+        except (lasso.ServerProviderNotFoundError,
+                lasso.ProfileUnknownProviderError) as e:
+            msg = 'Invalid SP [%s] (%r [%r])' % (logout.remoteProviderId,
+                                                 e, message)
+            self.error(msg)
+            raise UnknownProvider(msg)
+        except (lasso.ProfileInvalidProtocolprofileError,
+                lasso.DsError), e:
+            msg = 'Invalid SAML Request: %r (%r [%r])' % (logout.request,
+                                                          e, message)
+            self.error(msg)
+            raise InvalidRequest(msg)
+        except lasso.Error, e:
+            self.error('SLO unknown error: %s' % message)
+            raise cherrypy.HTTPError(400, 'Invalid logout request')
+
+        if logout.msgRelayState:
+            raise cherrypy.HTTPRedirect(logout.msgRelayState)
+        else:
+            raise cherrypy.HTTPError(400, 'Not logged in')
+
     def logout(self, message, relaystate=None, samlresponse=None):
         """
         Handle HTTP Redirect logout. This is an asynchronous logout
@@ -186,8 +207,7 @@ class LogoutRequest(ProviderPageBase):
         saml_sessions = us.get_provider_data('saml2')
         if saml_sessions is None:
             # No sessions means nothing to log out
-            self.error('Logout attempt without being loggged in.')
-            raise cherrypy.HTTPError(400, 'Not logged in')
+            return self._not_logged_in(logout, message)
 
         self.debug('%d sessions loaded' % saml_sessions.count())
         saml_sessions.dump()
@@ -225,6 +245,10 @@ class LogoutRequest(ProviderPageBase):
                 raise cherrypy.HTTPRedirect(400, 'Failed to log out user: %s '
                                             % e)
 
+            # Now set the full list of session indexes to log out
+            req = logout.get_request()
+            req.setSessionIndexes(tuple(set(session.session_indexes)))
+
             session.set_logoutstate(logout.msgUrl, logout.request.id, None)
             us.save_provider_data('saml2', saml_sessions)
 
@@ -240,8 +264,7 @@ class LogoutRequest(ProviderPageBase):
 
         saml_sessions = us.get_provider_data('saml2')
         if saml_sessions is None or saml_sessions.count() == 0:
-            self.error('Logout attempt without being loggged in.')
-            raise cherrypy.HTTPError(400, 'Not logged in')
+            return self._not_logged_in(logout, message)
 
         try:
             session = saml_sessions.get_last_session()