diff options
Diffstat (limited to 'ipsilon')
-rw-r--r-- | ipsilon/login/common.py | 19 | ||||
-rw-r--r-- | ipsilon/providers/saml2/sessions.py | 11 | ||||
-rw-r--r-- | ipsilon/providers/saml2idp.py | 41 |
3 files changed, 68 insertions, 3 deletions
diff --git a/ipsilon/login/common.py b/ipsilon/login/common.py index 9beb741..d616882 100644 --- a/ipsilon/login/common.py +++ b/ipsilon/login/common.py @@ -273,11 +273,28 @@ class Login(Page): class Logout(Page): + def __init__(self, *args, **kwargs): + super(Logout, self).__init__(*args, **kwargs) + self.handlers = {} def root(self, *args, **kwargs): - UserSession().logout(self.user) + us = UserSession() + + for provider in self.handlers: + self.debug("Calling logout for provider %s" % provider) + obj = self.handlers[provider] + obj() + + us.logout(self.user) return self._template('logout.html', title='Logout') + def add_handler(self, provider, handler): + """ + Providers can register a logout handler here that is called + when the IdP logout link is accessed. + """ + self.handlers[provider] = handler + class Cancel(Page): diff --git a/ipsilon/providers/saml2/sessions.py b/ipsilon/providers/saml2/sessions.py index fb1f646..5931734 100644 --- a/ipsilon/providers/saml2/sessions.py +++ b/ipsilon/providers/saml2/sessions.py @@ -140,12 +140,16 @@ class SAMLSessionsContainer(Log): self.sessions_logging_out[session.provider_id] = session - def get_next_logout(self): + def get_next_logout(self, remove=True): """ Get the next session in the logged-in state and move it to the logging_out state. Return the session that is found. + :param remove: for IdP-initiated logout we can't remove the + session otherwise when the request comes back + in the user won't be seen as being logged-on. + Return None if no more sessions in login state. """ try: @@ -153,7 +157,10 @@ class SAMLSessionsContainer(Log): except IndexError: return None - session = self.sessions.pop(provider_id) + if remove: + session = self.sessions.pop(provider_id) + else: + session = self.sessions.itervalues().next() if provider_id in self.sessions_logging_out: self.sessions_logging_out.pop(provider_id) diff --git a/ipsilon/providers/saml2idp.py b/ipsilon/providers/saml2idp.py index 8ff512c..9bc75b3 100644 --- a/ipsilon/providers/saml2idp.py +++ b/ipsilon/providers/saml2idp.py @@ -298,6 +298,8 @@ Provides SAML 2.0 authentication infrastructure. """ self._debug('Failed to init SAML2 provider: %r' % e) return None + self._root.logout.add_handler(self.name, self.idp_initiated_logout) + # Import all known applications data = self.get_data() for idval in data: @@ -320,6 +322,45 @@ Provides SAML 2.0 authentication infrastructure. """ if self.admin: self.admin.add_sps() + def idp_initiated_logout(self): + """ + Logout all SP sessions when the logout comes from the IdP. + + For the current user only. + """ + self._debug("IdP-initiated SAML2 logout") + us = UserSession() + + saml_sessions = us.get_provider_data('saml2') + if saml_sessions is None: + self._debug("No SAML2 sessions to logout") + return + session = saml_sessions.get_next_logout(remove=False) + if session is None: + return + + # Add a fake session to indicate where the user should + # be redirected to when all SP's are logged out. + idpurl = self._root.instance_base_url() + saml_sessions.add_session("_idp_initiated_logout", + idpurl, + "") + init_session = saml_sessions.find_session_by_provider(idpurl) + init_session.set_logoutstate(idpurl, "idp_initiated_logout", None) + saml_sessions.start_logout(init_session) + + logout = self.idp.get_logout_handler() + logout.setSessionFromDump(session.session.dump()) + logout.initRequest(session.provider_id) + try: + logout.buildRequestMsg() + except lasso.Error, e: + self.error('failure to build logout request msg: %s' % e) + raise cherrypy.HTTPRedirect(400, 'Failed to log out user: %s ' + % e) + + raise cherrypy.HTTPRedirect(logout.msgUrl) + class IdpMetadataGenerator(object): |