def document(self, *args, **kwargs): """Render the error document""" resp = request.environ.get('pylons.original_response') default_message = _("We're sorry but we weren't " "able to process this request.") if resp and resp.unicode_body: # WebError encode automatiquement les caractères spéciaux # sous forme d'entités (numériques ou nommées). # On doit les décoder pour éviter un double encodage au # niveau du template (Cf. ticket #191). def repl(match): entities = { 'amp': '&', 'gt': '>', 'lt': '<', 'quot': '"', 'apos': "'", } try: # On suppose qu'il s'agit d'une entité numérique. return unichr(int(str(match.group(2)), 10)) except ValueError: # Il ne s'agit probablement pas d'une entité numérique, # on tente une conversion avec les entités de base de XML. # Si cela échoue également, on renvoie le texte initial. return entities.get( str(match.group(1)), str(match.group(0))) default_message = re.sub( u'&(#([0-9]+)|amp|quot|lt|gt);', repl, resp.unicode_body) values = dict(prefix=request.environ.get('SCRIPT_NAME', ''), code=int(request.params.get('code', resp.status_int)), message=request.params.get('message', default_message)) return values
def get_through_proxy(server_type, host, url, data=None, headers=None, charset=None): """ Récupère le contenu d'un document à travers le mécanisme de proxy. @param server_type: Type d'application à "proxifier", par exemple : "nagios" ou "vigirrd". @type server_type: C{basestring} @param host: Nom de l'hôte (supervisé) concerné par la demande. @type host: C{unicode} @param url: URL à demander sur le serveur distant, avec éventuellement des paramètres intégrés (query string). Doit être encodé en UTF-8. @type url: C{str} @param data: Dictionnaire contenant une série de paramètres à transmettre dans la requête. Si des paramètres sont donnés, la requête engendrée deviendra automatiquement du type POST au lieu de GET. @type data: C{dict} @param headers: Dictionnaire d'en-têtes HTTP à passer en plus dans la requête. Vous pouvez par exemple utiliser l'en-tête 'X-Forwarded-For' pour indiquer l'adresse IP de l'utilisateur à l'origine de la requête proxifiée (à des fins de traçabilité/imputation). @type headers: C{dict} @param charset: Encodage éventuel de la requête. Si C{None}, l'encodage est déterminé automatiquement à partir de la requête en cours de traitement par TurboGears/WebOb. @return: Renvoie le résultat de la requête proxifiée, tel que retourné par urllib2. @rtype: C{file-like} """ server_type = u'' + server_type.lower() if charset is None: charset = pylons.request.charset service_name = None service = None if data is not None: # Éventuellement, l'utilisateur demande une page # qui se rapporte à un service particulier. service_name = data.get('service') if isinstance(service_name, str): service_name = service_name.decode(charset) # urlencode() ne tolère que le type "str" en entrée. # Ici, on manipule un UnicodeMultiDict de WebOb, # un passage vers UTF-8 est nécessaire. if isinstance(data, UnicodeMultiDict): data = reencode_multidict(data, charset) data = urllib.urlencode(data) if headers is None: headers = {} user = get_current_user() # S'il s'agit du proxy Nagios et que l'hôte donné # correspond à l'hôte virtuel des Services de Haut Niveau, # alors on utilise l'application "nagios-hls" à la place. hls_host = config.get('nagios_hls_host') if server_type == u'nagios' and host == hls_host: vigilo_server = DBSession.query( VigiloServer.name ).distinct().join( (Ventilation, Ventilation.idvigiloserver == VigiloServer.idvigiloserver), (Application, Application.idapp == Ventilation.idapp), ).filter(Application.name == u'nagios-hls' ).scalar() if vigilo_server is None: message = _('No server configured to monitor high-level services ' 'for application "%(app)s"') % { 'app': server_type, } LOGGER.warning(message) raise http_exc.HTTPNotFound(message) else: # On vérifie qu'il existe effectivement un hôte portant ce nom # et configuré pour être supervisé par Vigilo. host_obj = DBSession.query( Host ).filter(Host.name == host ).scalar() if host_obj is None: message = _('No such monitored host: %s') % host LOGGER.warning(message) raise http_exc.HTTPNotFound(message) # On regarde si l'utilisateur a accès à l'hôte demandé. if not config.is_manager.is_met(request.environ): if service_name: service = DBSession.query( LowLevelService ).join( (Host, Host.idhost == LowLevelService.idservice), ).filter(Host.name == host ).filter(LowLevelService.servicename == service_name ).scalar() # On traite le cas où l'utilisateur n'a pas les droits requis. if (service is not None and not service.is_allowed_for(user)) \ or (not host_obj.is_allowed_for(user)): message = None if service is not None: message = _('Access denied to host "%(host)s" and ' 'service "%(service)s"') % { 'host': host, 'service': service.servicename, } else: message = _('Access denied to host "%s"') % host LOGGER.warning(message) raise http_exc.HTTPForbidden(message) # On vérifie que l'hôte est effectivement pris en charge. # ie: qu'un serveur du parc héberge l'application server_type # responsable de cet hôte. vigilo_server = DBSession.query( VigiloServer.name ).join( (Ventilation, Ventilation.idvigiloserver == VigiloServer.idvigiloserver), (Application, Application.idapp == Ventilation.idapp), ).filter(Ventilation.idhost == host_obj.idhost ).filter(Application.name == server_type ).scalar() if vigilo_server is None: message = _('No server configured to monitor "%(host)s" ' 'for application "%(app)s"') % { 'app': server_type, 'host': host, } LOGGER.warning(message) raise http_exc.HTTPNotFound(message) # Récupére les informations sur l'emplacement de l'application # distante. Par défaut, on suppose que la connexion se fait en # texte clair (http) sur le port standard (80). app_path = config['app_path.%s' % server_type].strip('/') app_scheme = config.get('app_scheme.%s' % server_type, 'http') app_port = config.get('app_port.%s' % server_type, app_scheme == 'https' and 443 or 80) app_port = int(app_port) url = url.lstrip('/') manager_url = [app_scheme, "%s:%d" % (vigilo_server, app_port), app_path + "/", "", ""] manager_url = urlparse.urlunsplit(manager_url) full_url = [app_scheme, "%s:%d" % (vigilo_server, app_port), "%s/%s" % (app_path, url), "", ""] full_url = urlparse.urlunsplit(full_url) try: should_redirect = asbool(config.get('app_redirect.%s' % server_type, False)) except ValueError: LOGGER.error(_('Invalid value for app_redirect.%s, not redirecting.'), server_type) should_redirect = False if should_redirect: raise tg.redirect(full_url) LOGGER.info(_("Fetching '%s' through the proxy"), full_url) req = urllib2.Request(full_url, data, headers=headers) opener = urllib2.build_opener() # Si le support pour Kerberos est installé, on l'active. if HTTPKerberosAuthHandler: opener.add_handler(HTTPKerberosAuthHandler()) # Configuration de l'authentification # vers un éventuel proxy intermédiaire. proxy_auth_method = config.get('app_proxy_auth_method', None) proxy_auth_username = config.get('app_proxy_auth_username', None) proxy_auth_password = config.get('app_proxy_auth_password', None) if proxy_auth_method and proxy_auth_username and \ proxy_auth_password is not None: proxy_auth_method = proxy_auth_method.lower() proxy_pass_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() proxy_pass_manager.add_password( None, manager_url, proxy_auth_username, proxy_auth_password) if proxy_auth_method == 'basic': opener.add_handler(urllib2.ProxyBasicAuthHandler(proxy_pass_manager)) LOGGER.debug(_('Basic authentication to the proxy.')) elif proxy_auth_method == 'digest': opener.add_handler(urllib2.ProxyDigestAuthHandler(proxy_pass_manager)) LOGGER.debug(_('Digest authentication to the proxy.')) # Configuration de l'authentification # vers le site final (Nagios, VigiRRD, ...). final_auth_method = config.get('app_auth_method.%s' % server_type, None) final_auth_username = config.get('app_auth_username.%s' % server_type, None) final_auth_password = config.get('app_auth_password.%s' % server_type, None) if final_auth_method and final_auth_username and \ final_auth_password is not None: final_auth_method = final_auth_method.lower() final_pass_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() final_pass_manager.add_password( None, manager_url, final_auth_username, final_auth_password) if final_auth_method == 'basic': opener.add_handler(urllib2.HTTPBasicAuthHandler(final_pass_manager)) LOGGER.debug(_('Basic authentication to the website.')) elif final_auth_method == 'digest': opener.add_handler(urllib2.HTTPDigestAuthHandler(final_pass_manager)) LOGGER.debug(_('Digest authentication to the website.')) try: res = opener.open(req) except urllib2.HTTPError, e: # Permet d'associer les erreurs levées par urllib2 # à des erreurs reconnues par TurboGears2. # On obtient ainsi une page d'erreur plus sympathique. errors = { # On propage l'erreur 304 pour permettre l'utilisation # du cache du navigateur (#570). '304': http_exc.HTTPNotModified, '401': http_exc.HTTPForbidden, '404': http_exc.HTTPNotFound, '503': http_exc.HTTPServiceUnavailable, } error = errors.get(str(e.code)) if error is None: raise e raise error(unicode(e.msg))