Exemple #1
0
    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
Exemple #2
0
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))