def authenticate(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) """ has_auth = False for retriever in self.authinforetrievers: try: login, authinfo = retriever.authentication_information(req) except NoAuthInfo: continue has_auth = True try: session = self._authenticate(login, authinfo) except AuthenticationError: retriever.cleanup_authentication_information(req) continue # the next one may succeed for retriever_ in self.authinforetrievers: retriever_.authenticated(retriever, req, session, login, authinfo) return session, login # false if no authentication info found, i.e. this is not an # authentication failure if has_auth: req.set_message(req._('authentication failure')) login, authinfo = self.anoninfo if login: session = self._authenticate(login, authinfo) return session, login raise AuthenticationError()
def publish(self, rset=None): """log in the instance""" if self._cw.vreg.config['auth-mode'] == 'http': # HTTP authentication raise AuthenticationError() else: # Cookie authentication self._cw.status_out = http.client.FORBIDDEN return self.appli.need_login_content(self._cw)
def authenticate(self, cnx, login, password=None, **kwargs): """return CWUser eid for the given login/password if this account is defined in this source, else raise `AuthenticationError` two queries are needed since passwords are stored crypted, so we have to fetch the salt first """ self.info('ldap authenticate %s', login) if not password: # On Windows + ADAM this would have succeeded (!!!) # You get Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'. # we really really don't want that raise AuthenticationError() searchfilter = [ '(%s=%s)' % (replace_filter(self.user_login_attr), replace_filter(login)) ] searchfilter.extend(self.base_filters) searchstr = '(&%s)' % ''.join(searchfilter) # first search the user try: user = self._search(cnx, self.user_base_dn, self.user_base_scope, searchstr)[0] except IndexError: # no such user raise AuthenticationError() # check password by establishing a (unused) connection try: self._connect(user, password) except ldap3.LDAPException as ex: # Something went wrong, most likely bad credentials self.info('while trying to authenticate %s: %s', user, ex) raise AuthenticationError() except Exception: self.error('while trying to authenticate %s', user, exc_info=True) raise AuthenticationError() rset = cnx.execute( 'Any X,SN WHERE X cwuri %(extid)s, X is CWUser, ' 'X cw_source S, S name SN', {'extid': user['dn']}) if not rset or rset[0][1] != self.uri: # user is not known or has been moved away from this source raise AuthenticationError() return rset[0][0]
def authenticate_user(self, cnx, login, **authinfo): """validate login / password, raise AuthenticationError on failure return associated CWUser instance on success """ eid = self.check_auth_info(cnx, login, authinfo) cwuser = self._build_user(cnx, eid) if self.config.consider_user_state and \ not cwuser.cw_adapt_to('IWorkflowable').state in cwuser.AUTHENTICABLE_STATES: raise AuthenticationError('user is not in authenticable state') return cwuser
def anonymous_cnx(repo): """return a Connection for Anonymous user. raises an AuthenticationError if anonymous usage is not allowed """ anoninfo = getattr(repo.config, 'anonymous_user', lambda: None)() if anoninfo is None: # no anonymous user raise AuthenticationError('anonymous access is not authorized') anon_login, anon_password = anoninfo # use vreg's repository cache return connect(repo, anon_login, password=anon_password)
def check_auth_info(self, cnx, login, authinfo): """validate authentication, raise AuthenticationError on failure, return associated CWUser's eid on success. """ # iter on sources_by_uri then check enabled source since sources doesn't # contain copy based sources for source in self.sources_by_uri.values(): if self.config.source_enabled(source): try: return source.authenticate(cnx, login, **authinfo) except (NotImplementedError, AuthenticationError): continue else: raise AuthenticationError('authentication failed with all sources')
def _connect(self, user=None, userpwd=None): protocol, host, port = self.connection_info() self.info('connecting %s://%s:%s as %s', protocol, host, port, user and user['dn'] or 'anonymous') server = ldap3.Server(host, port=int(port)) conn = ldap3.Connection( server, user=user and user['dn'], client_strategy=ldap3.STRATEGY_SYNC_RESTARTABLE, auto_referrals=False) # Now bind with the credentials given. Let exceptions propagate out. if user is None: # XXX always use simple bind for data connection if not self.cnx_dn: conn.bind() else: self._authenticate(conn, {'dn': self.cnx_dn}, self.cnx_pwd) else: # user specified, we want to check user/password, no need to return # the connection which will be thrown out if not self._authenticate(conn, user, userpwd): raise AuthenticationError() return conn
def main_handle_request(self, req): """Process an HTTP request `req` :type req: `web.Request` :param req: the request object It returns the content of the http response. HTTP header and status are set on the Request object. """ if req.authmode == 'http': # activate realm-based auth realm = self.vreg.config['realm'] req.set_header('WWW-Authenticate', [('Basic', { 'realm': realm })], raw=False) content = b'' try: try: session = self.get_session(req) cnx = session.new_cnx() with cnx: # may need an open connection to access to e.g. properties req.set_cnx(cnx) cnx._open = None # XXX needed to reuse it a few line later :'( except AuthenticationError: # Keep the dummy session set at initialisation. such session will work to some # extend but raise an AuthenticationError on any database access. # XXX We want to clean up this approach in the future. But several cubes like # registration or forgotten password rely on this principle. @contextlib.contextmanager def dummy(): yield cnx = dummy() # nested try to allow LogOut to delegate logic to AuthenticationError # handler try: # Try to generate the actual request content with cnx: content = self.core_handle(req) # Handle user log-out except LogOut as ex: # When authentification is handled by cookie the code that # raised LogOut must has invalidated the cookie. We can just # reload the original url without authentification if self.vreg.config['auth-mode'] == 'cookie' and ex.url: req.headers_out.setHeader('location', str(ex.url)) if ex.status is not None: req.status_out = http_client.SEE_OTHER # When the authentification is handled by http we must # explicitly ask for authentification to flush current http # authentification information else: # Render "logged out" content. # assignement to ``content`` prevent standard # AuthenticationError code to overwrite it. content = self.loggedout_content(req) # let the explicitly reset http credential raise AuthenticationError() except Redirect as ex: # authentication needs redirection (eg openid) content = self.redirect_handler(req, ex) # Wrong, absent or Reseted credential except AuthenticationError: # We assume here that in http auth mode the user *May* provide # Authentification Credential if asked kindly. if self.vreg.config['auth-mode'] == 'http': req.status_out = http_client.UNAUTHORIZED # In the other case (coky auth) we assume that there is no way # for the user to provide them... # XXX But WHY ? else: req.status_out = http_client.FORBIDDEN # If previous error handling already generated a custom content # do not overwrite it. This is used by LogOut Except # XXX ensure we don't actually serve content if not content: content = self.need_login_content(req) assert isinstance(content, bytes) return content
def __getattribute__(self, attr): raise AuthenticationError()