web/views/authentication.py
author Sylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 02 Dec 2009 17:39:56 +0100
branchstable
changeset 3975 569771016abb
parent 2267 e1d2df3f1091
child 2887 1282dc6525c5
child 4212 ab6573088b4a
permissions -rw-r--r--
add a fourth item to 'view box' defintion, dispctrl, so that we can later globally sort all boxes instead of having view boxes before component boxes. 'view' boxes order is configured through uicfg.primaryview_display_ctrl, 'component' boxes order through the cwproperty system.

"""user authentication component

:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"

from logilab.common.decorators import clear_cache

from cubicweb import AuthenticationError, BadConnectionId
from cubicweb.dbapi import repo_connect, ConnectionProperties
from cubicweb.web import ExplicitLogin, InvalidSession
from cubicweb.web.application import AbstractAuthenticationManager


class RepositoryAuthenticationManager(AbstractAuthenticationManager):
    """authenticate user associated to a request and check session validity"""

    def __init__(self):
        self.repo = self.config.repository(self.vreg)
        self.log_queries = self.config['query-log-file']

    def validate_session(self, req, session):
        """check session validity, and return eventually hijacked session

        :raise InvalidSession:
          if session is corrupted for a reason or another and should be closed
        """
        # with this authentication manager, session is actually a dbapi
        # connection
        cnx = session
        login = req.get_authorization()[0]
        try:
            # calling cnx.user() check connection validity, raise
            # BadConnectionId on failure
            user = cnx.user(req)
            # check cnx.login and not user.login, since in case of login by
            # email, login and cnx.login are the email while user.login is the
            # actual user login
            if login and cnx.login != login:
                cnx.close()
                raise InvalidSession('login mismatch')
        except BadConnectionId:
            # check if a connection should be automatically restablished
            if (login is None or login == cnx.login):
                login, password = cnx.login, cnx.password
                cnx = self.authenticate(req, login, password)
                user = cnx.user(req)
                # backport session's data
                cnx.data = session.data
            else:
                raise InvalidSession('bad connection id')
        # associate the connection to the current request
        req.set_connection(cnx, user)
        return cnx

    def authenticate(self, req, _login=None, _password=None):
        """authenticate user and return corresponding user object

        :raise ExplicitLogin: if authentication is required (no authentication
        info found or wrong user/password)

        Note: this method is violating AuthenticationManager interface by
        returning a session instance instead of the user. This is expected by
        the InMemoryRepositorySessionManager.
        """
        if _login is not None:
            login, password = _login, _password
        else:
            login, password = req.get_authorization()
        if not login:
            # No session and no login -> try anonymous
            login, password = self.vreg.config.anonymous_user()
            if not login: # anonymous not authorized
                raise ExplicitLogin()
        # remove possibly cached cursor coming from closed connection
        clear_cache(req, 'cursor')
        cnxprops = ConnectionProperties(self.vreg.config.repo_method,
                                        close=False, log=self.log_queries)
        try:
            cnx = repo_connect(self.repo, login, password, cnxprops=cnxprops)
        except AuthenticationError:
            req.set_message(req._('authentication failure'))
            # restore an anonymous connection if possible
            anonlogin, anonpassword = self.vreg.config.anonymous_user()
            if anonlogin and anonlogin != login:
                cnx = repo_connect(self.repo, anonlogin, anonpassword,
                                   cnxprops=cnxprops)
                self._init_cnx(cnx, anonlogin, anonpassword)
            else:
                raise ExplicitLogin()
        else:
            self._init_cnx(cnx, login, password)
        # associate the connection to the current request
        req.set_connection(cnx)
        return cnx

    def _init_cnx(self, cnx, login, password):
        # decorate connection
        if login == self.vreg.config.anonymous_user()[0]:
            cnx.anonymous_connection = True
        cnx.vreg = self.vreg
        cnx.login = login
        cnx.password = password