from ipsilon.providers.saml2.provider import ServiceProvider
from ipsilon.providers.saml2.provider import InvalidProviderId
from ipsilon.providers.saml2.provider import NameIdNotAllowed
-from ipsilon.providers.saml2.sessions import SAMLSessionsContainer
+from ipsilon.tools import saml2metadata as metadata
from ipsilon.util.policy import Policy
from ipsilon.util.user import UserSession
from ipsilon.util.trans import Transaction
class AuthenticateRequest(ProviderPageBase):
- def __init__(self, *args, **kwargs):
- super(AuthenticateRequest, self).__init__(*args, **kwargs)
+ def __init__(self, site, provider, *args, **kwargs):
+ super(AuthenticateRequest, self).__init__(site, provider)
self.stage = 'init'
self.trans = None
+ self.binding = None
def _preop(self, *args, **kwargs):
try:
# generate a new id or get current one
self.trans = Transaction('saml2', **kwargs)
- if self.trans.cookie.value != self.trans.provider:
- self.debug('Invalid transaction, %s != %s' % (
- self.trans.cookie.value, self.trans.provider))
+
+ self.debug('self.binding=%s, transdata=%s' %
+ (self.binding, self.trans.retrieve()))
+ if self.binding is None:
+ # SAML binding is unknown, try to get it from transaction
+ transdata = self.trans.retrieve()
+ self.binding = transdata.get('saml2_binding')
+ else:
+ # SAML binding known, store in transaction
+ data = {'saml2_binding': self.binding}
+ self.trans.store(data)
+
+ # Only check for cookie for those bindings which use one
+ if self.binding not in (metadata.SAML2_SERVICE_MAP['sso-soap'][1]):
+ if self.trans.cookie.value != self.trans.provider:
+ self.debug('Invalid transaction, %s != %s' % (
+ self.trans.cookie.value, self.trans.provider))
except Exception, e: # pylint: disable=broad-except
self.debug('Transaction initialization failed: %s' % repr(e))
raise cherrypy.HTTPError(400, 'Invalid transaction id')
return login
- def saml2login(self, request):
+ def _idp_initiated_login(self, spidentifier, relaystate):
+ """
+ Perform an Idp-initiated login
+
+ Exceptions are handled by the caller
+ """
+ login = self.cfg.idp.get_login_handler()
- if not request:
+ login.initIdpInitiatedAuthnRequest(spidentifier)
+
+ # Hardcode for now, handle Artifact later
+ login.request.protocolBinding = lasso.SAML2_METADATA_BINDING_POST
+
+ login.processAuthnRequestMsg()
+
+ if relaystate is not None:
+ login.msgRelayState = relaystate
+ else:
+ provider = ServiceProvider(self.cfg, login.remoteProviderId)
+ if provider.splink is not None:
+ login.msgRelayState = provider.splink
+ else:
+ login.msgRelayState = login.remoteProviderId
+
+ return login
+
+ def saml2login(self, request, spidentifier=None, relaystate=None):
+ """
+ request: the SAML request
+ spidentifier: the provider ID for IdP-initiated login
+ relaystate: optional string to direct user to particular place on
+ the SP after sending POST. If one is not provided then
+ the protected site from the SP is used, otherwise it
+ is set to the remote provider ID.
+ """
+ if not request and not spidentifier:
raise cherrypy.HTTPError(400,
'SAML request token missing or empty')
- try:
- login = self._parse_request(request)
- except InvalidRequest, e:
- self.debug(str(e))
- raise cherrypy.HTTPError(400, 'Invalid SAML request token')
- except UnknownProvider, e:
- self.debug(str(e))
- raise cherrypy.HTTPError(400, 'Unknown Service Provider')
- except Exception, e: # pylint: disable=broad-except
- self.debug(str(e))
- raise cherrypy.HTTPError(500)
+ if spidentifier:
+ try:
+ login = self._idp_initiated_login(spidentifier, relaystate)
+ except lasso.ServerProviderNotFoundError:
+ raise cherrypy.HTTPError(400, 'Unknown Service Provider')
+ except Exception, e: # pylint: disable=broad-except
+ self.debug(str(e))
+ raise cherrypy.HTTPError(500)
+ else:
+ try:
+ login = self._parse_request(request)
+ except InvalidRequest, e:
+ self.debug(str(e))
+ raise cherrypy.HTTPError(400, 'Invalid SAML request token')
+ except UnknownProvider, e:
+ self.debug(str(e))
+ raise cherrypy.HTTPError(400, 'Unknown Service Provider')
+ except Exception, e: # pylint: disable=broad-except
+ self.debug(str(e))
+ raise cherrypy.HTTPError(500)
return login
login.assertion.subject.nameId.content = nameid
else:
self.trans.wipe()
+ self.error('Authentication succeeded but it was not ' +
+ 'provided by NameID %s' % nameidfmt)
raise AuthenticationError("Unavailable Name ID type",
lasso.SAML2_STATUS_CODE_AUTHN_FAILED)
self.debug('Assertion: %s' % login.assertion.dump())
- saml_sessions = us.get_provider_data('saml2')
- if saml_sessions is None:
- saml_sessions = SAMLSessionsContainer()
-
- session = saml_sessions.find_session_by_provider(
- login.remoteProviderId)
- if session:
- # TODO: something...
- self.debug('Login session for this user already exists!?')
- session.dump()
+ saml_sessions = self.cfg.idp.sessionfactory
lasso_session = lasso.Session()
lasso_session.addAssertion(login.remoteProviderId, login.assertion)
+ provider = ServiceProvider(self.cfg, login.remoteProviderId)
saml_sessions.add_session(login.assertion.id,
login.remoteProviderId,
- lasso_session)
- us.save_provider_data('saml2', saml_sessions)
+ user.name,
+ lasso_session.dump(),
+ None,
+ provider.logout_mechs)
def saml2error(self, login, code, message):
status = lasso.Samlp2Status()
}
return self._template('saml2/post_response.html', **context)
+ elif login.protocolProfile == lasso.LOGIN_PROTOCOL_PROFILE_BRWS_LECP:
+ login.buildResponseMsg()
+ self.debug("Returning ECP: %s" % login.msgBody)
+ return login.msgBody
+
else:
raise cherrypy.HTTPError(500)