pam: use a pam object method instead of pam module function
[cascardo/ipsilon.git] / ipsilon / providers / saml2 / auth.py
index 611c9bf..cc41bb8 100644 (file)
@@ -5,7 +5,6 @@ from ipsilon.providers.common import AuthenticationError, InvalidRequest
 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
@@ -26,8 +25,8 @@ class UnknownProvider(ProviderException):
 
 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
@@ -100,23 +99,65 @@ class AuthenticateRequest(ProviderPageBase):
 
         return login
 
-    def saml2login(self, request):
+    def _idp_initiated_login(self, spidentifier, relaystate):
+        """
+        Perform an Idp-initiated login
 
-        if not request:
+        Exceptions are handled by the caller
+        """
+        login = self.cfg.idp.get_login_handler()
+
+        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
 
@@ -212,6 +253,8 @@ class AuthenticateRequest(ProviderPageBase):
             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)
 
@@ -275,23 +318,17 @@ class AuthenticateRequest(ProviderPageBase):
 
         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()