コード例 #1
0
 def report_get(self) -> pyramid.response.Response:
     """ Get the PDF. """
     url = Url("{0!s}/report/{1!s}".format(
         self.config["print_url"], self.request.matchdict.get("ref")))
     if self.config.get("print_get_redirect", False):
         raise HTTPFound(location=url.url())
     return self._proxy_response("print", url)
コード例 #2
0
ファイル: theme.py プロジェクト: camptocamp/c2cgeoportal
    async def _wfs_get_features_type(
        self, wfs_url: Url, ogc_server_name: str, preload: bool = False
    ) -> Tuple[Optional[etree.Element], Set[str]]:
        errors = set()

        wfs_url.add_query(
            {
                "SERVICE": "WFS",
                "VERSION": "1.0.0",
                "REQUEST": "DescribeFeatureType",
                "ROLE_IDS": "0",
                "USER_ID": "0",
            }
        )

        LOG.debug("WFS DescribeFeatureType for the URL: %s", wfs_url)

        # forward request to target (without Host Header)
        headers = dict(self.request.headers)
        if wfs_url.hostname != "localhost" and "Host" in headers:
            headers.pop("Host")

        headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist)

        try:
            content, _ = await asyncio.get_event_loop().run_in_executor(
                None, get_http_cached, self.http_options, wfs_url, headers
            )
        except requests.exceptions.RequestException as exception:
            error = (
                f"Unable to get WFS DescribeFeatureType from the URL '{wfs_url.url()}' for "
                f"OGC server {ogc_server_name}, "
                + (
                    f"return the error: {exception.response.status_code} {exception.response.reason}"
                    if exception.response is not None
                    else f"{exception}"
                )
            )
            errors.add(error)
            LOG.exception(error)
            return None, errors
        except Exception:
            error = (
                f"Unable to get WFS DescribeFeatureType from the URL {wfs_url} for "
                f"OGC server {ogc_server_name}"
            )
            errors.add(error)
            LOG.exception(error)
            return None, errors

        if preload:
            return None, errors

        try:
            return lxml.XML(content), errors
        except Exception as e:
            errors.add(
                f"Error '{e!s}' on reading DescribeFeatureType from URL {wfs_url}:\n{content.decode()}"
            )
            return None, errors
コード例 #3
0
ファイル: test_url.py プロジェクト: camptocamp/c2cgeoportal
 def test_url_encode3(self):
     url = Url("http://example.com/toto?%C3%A0=%C3%A9")
     url.add_query({"1": "2"})
     self.assertEqual(url.scheme, "http")
     self.assertEqual(url.netloc, "example.com")
     self.assertEqual(url.path, "/toto")
     self.assertEqual(url.fragment, "")
     self.assertEqual(url.query, {"à": "é", "1": "2"})
コード例 #4
0
ファイル: printproxy.py プロジェクト: camptocamp/c2cgeoportal
 def report_get(self) -> pyramid.response.Response:
     """Get the PDF."""
     url = Url(
         f"{self.request.get_organization_print_url()}/report/{self.request.matchdict.get('ref')}"
     )
     if self.request.registry.settings.get("print_get_redirect", False):
         raise HTTPFound(location=url.url())
     return self._proxy_response("print", url)
コード例 #5
0
ファイル: test_url.py プロジェクト: camptocamp/c2cgeoportal
 def test_url_port(self):
     url = Url("http://example.com:8480/toto")
     url.add_query({"1": "2"})
     self.assertEqual(url.scheme, "http")
     self.assertEqual(url.hostname, "example.com")
     self.assertEqual(url.netloc, "example.com:8480")
     self.assertEqual(url.path, "/toto")
     self.assertEqual(url.fragment, "")
     self.assertEqual(url.query, {"1": "2"})
コード例 #6
0
ファイル: test_url.py プロジェクト: camptocamp/c2cgeoportal
 def test_url(self):
     url = Url("http://test/")
     url.add_query({"Name": "Bob", "Age": "18", "Nationality": "Việt Nam"})
     self.assertEqual(url.scheme, "http")
     self.assertEqual(url.netloc, "test")
     self.assertEqual(url.path, "/")
     self.assertEqual(url.fragment, "")
     self.assertEqual(url.query, {
         "Name": "Bob",
         "Age": "18",
         "Nationality": "Việt Nam"
     })
コード例 #7
0
 def _build_url(self,
                url: Url) -> Tuple[Url, Dict[str, str], Dict[str, Any]]:
     hostname = url.hostname
     host_map = self.config.get("lingua_extractor", {}).get("host_map", {})
     if hostname in host_map:
         map_ = host_map[hostname]
         if "netloc" in map_:
             url.netloc = map_["netloc"]
         if "scheme" in map_:
             url.scheme = map_["scheme"]
         kwargs = {"verify": map_["verify"]} if "verify" in map_ else {}
         return url, map_.get("headers", {}), kwargs
     return url, {}, {}
コード例 #8
0
 def report_create(self) -> pyramid.response.Response:
     """ Create PDF. """
     return self._proxy_response(
         "print",
         Url("{}/report.{}".format(self.config["print_url"],
                                   self.request.matchdict.get("format"))),
     )
コード例 #9
0
 def status(self) -> pyramid.response.Response:
     """ PDF status. """
     return self._proxy_response(
         "print",
         Url("{}/status/{}.json".format(self.config["print_url"],
                                        self.request.matchdict.get("ref"))),
     )
コード例 #10
0
def filter_capabilities(content: str, wms: bool, url: Url, headers: Dict[str,
                                                                         str],
                        request: pyramid.request.Request) -> str:
    """Filter the WMS/WFS capabilities."""

    wms_structure_ = wms_structure(url, headers.get("Host"), request)

    ogc_server_ids = (get_ogc_server_wms_url_ids(request) if wms else
                      get_ogc_server_wfs_url_ids(request)).get(url.url())
    gmf_private_layers = copy.copy(get_private_layers(ogc_server_ids))
    for id_ in list(get_protected_layers(request, ogc_server_ids).keys()):
        if id_ in gmf_private_layers:
            del gmf_private_layers[id_]

    private_layers = set()
    for gmf_layer in list(gmf_private_layers.values()):
        for ogc_layer in gmf_layer.layer.split(","):
            private_layers.add(ogc_layer)
            if ogc_layer in wms_structure_:
                private_layers.update(wms_structure_[ogc_layer])

    parser = defusedxml.expatreader.create_parser(forbid_external=False)
    # skip inclusion of DTDs
    parser.setFeature(xml.sax.handler.feature_external_ges, False)
    parser.setFeature(xml.sax.handler.feature_external_pes, False)

    result = StringIO()
    downstream_handler = XMLGenerator(result, "utf-8")
    filter_handler = _CapabilitiesFilter(parser,
                                         downstream_handler,
                                         "Layer" if wms else "FeatureType",
                                         layers_blacklist=private_layers)
    filter_handler.parse(StringIO(content))  # type: ignore
    return result.getvalue()
コード例 #11
0
 def dev(self) -> pyramid.response.Response:
     path = self.THEME_RE.sub("", self.request.path_info)
     if self.request.path.endswith("/dynamic.js"):
         return HTTPFound(location=self.request.route_url(
             "dynamic", _query=self.request.params))
     return self._proxy_response(
         "dev", Url(f"{self.dev_url.rstrip('/')}/{path.lstrip('/')}"))
コード例 #12
0
ファイル: printproxy.py プロジェクト: camptocamp/c2cgeoportal
    def _capabilities(self, templates: List[str], query_string: Dict[str, str],
                      method: str,
                      referrer: str) -> Tuple[requests.Response, str]:
        del query_string  # Just for caching
        del method  # Just for caching
        del referrer  # Just for caching
        # get URL
        _url = self.request.get_organization_print_url() + "/capabilities.json"

        response = self._proxy(Url(_url))
        response.raise_for_status()

        if self.request.method == "GET":
            try:
                capabilities = response.json()
            except json.decoder.JSONDecodeError:
                LOG.exception("Unable to parse capabilities: %s",
                              response.text)
                raise HTTPBadGateway(response.text)  # pylint: disable=raise-missing-from

            capabilities["layouts"] = list(
                layout for layout in capabilities["layouts"]
                if layout["name"] in templates)

            pretty = self.request.params.get("pretty", "false") == "true"
            content = json.dumps(capabilities,
                                 separators=None if pretty else (",", ":"),
                                 indent=4 if pretty else None)
        else:
            content = ""

        return response, content
コード例 #13
0
ファイル: printproxy.py プロジェクト: camptocamp/c2cgeoportal
 def cancel(self) -> pyramid.response.Response:
     """PDF cancel."""
     return self._proxy_response(
         "print",
         Url(f"{self.request.get_organization_print_url()}/cancel/{self.request.matchdict.get('ref')}"
             ),
     )
コード例 #14
0
ファイル: printproxy.py プロジェクト: camptocamp/c2cgeoportal
 def status(self) -> pyramid.response.Response:
     """PDF status."""
     return self._proxy_response(
         "print",
         Url(f"{self.request.get_organization_print_url()}/status/{self.request.matchdict.get('ref')}.json"
             ),
     )
コード例 #15
0
ファイル: printproxy.py プロジェクト: camptocamp/c2cgeoportal
 def report_create(self) -> pyramid.response.Response:
     """Create PDF."""
     return self._proxy_response(
         "print",
         Url(f"{ self.request.get_organization_print_url()}/report.{self.request.matchdict.get('format')}"
             ),
     )
コード例 #16
0
ファイル: theme.py プロジェクト: camptocamp/c2cgeoportal
 def get_url_internal_wfs(
     self, ogc_server: main.OGCServer, errors: Set[str]
 ) -> Tuple[Optional[Url], Optional[Url], Optional[Url]]:
     # required to do every time to validate the url.
     if ogc_server.auth != main.OGCSERVER_AUTH_NOAUTH:
         url: Optional[Url] = Url(
             self.request.route_url("mapserverproxy", _query={"ogcserver": ogc_server.name})
         )
         url_wfs: Optional[Url] = url
         url_internal_wfs = get_url2(
             f"The OGC server (WFS) '{ogc_server.name}'",
             ogc_server.url_wfs or ogc_server.url,
             self.request,
             errors=errors,
         )
     else:
         url = get_url2(f"The OGC server '{ogc_server.name}'", ogc_server.url, self.request, errors=errors)
         url_wfs = (
             get_url2(
                 f"The OGC server (WFS) '{ogc_server.name}'",
                 ogc_server.url_wfs,
                 self.request,
                 errors=errors,
             )
             if ogc_server.url_wfs is not None
             else url
         )
         url_internal_wfs = url_wfs
     return url_internal_wfs, url, url_wfs
コード例 #17
0
    async def _wms_get_features_type(
            self,
            wfs_url: Url,
            preload: bool = False) -> Tuple[Optional[etree.Element], Set[str]]:
        errors = set()

        wfs_url.add_query({
            "SERVICE": "WFS",
            "VERSION": "1.0.0",
            "REQUEST": "DescribeFeatureType",
            "ROLE_ID": "0",
            "USER_ID": "0",
        })

        LOG.debug("WFS DescribeFeatureType for base URL: %s", wfs_url)

        # forward request to target (without Host Header)
        headers = dict(self.request.headers)
        if wfs_url.hostname != "localhost" and "Host" in headers:
            headers.pop("Host")

        try:
            response = await asyncio.get_event_loop().run_in_executor(
                None, get_http_cached, self.http_options, wfs_url, headers)
        except Exception:
            errors.add("Unable to get DescribeFeatureType from URL {}".format(
                wfs_url))
            return None, errors

        if not response.ok:
            errors.add(
                "DescribeFeatureType from URL {} return the error: {:d} {}".
                format(wfs_url, response.status_code, response.reason))
            return None, errors

        if preload:
            return None, errors

        try:
            return lxml.XML(response.text.encode("utf-8")), errors
        except Exception as e:
            errors.add(
                "Error '{}' on reading DescribeFeatureType from URL {}:\n{}".
                format(str(e), wfs_url, response.text))
            return None, errors
コード例 #18
0
def wms_structure(wms_url: Url, host: str,
                  request: pyramid.request.Request) -> Dict[str, List[str]]:
    """Get a simple serializable structure of the WMS capabilities."""
    url = wms_url.clone().add_query({
        "SERVICE": "WMS",
        "VERSION": "1.1.1",
        "REQUEST": "GetCapabilities"
    })

    # Forward request to target (without Host Header)
    headers = {}
    if url.hostname == "localhost" and host is not None:
        headers["Host"] = host
    try:
        response = requests.get(url.url(),
                                headers=headers,
                                **request.registry.settings.get(
                                    "http_options", {}))
    except Exception:
        LOG.exception("Unable to GetCapabilities from wms_url '%s'", wms_url)
        raise HTTPBadGateway(  # pylint: disable=raise-missing-from
            "Unable to GetCapabilities, see logs for details")

    if not response.ok:
        raise HTTPBadGateway(
            f"GetCapabilities from wms_url {url.url()} return the error: "
            f"{response.status_code:d} {response.reason}")

    try:
        wms = WebMapService(None, xml=response.content)
        result: Dict[str, List[str]] = {}

        def _fill(name: str, parent: ContentMetadata) -> None:
            if parent is None:
                return
            if parent.name not in result:
                result[parent.name] = []
            result[parent.name].append(name)
            _fill(name, parent.parent)

        for layer in list(wms.contents.values()):
            _fill(layer.name, layer.parent)
        return result

    except AttributeError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
        LOG.exception(error)
        raise HTTPBadGateway(error)  # pylint: disable=raise-missing-from

    except SyntaxError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
        LOG.exception(error)
        raise HTTPBadGateway(error)  # pylint: disable=raise-missing-from
コード例 #19
0
ファイル: pdfreport.py プロジェクト: maltaesousa/c2cgeoportal
    def _do_print(self, spec: Dict[str, Any]) -> pyramid.response.Response:
        """ Create and get report PDF. """

        headers = dict(self.request.headers)
        headers["Content-Type"] = "application/json"
        response = self._proxy(
            Url("{0!s}/buildreport.{1!s}".format(self.config["print_url"], spec["outputFormat"])),
            method="POST",
            body=dumps(spec).encode("utf-8"),
            headers=headers,
        )

        return self._build_response(response, response.content, Cache.NO, "pdfreport")
コード例 #20
0
    def _proxy_callback(
        self, cache_control: Cache, url: Url, params: Dict[str, str], **kwargs: Any
    ) -> Response:
        if self.request.matched_route.name.endswith("_path"):
            if self.request.matchdict["path"] == ("favicon.ico",):
                return HTTPFound("/favicon.ico")
            url = url.clone()
            url.path = self.request.path

        response = self._proxy(url=url, params=params, **kwargs)

        content = response.content
        if self.lower_params.get("request") == "getcapabilities":
            content = filter_capabilities(
                response.text,
                self.lower_params.get("service") == "wms",
                url,
                self.request.headers,
                self.request,
            ).encode("utf-8")

        content_type = response.headers["Content-Type"]

        return self._build_response(response, content, cache_control, "mapserver", content_type=content_type)
コード例 #21
0
def wms_structure(wms_url: Url, host: str, request: pyramid.request.Request) -> Dict[str, List[str]]:
    url = wms_url.clone().add_query({"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})

    # Forward request to target (without Host Header)
    headers = dict()
    if url.hostname == "localhost" and host is not None:
        headers["Host"] = host
    try:
        response = requests.get(
            url.url(), headers=headers, **request.registry.settings.get("http_options", {})
        )
    except Exception:
        raise HTTPBadGateway("Unable to GetCapabilities from wms_url {}".format(wms_url))

    if not response.ok:
        raise HTTPBadGateway(
            "GetCapabilities from wms_url {} return the error: {:d} {}".format(
                url.url(), response.status_code, response.reason
            )
        )

    try:
        wms = WebMapService(None, xml=response.content)
        result: Dict[str, List[str]] = {}

        def _fill(name: str, parent: ContentMetadata) -> None:
            if parent is None:
                return
            if parent.name not in result:
                result[parent.name] = []
            result[parent.name].append(name)
            _fill(name, parent.parent)

        for layer in list(wms.contents.values()):
            _fill(layer.name, layer.parent)
        return result

    except AttributeError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
        LOG.exception(error)
        raise HTTPBadGateway(error)

    except SyntaxError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
        LOG.exception(error)
        raise HTTPBadGateway(error)
コード例 #22
0
def filter_wfst_capabilities(content: str, wfs_url: Url,
                             request: pyramid.request.Request) -> str:
    """Filter the WTS capabilities."""

    writable_layers: Set[str] = set()
    ogc_server_ids = get_ogc_server_wfs_url_ids(request).get(wfs_url.url())

    for gmf_layer in list(
            get_writable_layers(request, ogc_server_ids).values()):
        writable_layers |= set(gmf_layer.layer.split(","))

    parser = defusedxml.expatreader.create_parser(forbid_external=False)
    # skip inclusion of DTDs
    parser.setFeature(xml.sax.handler.feature_external_ges, False)
    parser.setFeature(xml.sax.handler.feature_external_pes, False)

    result = StringIO()
    downstream_handler = XMLGenerator(result, "utf-8")
    filter_handler = _CapabilitiesFilter(parser,
                                         downstream_handler,
                                         "FeatureType",
                                         layers_whitelist=writable_layers)
    filter_handler.parse(StringIO(content))  # type: ignore
    return result.getvalue()
コード例 #23
0
ファイル: pdfreport.py プロジェクト: camptocamp/c2cgeoportal
    def get_report(self) -> pyramid.response.Response:
        self.layername = self.request.matchdict["layername"]
        layer_config = self.config["layers"].get(self.layername)

        multiple = layer_config.get("multiple", False)
        ids = self.request.matchdict["ids"]
        if multiple:
            ids = ids.split(",")

        if layer_config is None:
            raise HTTPBadRequest("Layer not found")

        features_ids = ([self.layername + "." + id_ for id_ in ids]
                        if multiple else [self.layername + "." + ids])

        if layer_config["check_credentials"]:
            # FIXME: support of mapserver groups
            ogc_server = (models.DBSession.query(main.OGCServer).filter(
                main.OGCServer.name == layer_config["ogc_server"]).one())
            ogc_server_ids = [ogc_server]

            private_layers_object = get_private_layers(ogc_server_ids)
            private_layers_names = [
                private_layers_object[oid].name
                for oid in private_layers_object
            ]

            protected_layers_object = get_protected_layers(
                self.request.user, ogc_server_ids)
            protected_layers_names = [
                protected_layers_object[oid].name
                for oid in protected_layers_object
            ]

            if self.layername in private_layers_names and self.layername not in protected_layers_names:
                raise HTTPForbidden

        srs = layer_config["srs"]

        mapserv_url = self.request.route_url(
            "mapserverproxy", _query={"ogcserver": layer_config["ogc_server"]})
        url = Url(mapserv_url)
        url.add_query({
            "service": "WFS",
            "version": "1.1.0",
            "outputformat": "gml3",
            "request": "GetFeature",
            "typeName": self.layername,
            "featureid": ",".join(features_ids),
            "srsName": srs,
        })
        vector_request_url = url.url()

        spec = layer_config["spec"]
        if spec is None:
            spec = {
                "layout": self.layername,
                "outputFormat": "pdf",
                "attributes": {
                    "ids": [{
                        "id": id_
                    } for id_ in ids]
                } if multiple else {
                    "id": id
                },
            }
            map_config = layer_config.get("map")
            if map_config is not None:
                spec["attributes"]["map"] = self._build_map(
                    mapserv_url, vector_request_url, srs, map_config)

            maps_config = layer_config.get("maps")
            if maps_config is not None:
                spec["attributes"]["maps"] = []
                for map_config in maps_config:
                    spec["attributes"]["maps"].append(
                        self._build_map(mapserv_url, vector_request_url, srs,
                                        map_config))
        else:
            datasource = layer_config.get("datasource", True)
            if multiple and datasource:
                data = dumps(layer_config["data"])
                data_list = [
                    loads(
                        data % {
                            "layername": self.layername,
                            "id": id_,
                            "srs": srs,
                            "mapserv_url": mapserv_url,
                            "vector_request_url": vector_request_url,
                        }) for id_ in ids
                ]
                self.walker(spec, "%(datasource)s", data_list)
                spec = loads(
                    dumps(spec) % {
                        "layername": self.layername,
                        "srs": srs,
                        "mapserv_url": mapserv_url,
                        "vector_request_url": vector_request_url,
                    })
            elif multiple:
                spec = loads(
                    dumps(spec) % {
                        "layername": self.layername,
                        "ids": ",".join(ids),
                        "srs": srs,
                        "mapserv_url": mapserv_url,
                        "vector_request_url": vector_request_url,
                    })
            else:
                spec = loads(
                    dumps(spec) % {
                        "layername": self.layername,
                        "id": ids,
                        "srs": srs,
                        "mapserv_url": mapserv_url,
                        "vector_request_url": vector_request_url,
                    })

        return self._do_print(spec)
コード例 #24
0
ファイル: proxy.py プロジェクト: camptocamp/c2cgeoportal
    def _proxy(
        self,
        url: Url,
        params: Optional[Dict[str, str]] = None,
        method: Optional[str] = None,
        cache: bool = False,
        body: Optional[bytes] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> requests.models.Response:
        # Get query string
        params = dict(self.request.params) if params is None else params
        url = url.clone().add_query(params, True)

        LOG.debug("Send query to URL:\n%s.", url)

        if method is None:
            method = self.request.method

        headers = dict(self.request.headers if headers is None else headers)

        # Forward request to target (without Host Header).
        # The original Host will be added back by pyramid.
        if url.hostname not in self.host_forward_host and "Host" in headers:
            headers.pop("Host")

        # Forward the request tracking ID to the other service. This will allow to follow the logs belonging
        # to a single request coming from the user
        headers.setdefault("X-Request-ID", self.request.c2c_request_id)
        # If we really want to respect the specification, we should chain with the content of the previous
        # proxy, see also:
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
        forwarded = {
            "for": self.request.client_addr,
            "proto": self.request.scheme
        }
        if "Host" in self.request.headers:
            forwarded["host"] = self.request.headers["Host"]
        forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
        if "Forwarded" in headers:
            headers["Forwarded"] = ",".join(
                [headers["Forwarded"], forwarded_str])
        else:
            headers["Forwarded"] = forwarded_str
        # Set alternative "X-Forwarded" headers
        for forwarded_elements in reversed(headers["Forwarded"].split(",")):
            for element in forwarded_elements.split(";"):
                key, value = element.split("=")
                header_key = f"X-Forwarded-{key.capitalize()}"
                header_value = headers.get(header_key)
                headers[
                    header_key] = value if header_value is None else ", ".join(
                        [header_value, value])

        if not cache:
            headers["Cache-Control"] = "no-cache"

        if method in ("POST", "PUT") and body is None:
            body = self.request.body

        headers = restrict_headers(headers, self.headers_whitelist,
                                   self.headers_blacklist)

        try:
            if method in ("POST", "PUT"):
                response = requests.request(method,
                                            url.url(),
                                            data=body,
                                            headers=headers,
                                            **self.http_options)
            else:
                response = requests.request(method,
                                            url.url(),
                                            headers=headers,
                                            **self.http_options)
        except Exception:
            errors = [
                "Error '%s' while getting the URL:", "%s", "Method: %s",
                "--- With headers ---", "%s"
            ]
            args1 = [
                sys.exc_info()[0],
                url,
                method,
                "\n".join([
                    f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args1.append(body.decode("utf-8"))
            LOG.exception("\n".join(errors), *args1)

            raise HTTPBadGateway(  # pylint: disable=raise-missing-from
                "Error on backend, See logs for detail")

        if not response.ok:
            errors = [
                "Error '%s' in response of URL:",
                "%s",
                "Status: %d",
                "Method: %s",
                "--- With headers ---",
                "%s",
            ]
            args2: List[Union[str, int]] = [
                response.reason,
                url.url(),
                response.status_code,
                method,
                "\n".join([
                    f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args2.append(body.decode("utf-8"))
            errors += ["--- Return content ---", "%s"]
            args2.append(response.text)
            LOG.error("\n".join(errors), *args2)

            raise exception_response(response.status_code)
        if not response.headers.get("Content-Type", "").startswith("image/"):
            LOG.debug("Get result for URL: %s:\n%s.", url, body)

        return response
コード例 #25
0
ファイル: test_url.py プロジェクト: camptocamp/c2cgeoportal
 def test_url_noparam(self):
     url = Url("http://example.com/")
     url.add_query({})
     self.assertEqual(url.url(), "http://example.com/")
コード例 #26
0
 def cancel(self) -> pyramid.response.Response:
     """ PDF cancel. """
     return self._proxy_response(
         "print",
         Url("{}/cancel/{}".format(self.config["print_url"],
                                   self.request.matchdict.get("ref"))))
コード例 #27
0
ファイル: test_url.py プロジェクト: camptocamp/c2cgeoportal
    def test_url_update(self):
        url = Url("http://test:123/toto?map=123#456")
        url.hostname = "example"
        assert url.netloc == "example:123"
        assert url.port == 123
        assert url.url() == "http://example:123/toto?map=123#456"

        url = Url("http://test:123/toto?map=123#456")
        url.netloc = "example"
        assert url.hostname == "example"
        assert url.port is None
        assert url.url() == "http://example/toto?map=123#456"

        url = Url("http://test:123/toto?map=123#456")
        url.netloc = "example:234"
        assert url.hostname == "example"
        assert url.port == 234
        assert url.url() == "http://example:234/toto?map=123#456"

        url = Url("http://test:123/toto?map=123#456")
        url.port = 345
        assert url.netloc == "test:345"
        assert url.hostname == "test"
        assert url.url() == "http://test:345/toto?map=123#456"

        url = Url("http://test:123/toto?map=123#456")
        url.port = None
        assert url.netloc == "test"
        assert url.hostname == "test"
        assert url.url() == "http://test/toto?map=123#456"
コード例 #28
0
    def _proxy(
        self,
        url: Url,
        params: Dict[str, str] = None,
        method: str = None,
        cache: bool = False,
        body: bytes = None,
        headers: Dict[str, str] = None,
    ) -> pyramid.response.Response:
        # Get query string
        params = dict(self.request.params) if params is None else params
        url = url.clone().add_query(params, True)

        LOG.debug("Send query to URL:\n%s.", url)

        if method is None:
            method = self.request.method

        if headers is None:
            headers = dict(self.request.headers)

        # Forward request to target (without Host Header)
        if url.hostname not in self.host_forward_host and "Host" in headers:
            headers.pop("Host")

        # Forward the request tracking ID to the other service. This will allow to follow the logs belonging
        # to a single request coming from the user
        headers.setdefault("X-Request-ID", self.request.c2c_request_id)
        # If we releay want to respect the specification, we should chain with the content of the previous
        # proxy, see also:
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
        forwarded = {
            "for": self.request.client_addr,
            "proto": self.request.scheme
        }
        if "Host" in self.request.headers:
            forwarded["host"] = self.request.headers["Host"]
        forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
        if "Forwarded" in headers:
            headers["Forwarded"] = ",".join(
                [headers["Forwarded"], forwarded_str])
        else:
            headers["Forwarded"] = forwarded_str

        if not cache:
            headers["Cache-Control"] = "no-cache"

        if method in ("POST", "PUT") and body is None:
            body = self.request.body

        try:
            if method in ("POST", "PUT"):
                response = requests.request(method,
                                            url.url(),
                                            data=body,
                                            headers=headers,
                                            **self.http_options)
            else:
                response = requests.request(method,
                                            url.url(),
                                            headers=headers,
                                            **self.http_options)
        except Exception:
            errors = [
                "Error '%s' while getting the URL:", "%s", "Method: %s",
                "--- With headers ---", "%s"
            ]
            args1 = [
                sys.exc_info()[0],
                url,
                method,
                "\n".join([
                    "{}: {}".format(
                        h,
                        v if h not in ("Authorization", "Cookies") else "***")
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args1.append(body.decode("utf-8"))
            LOG.error("\n".join(errors), *args1, exc_info=True)

            raise HTTPBadGateway("Error on backend, See logs for detail")

        if not response.ok:
            errors = [
                "Error '%s' in response of URL:",
                "%s",
                "Status: %d",
                "Method: %s",
                "--- With headers ---",
                "%s",
            ]
            args2: List[Union[str, int]] = [
                response.reason,
                url.url(),
                response.status_code,
                method,
                "\n".join([
                    "{}: {}".format(
                        h,
                        v if h not in ("Authorization", "Cookies") else "***")
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args2.append(body.decode("utf-8"))
            errors += ["--- Return content ---", "%s"]
            args2.append(response.text)
            LOG.error("\n".join(errors), *args2)

            raise exception_response(response.status_code)

        return response
コード例 #29
0
 def _get_wfs_url(self, errors: Set[str]) -> Optional[Url]:
     return Url(self.settings.get("tinyows_url"))
コード例 #30
0
    def __init__(self, server_iface: qgis.server.QgsServerInterface):
        super().__init__(server_iface)

        self.server_iface = server_iface
        self.initialized = False

        try:
            config.init(
                os.environ.get("GEOMAPFISH_CONFIG",
                               "/etc/qgisserver/geomapfish.yaml"))

            c2cwsgiutils.broadcast.init()

            DBSession = create_session_factory(  # noqa: ignore=N806
                config.get("sqlalchemy_slave.url"),
                config.get_config().get("sqlalchemy", {}))

            if "GEOMAPFISH_OGCSERVER" in os.environ:
                self.single = True
                self.ogcserver_accesscontrol = OGCServerAccessControl(
                    server_iface,
                    os.environ["GEOMAPFISH_OGCSERVER"],
                    os.environ["QGIS_PROJECT_FILE"],
                    config.get("srid"),
                    DBSession,
                )

                LOG.info("Use OGC server named '%s'.",
                         os.environ["GEOMAPFISH_OGCSERVER"])
                self.initialized = True
            elif "GEOMAPFISH_ACCESSCONTROL_CONFIG" in os.environ:
                self.single = False
                self.ogcserver_accesscontrols = {}
                with open(os.environ["GEOMAPFISH_ACCESSCONTROL_CONFIG"],
                          encoding="utf-8") as ac_config_file:
                    ac_config = yaml.safe_load(ac_config_file.read())

                for map_, map_config in ac_config.get("map_config").items():
                    map_config["access_control"] = OGCServerAccessControl(
                        server_iface, map_config["ogc_server"], map_,
                        config.get("srid"), DBSession)
                    self.ogcserver_accesscontrols[map_] = map_config
                LOG.info("Use config '%s'.",
                         os.environ["GEOMAPFISH_ACCESSCONTROL_CONFIG"])
                self.initialized = True
            elif "GEOMAPFISH_ACCESSCONTROL_BASE_URL" in os.environ:
                self.ogcserver_accesscontrols = {}
                single_ogc_server = None
                base_url = Url(os.environ["GEOMAPFISH_ACCESSCONTROL_BASE_URL"])
                session = DBSession()
                try:
                    from c2cgeoportal_commons.models.main import (  # pylint: disable=import-outside-toplevel
                        OGCServer, )

                    for ogcserver in session.query(OGCServer).all():
                        errors: Set[str] = set()
                        url = get_url2(
                            f"The OGC server '{ogcserver.name}'",
                            ogcserver.url,
                            None,
                            errors,
                            config.get_config().get("servers", {}),
                        )

                        if errors:
                            LOG.warning(
                                "Ignoring OGC server '%s', get error on parsing URL:\n%s",
                                ogcserver.name,
                                "\n".join(errors),
                            )
                            continue
                        if url is None:
                            LOG.warning(
                                "Ignoring OGC server '%s', the URL is None",
                                ogcserver.name)
                            continue
                        if (base_url.scheme == url.scheme
                                and base_url.netloc == url.netloc
                                and base_url.path == url.path):
                            query = url.query_lower
                            if "map" not in query:
                                if single_ogc_server is None:
                                    single_ogc_server = ogcserver
                                    LOG.debug(
                                        "OGC server '%s', 'map' is not in the parameters => single server?",
                                        ogcserver.name,
                                    )
                                else:
                                    LOG.error(
                                        "OGC server '%s', 'map' is not in the parameters and we already have a single OCG server '%s'",
                                        ogcserver.name,
                                        single_ogc_server.name,
                                    )
                                continue

                            map_ = url.query_lower["map"]
                            self.ogcserver_accesscontrols[map_] = {
                                "ogcserver":
                                ogcserver.name,
                                "access_control":
                                OGCServerAccessControl(
                                    server_iface,
                                    ogcserver.name,
                                    map_,
                                    config.get("srid"),
                                    DBSession,
                                    ogcserver=ogcserver,
                                ),
                            }
                            LOG.info("OGC server '%s' registered for map",
                                     ogcserver.name)
                        else:
                            LOG.debug(
                                "Ignoring OGC server '%s', Don't match the base URL '%s' and '%s'",
                                ogcserver.name,
                                base_url,
                                url,
                            )
                    if self.ogcserver_accesscontrols and single_ogc_server is not None:
                        if os.environ.get("QGIS_PROJECT_FILE"):
                            LOG.error(
                                "We have OGC servers with and without parameter MAP and a value in QGIS_PROJECT_FILE, fallback to single OGC server mode."
                            )
                            self.ogcserver_accesscontrols = {}
                        else:
                            LOG.error(
                                "We have OGC servers with and without parameter MAP but no value in QGIS_PROJECT_FILE, fallback to multiple OGC server mode."
                            )
                            single_ogc_server = None
                    if single_ogc_server is not None:
                        self.single = True
                        self.ogcserver_accesscontrol = OGCServerAccessControl(
                            server_iface,
                            single_ogc_server.name,
                            os.environ["QGIS_PROJECT_FILE"],
                            config.get("srid"),
                            DBSession,
                            single_ogc_server,
                        )

                        LOG.info("Use OGC server named '%s'.",
                                 single_ogc_server.name)
                    else:
                        self.single = False
                    self.initialized = True
                finally:
                    session.close()
            else:
                LOG.error(
                    "The environment variable 'GEOMAPFISH_OGCSERVER', 'GEOMAPFISH_ACCESSCONTROL_CONFIG' "
                    "or 'GEOMAPFISH_ACCESSCONTROL_BASE_URL' should be defined.",
                )

        except Exception:  # pylint: disable=broad-except
            LOG.error("Cannot setup GeoMapFishAccessControl", exc_info=True)

        server_iface.registerAccessControl(
            self, int(os.environ.get("GEOMAPFISH_POSITION", 100)))