-# 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
-from ipsilon.providers.saml2.sessions import SAMLSessionsContainer
from ipsilon.providers.saml2.auth import UnknownProvider
from ipsilon.util.user import UserSession
import cherrypy
self.error('SLO unknown error: %s' % message)
raise cherrypy.HTTPError(400, 'Invalid logout request')
- # TODO: verify that the session index is in the request
session_indexes = logout.request.sessionIndexes
self.debug('SLO from %s with %s sessions' %
(logout.remoteProviderId, session_indexes))
- session = saml_sessions.find_session_by_provider(
- logout.remoteProviderId)
+ # Find the first session being asked to log out. Later we loop over
+ # all the session indexes and mark them as logging out but only one
+ # is needed to handle the request.
+ if len(session_indexes) < 1:
+ self.error('SLO empty session Indexes: %s')
+ raise cherrypy.HTTPError(400, 'Invalid logout request')
+ session = saml_sessions.get_session_by_id(session_indexes[0])
if session:
try:
- logout.setSessionFromDump(session.session.dump())
+ logout.setSessionFromDump(session.login_session)
except lasso.ProfileBadSessionDumpError as e:
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)
except lasso.Error, e:
self.error('SLO failed to build logout response: %s' % e)
- session.set_logoutstate(logout.msgUrl, logout.request.id,
- message)
- saml_sessions.start_logout(session)
-
- us.save_provider_data('saml2', saml_sessions)
+ for ind in session_indexes:
+ session = saml_sessions.get_session_by_id(ind)
+ if session:
+ session.set_logoutstate(relaystate=logout.msgUrl,
+ request=message)
+ saml_sessions.start_logout(session)
+ else:
+ self.error('SLO request to log out non-existent session: %s' %
+ ind)
return
self.debug('SLO response to request id %s' %
logout.response.inResponseTo)
- saml_sessions = us.get_provider_data('saml2')
- if saml_sessions is None:
- # TODO: return logged out instead
- saml_sessions = SAMLSessionsContainer()
-
- # TODO: need to log out each SessionIndex?
- session = saml_sessions.find_session_by_provider(
- logout.remoteProviderId)
+ session = saml_sessions.get_session_by_request_id(
+ logout.response.inResponseTo)
if session is not None:
self.debug('Logout response session logout id is: %s' %
session.session_id)
- saml_sessions.remove_session_by_provider(
- logout.remoteProviderId)
- us.save_provider_data('saml2', saml_sessions)
+ saml_sessions.remove_session(session)
user = us.get_user()
self._audit('Logged out user: %s [%s] from %s' %
(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
us = UserSession()
- 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')
-
- self.debug('%d sessions loaded' % saml_sessions.count())
- saml_sessions.dump()
+ saml_sessions = self.cfg.idp.sessionfactory
if lasso.SAML2_FIELD_REQUEST in message:
self._handle_logout_request(us, logout, saml_sessions, message)
# Fall through to handle any remaining sessions.
# Find the next SP to logout and send a LogoutRequest
- saml_sessions = us.get_provider_data('saml2')
session = saml_sessions.get_next_logout()
if session:
self.debug('Going to log out %s' % session.provider_id)
try:
- logout.setSessionFromDump(session.session.dump())
+ logout.setSessionFromDump(session.login_session)
except lasso.ProfileBadSessionDumpError as e:
self.error('Failed to load session: %s' % e)
raise cherrypy.HTTPRedirect(400, 'Failed to log out user: %s '
raise cherrypy.HTTPRedirect(400, 'Failed to log out user: %s '
% e)
- # Now set the full list of session indexes to log out
+ # Set the full list of session indexes for this provider to
+ # log out
+ self.debug('logging out provider id %s' % session.provider_id)
+ indexes = saml_sessions.get_session_id_by_provider_id(
+ session.provider_id
+ )
+ self.debug('Requesting logout for sessions %s' % indexes)
req = logout.get_request()
- req.setSessionIndexes(tuple(set(session.session_indexes)))
+ req.setSessionIndexes(indexes)
- session.set_logoutstate(logout.msgUrl, logout.request.id, None)
- us.save_provider_data('saml2', saml_sessions)
+ session.set_logoutstate(relaystate=logout.msgUrl,
+ request_id=logout.request.id)
+ saml_sessions.start_logout(session, initial=False)
self.debug('Request logout ID %s for session ID %s' %
(logout.request.id, session.session_id))
# Otherwise we're done, respond to the original request using the
# response we cached earlier.
- 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')
-
try:
- session = saml_sessions.get_last_session()
+ session = saml_sessions.get_initial_logout()
except ValueError:
self.debug('SLO get_last_session() unable to find last session')
raise cherrypy.HTTPError(400, 'Unable to determine logout state')
- redirect = session.logoutstate.get('relaystate')
+ redirect = session.relaystate
if not redirect:
redirect = self.basepath
+ saml_sessions.remove_session(session)
+
# Log out of cherrypy session
user = us.get_user()
self._audit('Logged out user: %s [%s] from %s' %