# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
+from ipsilon.login.common import LoginHelper
from ipsilon.providers.common import ProviderBase, ProviderPageBase, \
ProviderInstaller
from ipsilon.providers.saml2.auth import AuthenticateRequest
from ipsilon.providers.saml2.rest import Saml2RestBase
from ipsilon.providers.saml2.provider import IdentityProvider
from ipsilon.providers.saml2.sessions import SAMLSessionFactory
-from ipsilon.util.data import SAML2SessionStore
from ipsilon.tools.certs import Certificate
from ipsilon.tools import saml2metadata as metadata
from ipsilon.tools import files
def is_lasso_ecp_enabled():
- # Full ECP support appeared in lasso version 2.4.2
- return lasso.checkVersion(2, 4, 2, lasso.CHECK_VERSION_NUMERIC)
+ # Look for an exported symbol we know was added with ECP support
+ return 'ECP_ERROR_MISSING_AUTHN_REQUEST' in dir(lasso)
-class SSO_SOAP(AuthenticateRequest):
+class SSO_SOAP(AuthenticateRequest, LoginHelper):
- def __init__(self, *args, **kwargs):
- super(SSO_SOAP, self).__init__(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(SSO_SOAP, self).__init__(site, provider, *args, **kwargs)
+ # pylint: disable=protected-access
+ self.info = provider._root.login.info
self.binding = metadata.SAML2_SERVICE_MAP['sso-soap'][1]
@cherrypy.tools.require_content_type(
self.debug("SSO_SOAP transaction provider=%s id=%s" %
(self.trans.provider, self.trans.transaction_id))
- us = UserSession()
- us.remote_login()
- user = us.get_user()
- self.debug("SSO_SOAP user=%s" % (user.name))
-
- if not user:
+ username, auth_type = self.get_external_auth_info()
+ if not username:
raise cherrypy.HTTPError(403, 'No user specified for SSO_SOAP')
+ self.debug("SSO_SOAP user=%s auth_type=%s" % (username, auth_type))
+ self.initialize_login_session(username, self.info, auth_type)
soap_xml_doc = cherrypy.request.rfile.read()
soap_xml_doc = soap_xml_doc.strip()
class Redirect(AuthenticateRequest):
- def __init__(self, *args, **kwargs):
- super(Redirect, self).__init__(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(Redirect, self).__init__(site, provider, *args, **kwargs)
self.binding = metadata.SAML2_SERVICE_MAP['sso-redirect'][1]
def GET(self, *args, **kwargs):
query = cherrypy.request.query_string
- login = self.saml2login(query)
+ spidentifier = kwargs.get('SPIdentifier')
+ relaystate = kwargs.get(lasso.SAML2_FIELD_RELAYSTATE)
+
+ login = self.saml2login(query, spidentifier, relaystate)
return self.auth(login)
class POSTAuth(AuthenticateRequest):
- def __init__(self, *args, **kwargs):
- super(POSTAuth, self).__init__(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(POSTAuth, self).__init__(site, provider, *args, **kwargs)
self.binding = metadata.SAML2_SERVICE_MAP['sso-post'][1]
def POST(self, *args, **kwargs):
self.debug('Continue auth for %s' % user.name)
if 'saml2_request' not in transdata:
- self.debug("Couldn't find Request dump?!")
+ self.error("Couldn't find Request dump in transaction?!")
# TODO: Return to SP with auth failed error
raise cherrypy.HTTPError(400)
dump = transdata['saml2_request']
try:
login = self.cfg.idp.get_login_handler(dump)
except Exception, e: # pylint: disable=broad-except
- self.debug('Failed to load status from dump: %r' % e)
+ self.error('Failed to load login status from dump: %r' % e)
if not login:
- self.debug("Empty Request dump?!")
+ self.error("Empty login Request dump?!")
# TODO: Return to SP with auth failed error
raise cherrypy.HTTPError(400)
return self.auth(login)
-class RedirectLogout(LogoutRequest):
+class Logout(LogoutRequest):
def GET(self, *args, **kwargs):
query = cherrypy.request.query_string
class SSO(ProviderPageBase):
- def __init__(self, *args, **kwargs):
- super(SSO, self).__init__(*args, **kwargs)
- self.Redirect = Redirect(*args, **kwargs)
- self.POST = POSTAuth(*args, **kwargs)
- self.Continue = Continue(*args, **kwargs)
- self.SOAP = SSO_SOAP(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(SSO, self).__init__(site, provider)
+ self.Redirect = Redirect(site, provider, *args, **kwargs)
+ self.POST = POSTAuth(site, provider, *args, **kwargs)
+ self.Continue = Continue(site, provider, *args, **kwargs)
+ self.SOAP = SSO_SOAP(site, provider, *args, **kwargs)
class SLO(ProviderPageBase):
- def __init__(self, *args, **kwargs):
- super(SLO, self).__init__(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(SLO, self).__init__(site, provider)
self.debug('SLO init')
- self.Redirect = RedirectLogout(*args, **kwargs)
+ self.Redirect = Logout(site, provider, *args, **kwargs)
# one week
class SAML2(ProviderPageBase):
- def __init__(self, *args, **kwargs):
- super(SAML2, self).__init__(*args, **kwargs)
- self.metadata = Metadata(*args, **kwargs)
- self.SSO = SSO(*args, **kwargs)
- self.SLO = SLO(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(SAML2, self).__init__(site, provider)
+ self.metadata = Metadata(site, provider, *args, **kwargs)
+ self.SSO = SSO(site, provider, *args, **kwargs)
+ self.SLO = SLO(site, provider, *args, **kwargs)
class IdpProvider(ProviderBase):
logger.addHandler(lh)
logger.setLevel(logging.DEBUG)
- store = SAML2SessionStore(
- database_url=self.get_config_value('session database url')
- )
- bt = cherrypy.process.plugins.BackgroundTask(
- 60, store.remove_expired_sessions
- )
- bt.start()
+ def get_providers(self):
+ return self.admin.providers
@property
def allow_self_registration(self):
return self.get_config_value('default allowed attributes')
def get_tree(self, site):
- self.idp = self.init_idp()
self.page = SAML2(site, self)
self.admin = Saml2AdminPage(site, self)
self.rest = Saml2RestBase(site, self)
return self.page
+ def used_datastores(self):
+ # pylint: disable=protected-access
+ return [self.sessionfactory._ss]
+
def init_idp(self):
idp = None
self.sessionfactory = SAMLSessionFactory(
idp = IdentityProvider(self,
sessionfactory=self.sessionfactory)
except Exception, e: # pylint: disable=broad-except
- self.debug('Failed to init SAML2 provider: %r' % e)
+ self.error('Failed to init SAML2 provider: %r' % e)
return None
self._root.logout.add_handler(self.name, self.idp_initiated_logout)
try:
idp.add_provider(sp)
except Exception, e: # pylint: disable=broad-except
- self.debug('Failed to add SP %s: %r' % (sp['name'], e))
+ self.error('Failed to add SP %s: %r' % (sp['name'], e))
return idp
Logout all SP sessions when the logout comes from the IdP.
For the current user only.
+
+ Only use HTTP-Redirect to start the logout. This is guaranteed
+ to be supported in SAML 2.
"""
self.debug("IdP-initiated SAML2 logout")
us = UserSession()
user = us.get_user()
saml_sessions = self.sessionfactory
- session = saml_sessions.get_next_logout()
+ # pylint: disable=unused-variable
+ (mech, session) = saml_sessions.get_next_logout(
+ logout_mechs=[lasso.SAML2_METADATA_BINDING_REDIRECT])
if session is None:
return
# be redirected to when all SP's are logged out.
idpurl = self._root.instance_base_url()
session_id = "_" + uuid.uuid4().hex.upper()
- saml_sessions.add_session(session_id, idpurl, user.name, "")
+ saml_sessions.add_session(session_id, idpurl, user.name, "", "",
+ [lasso.SAML2_METADATA_BINDING_REDIRECT])
init_session = saml_sessions.get_session_by_id(session_id)
saml_sessions.start_logout(init_session, relaystate=idpurl)