# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""user authentication component"""from__future__importwith_statement__docformat__="restructuredtext en"fromthreadingimportLockfromlogilab.common.decoratorsimportclear_cachefromcubicwebimportAuthenticationError,BadConnectionIdfromcubicweb.viewimportComponentfromcubicweb.dbapiimportrepo_connect,ConnectionPropertiesfromcubicweb.webimportInvalidSessionfromcubicweb.web.applicationimportAbstractAuthenticationManagerclassNoAuthInfo(Exception):passclassWebAuthInfoRetreiver(Component):__registry__='webauth'order=None__abstract__=Truedefauthentication_information(self,req):"""retreive authentication information from the given request, raise NoAuthInfo if expected information is not found. """raiseNotImplementedError()defauthenticated(self,retreiver,req,cnx,login,authinfo):"""callback when return authentication information have opened a repository connection successfully. Take care req has no session attached yet, hence req.execute isn't available. """passdefrequest_has_auth_info(self,req):"""tells from the request if it has enough information to proceed to authentication, would the current session be invalidated """raiseNotImplementedError()defrevalidate_login(self,req):"""returns a login string or None, for repository session validation purposes """raiseNotImplementedError()defcleanup_authentication_information(self,req):"""called when the retriever has returned some authentication information but we get an authentication error when using them, so it get a chance to cleanup things (e.g. remove cookie) """passclassLoginPasswordRetreiver(WebAuthInfoRetreiver):__regid__='loginpwdauth'order=10defauthentication_information(self,req):"""retreive authentication information from the given request, raise NoAuthInfo if expected information is not found. """login,password=req.get_authorization()ifnotlogin:raiseNoAuthInfo()returnlogin,{'password':password}defrequest_has_auth_info(self,req):returnreq.get_authorization()[0]isnotNonedefrevalidate_login(self,req):returnreq.get_authorization()[0]classRepositoryAuthenticationManager(AbstractAuthenticationManager):"""authenticate user associated to a request and check session validity"""def__init__(self,vreg):super(RepositoryAuthenticationManager,self).__init__(vreg)self.repo=vreg.config.repository(vreg)self.log_queries=vreg.config['query-log-file']self.authinforetrievers=sorted(vreg['webauth'].possible_objects(vreg),key=lambdax:x.order)# 2-uple login / password, login is None when no anonymous access# configuredself.anoninfo=vreg.config.anonymous_user()ifself.anoninfo[0]:self.anoninfo=(self.anoninfo[0],{'password':self.anoninfo[1]})defvalidate_session(self,req,session):"""check session validity and return the connected user on success. raise :exc:`InvalidSession` if session is corrupted for a reason or another and should be closed also invoked while going from anonymous to logged in """forretrieverinself.authinforetrievers:ifretriever.request_has_auth_info(req):login=retriever.revalidate_login(req)returnself._validate_session(req,session,login)# let's try with the current sessionreturnself._validate_session(req,session,None)def_validate_session(self,req,session,login):# check session.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 loginifloginandsession.login!=login:raiseInvalidSession('login mismatch')try:# calling cnx.user() check connection validity, raise# BadConnectionId on failureuser=session.cnx.user(req)exceptBadConnectionId:raiseInvalidSession('bad connection id')returnuserdefauthenticate(self,req):"""authenticate user using connection information found in the request, and return corresponding a :class:`~cubicweb.dbapi.Connection` instance, as well as login used to open the connection. raise :exc:`cubicweb.AuthenticationError` if authentication failed (no authentication info found or wrong user/password) """forretrieverinself.authinforetrievers:try:login,authinfo=retriever.authentication_information(req)exceptNoAuthInfo:continuetry:cnx=self._authenticate(login,authinfo)exceptAuthenticationError:retriever.cleanup_authentication_information(req)continue# the next one may succeedforretriever_inself.authinforetrievers:retriever_.authenticated(retriever,req,cnx,login,authinfo)returncnx,login# false if no authentication info found, eg this is not an# authentication failureif'login'inlocals():req.set_message(req._('authentication failure'))login,authinfo=self.anoninfoiflogin:cnx=self._authenticate(login,authinfo)cnx.anonymous_connection=Truereturncnx,loginraiseAuthenticationError()def_authenticate(self,login,authinfo):cnxprops=ConnectionProperties(self.vreg.config.repo_method,close=False,log=self.log_queries)cnx=repo_connect(self.repo,login,cnxprops=cnxprops,**authinfo)# decorate connectioncnx.vreg=self.vregreturncnx