diff --git a/.env.dev b/.env.dev index d79ee720b..fd4b160e1 100644 --- a/.env.dev +++ b/.env.dev @@ -34,7 +34,6 @@ LAYMAN_GS_PATH=/geoserver/ LAYMAN_GS_USER=layman LAYMAN_GS_PASSWORD=laymanpwd LAYMAN_GS_ROLE=LAYMAN_ROLE -LAYMAN_GS_PROXY_BASE_URL=http://localhost:8000/geoserver/ LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE=e58e4774e3dc7d6443ad59a8202c5ee0 LAYMAN_GS_NORMALIZED_RASTER_DIRECTORY=normalized_raster_data_dev diff --git a/.env.test b/.env.test index 083594988..d640a9dde 100644 --- a/.env.test +++ b/.env.test @@ -34,7 +34,6 @@ LAYMAN_GS_PATH=/geoserver/ LAYMAN_GS_USER=layman_test LAYMAN_GS_PASSWORD=laymanpwd LAYMAN_GS_ROLE=LAYMAN_TEST_ROLE -LAYMAN_GS_PROXY_BASE_URL=http://localhost:8000/geoserver/ LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE=e58e4774e3dc7d6443ad59a8202c5ee0 LAYMAN_GS_NORMALIZED_RASTER_DIRECTORY=normalized_raster_data_test @@ -58,7 +57,7 @@ LAYMAN_SKIP_REDIS_LOADING=false # Flask-style server name (domain and port) LAYMAN_SERVER_NAME=layman_test_run_1:8000 -LAYMAN_PROXY_SERVER_NAME=enjoychallenge.tech +LAYMAN_PROXY_SERVER_NAME=localhost:8000 # internal settings LAYMAN_TIMGEN_URL=http://timgen:8080/ diff --git a/CHANGELOG.md b/CHANGELOG.md index b97a691e4..49fc2c449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,15 @@ ## v1.22.0 {release-date} ### Upgrade requirements +- Stop using environment variable `LAYMAN_GS_PROXY_BASE_URL`, it has no effect to Layman anymore. + - GeoServer's [Proxy Base URL](https://docs.geoserver.org/2.21.x/en/user/configuration/globalsettings.html) is now automatically set by Layman on each start. Value is automatically derived from environment variables [`LAYMAN_CLIENT_PUBLIC_URL`](doc/env-settings.md#layman_client_public_url) (protocol), [`LAYMAN_PROXY_SERVER_NAME`](doc/env-settings.md#layman_proxy_server_name) (domain and port), and [`LAYMAN_GS_PATH`](doc/env-settings.md#layman_gs_path) (path). ### Migrations and checks #### Schema migrations #### Data migrations - [#765](https://github.com/LayerManager/layman/issues/765) Fix `issuer_id` value in `users` table that was broken since v1.21.0. - [#765](https://github.com/LayerManager/layman/issues/765) Remove `authn.txt` files from workspace directories. The same information as in `authn.txt` files is saved in prime DB schema. ### Changes -- [#868](https://github.com/LayerManager/layman/issues/868) Endpoint [GET Publications](doc/rest.md#get-publications) respects [HTTP header `X-Forwarded-Prefix`](doc/client-proxy.md#x-forwarded-prefix-http-header) of the request in response in `url` key. +- [#868](https://github.com/LayerManager/layman/issues/868) Endpoints [GET Publications](doc/rest.md#get-publications), [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](doc/rest.md#get-workspace-layers) and [GET Maps](doc/rest.md#get-maps) [GET Workspace Maps](doc/rest.md#get-workspace-maps) respects [HTTP header `X-Forwarded-Prefix`](doc/client-proxy.md#x-forwarded-prefix-http-header) of the request in response in `url` key. Endpoint [GET Workspace Layer](doc/rest.md#get-workspace-layer) respects it in `url`, `thumbnail`.`url`, `metadata`.`comparison_url`, `wms`.`url`, `wfs`.`url` and `style`.`url` keys. Endpoint [GET Workspace Map](doc/rest.md#get-workspace-map) respects it in `url`, `file`.`url`, `thumbnail`.`url` and `metadata`.`comparison_url` keys. - [#880](https://github.com/LayerManager/layman/issues/880) Use Docker Compose v2 (`docker compose`) in Makefile without `compatibility` flag and remove `Makefile_docker-compose_v1` file. Docker containers are named according to Docker Compose v2 and may have different name after upgrade. - [#765](https://github.com/LayerManager/layman/issues/765) Stop saving OAuth2 claims in filesystem, use prime DB schema only. - [#893](https://github.com/LayerManager/layman/issues/893) It is possible to specify logging level by new environment variable [LAYMAN_LOGLEVEL](doc/env-settings.md#LAYMAN_LOGLEVEL). Default level is `INFO`. @@ -733,7 +735,7 @@ There is a critical bug in this release, posting new layer breaks Layman: https: - [#74](https://github.com/LayerManager/layman/issues/74) Layman user and role at GeoServer defined by [LAYMAN_GS_USER](doc/env-settings.md#LAYMAN_GS_USER) and [LAYMAN_GS_ROLE](doc/env-settings.md#LAYMAN_GS_ROLE) are now created automatically on Layman's startup if an only if new environment variable [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD) is provided. There is no need to set [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD) for other reason than automatically creating Layman user and Layman role. - No change is required. If you are migrating existing instance, Layman user and role are already created, so you don't need to set [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD). If this is your first Layman release, [GEOSERVER_ADMIN_PASSWORD](doc/env-settings.md#GEOSERVER_ADMIN_PASSWORD) is set in `.env` files starting with this version, so Layman user and role at GeoServer will be automatically created on startup. - No need to run command `make geoserver-reset-default-datadir` from now on. This command was removed from make options. -- [#62](https://github.com/LayerManager/layman/issues/62) GeoServer [Proxy Base URL](https://docs.geoserver.org/2.21.x/en/user/configuration/globalsettings.html) is now automatically set on Layman's startup according to [LAYMAN_GS_PROXY_BASE_URL](doc/env-settings.md#LAYMAN_GS_PROXY_BASE_URL). If you do not set the variable, value is calculated as [LAYMAN_CLIENT_PUBLIC_URL](doc/env-settings.md#LAYMAN_CLIENT_PUBLIC_URL)+[LAYMAN_GS_PATH](doc/env-settings.md#LAYMAN_GS_PATH). If you set it to empty string, no change of Proxy Base URL will be done on GeoServer side. +- [#62](https://github.com/LayerManager/layman/issues/62) GeoServer [Proxy Base URL](https://docs.geoserver.org/2.21.x/en/user/configuration/globalsettings.html) is now automatically set on Layman's startup according to [LAYMAN_GS_PROXY_BASE_URL](https://github.com/LayerManager/layman/blob/v1.21.1/doc/env-settings.md#LAYMAN_GS_PROXY_BASE_URL). If you do not set the variable, value is calculated as [LAYMAN_CLIENT_PUBLIC_URL](doc/env-settings.md#LAYMAN_CLIENT_PUBLIC_URL)+[LAYMAN_GS_PATH](doc/env-settings.md#LAYMAN_GS_PATH). If you set it to empty string, no change of Proxy Base URL will be done on GeoServer side. - [#83](https://github.com/LayerManager/layman/issues/89) All layers are created as `GEOMETRY` type, so any other type can be added (for example polygons can be added to points). - [#73](https://github.com/LayerManager/layman/issues/73) Layman users are automatically created on GeoServer (either at start up of Layman or when reserved) with separate role and workspace. Username is the same as in Layman, name of role is `"USER_"+username`, name of workspace is the same as username. Read and write permissions for workspace are set according to Layman's authorization (as of now read-everyone-write-everyone or read-everyone-write-owner). - New environment variables [LAYMAN_GS_USER_GROUP_SERVICE](doc/env-settings.md#LAYMAN_GS_USER_GROUP_SERVICE) and [LAYMAN_GS_ROLE_SERVICE](doc/env-settings.md#LAYMAN_GS_ROLE_SERVICE) enable to control which user/group and role services are used at GeoServer. Not setting these variables means to use default services. diff --git a/doc/client-proxy.md b/doc/client-proxy.md index 44c9dd190..d3c7421b6 100644 --- a/doc/client-proxy.md +++ b/doc/client-proxy.md @@ -43,3 +43,23 @@ For example, if you send request to `/layman-client-proxy/rest/publications` wit Currently, value of `X-Forwarded-Prefix` affects following URLs: * [GET Publications](rest.md#get-publications) * `url` key +* [GET Layers](rest.md#get-layers) + * `url` key +* [GET Workspace Layers](rest.md#get-workspace-layers) + * `url` key +* [GET Maps](rest.md#get-maps) + * `url` key +* [GET Workspace Maps](rest.md#get-workspace-maps) + * `url` key +* [GET Workspace Layer](rest.md#get-workspace-layer) + * `url` key + * `wms`.`url` key + * `wfs`.`url` key + * `style`.`url` key + * `thumbnail`.`url` key + * `metadata`.`comparison_url` key +* [GET Workspace Map](rest.md#get-workspace-map) + * `url` key + * `file`.`url` key + * `thumbnail`.`url` key + * `metadata`.`comparison_url` key diff --git a/doc/env-settings.md b/doc/env-settings.md index 7995438e8..b8df14c6f 100644 --- a/doc/env-settings.md +++ b/doc/env-settings.md @@ -167,9 +167,6 @@ Password of [LAYMAN_GS_USER](#LAYMAN_GS_USER). ### LAYMAN_GS_ROLE Name of [GeoServer role](https://docs.geoserver.org/2.21.x/en/user/security/webadmin/ugr.html#edit-role-service) of [LAYMAN_GS_USER](#LAYMAN_GS_USER). The role is used to create explicit [access rule](https://docs.geoserver.org/2.21.x/en/user/security/layer.html) for all layers published by Layman. The LAYMAN_GS_ROLE must be another role than default [`ADMIN`](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/roleservices.html#mapping-roles-to-system-roles) role (defined by `adminRoleName`)! The role will be created automatically if [GEOSERVER_ADMIN_PASSWORD](#GEOSERVER_ADMIN_PASSWORD) is provided. -### LAYMAN_GS_PROXY_BASE_URL -GeoServer [Proxy Base URL](https://docs.geoserver.org/2.21.x/en/user/configuration/globalsettings.html). It is automatically set on Layman's startup. If you do not set the variable, value is calculated as protocol, host, and port of [LAYMAN_CLIENT_PUBLIC_URL](#LAYMAN_CLIENT_PUBLIC_URL) followed by [LAYMAN_GS_PATH](#LAYMAN_GS_PATH). If you set it to empty string, no change of Proxy Base URL will be done on GeoServer side. - ### LAYMAN_GS_USER_GROUP_SERVICE Name of [user/group service](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/usergroupservices.html) used for managing users at GeoServer. If not set (default), the service named `default` is chosen. Usually it's [XML user/group service](https://docs.geoserver.org/2.21.x/en/user/security/usergrouprole/usergroupservices.html#xml-user-group-service). diff --git a/src/layman/__init__.py b/src/layman/__init__.py index b46863c84..075376a09 100644 --- a/src/layman/__init__.py +++ b/src/layman/__init__.py @@ -91,8 +91,7 @@ gs_util.ensure_user_role(settings.LAYMAN_GS_USER, 'ADMIN', settings.GEOSERVER_ADMIN_AUTH) gs_util.ensure_user_role(settings.LAYMAN_GS_USER, settings.LAYMAN_GS_ROLE, settings.GEOSERVER_ADMIN_AUTH) - if settings.LAYMAN_GS_PROXY_BASE_URL != '': - gs_util.ensure_proxy_base_url(settings.LAYMAN_GS_PROXY_BASE_URL, settings.LAYMAN_GS_AUTH) + gs_util.ensure_proxy_base_url(settings.LAYMAN_GS_PROXY_BASE_URL, settings.LAYMAN_GS_AUTH) if not IN_UPGRADE_PROCESS: logger.info(f'Adjusting GeoServer SRS') diff --git a/src/layman/layer/filesystem/thumbnail.py b/src/layman/layer/filesystem/thumbnail.py index 0f6f4f72a..7edc50dbd 100644 --- a/src/layman/layer/filesystem/thumbnail.py +++ b/src/layman/layer/filesystem/thumbnail.py @@ -32,13 +32,14 @@ def ensure_layer_thumbnail_dir(workspace, layername): return thumbnail_dir -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): thumbnail_path = get_layer_thumbnail_path(workspace, layername) if os.path.exists(thumbnail_path): return { 'thumbnail': { 'url': url_for('rest_workspace_layer_thumbnail.get', workspace=workspace, - layername=layername), + layername=layername, + x_forwarded_prefix=x_forwarded_prefix), 'path': os.path.relpath(thumbnail_path, common_util.get_workspace_dir(workspace)) }, '_thumbnail': { diff --git a/src/layman/layer/geoserver/sld.py b/src/layman/layer/geoserver/sld.py index 021b98ded..36aa13ab9 100644 --- a/src/layman/layer/geoserver/sld.py +++ b/src/layman/layer/geoserver/sld.py @@ -41,10 +41,10 @@ def delete_layer(workspace, layername): return result -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): response = get_style_response(workspace, layername, gs_util.headers_sld['1.0.0'], settings.LAYMAN_GS_AUTH) if response and response.status_code == 200: - url = url_for('rest_workspace_layer_style.get', workspace=workspace, layername=layername) + url = url_for('rest_workspace_layer_style.get', workspace=workspace, layername=layername, x_forwarded_prefix=x_forwarded_prefix) info = { 'style': { 'url': url, diff --git a/src/layman/layer/geoserver/util.py b/src/layman/layer/geoserver/util.py index 84c0c62c7..f0d2108b3 100644 --- a/src/layman/layer/geoserver/util.py +++ b/src/layman/layer/geoserver/util.py @@ -19,6 +19,11 @@ def get_gs_proxy_base_url(): return proxy_base_url +def get_gs_proxy_server_url(): + proxy_base_url = f'{settings.LAYMAN_PUBLIC_URL_SCHEME}://{settings.LAYMAN_PROXY_SERVER_NAME}' + return proxy_base_url + + def wms_proxy(wms_url, xml=None, version=None, headers=None): from layman.layer.geoserver.wms import VERSION version = version or VERSION diff --git a/src/layman/layer/geoserver/wfs.py b/src/layman/layer/geoserver/wfs.py index 64e9b9dfa..830668ac9 100644 --- a/src/layman/layer/geoserver/wfs.py +++ b/src/layman/layer/geoserver/wfs.py @@ -9,7 +9,7 @@ from layman import util as layman_util from layman.layer import LAYER_TYPE import requests_util.retry -from .util import get_gs_proxy_base_url, get_external_db_store_name +from .util import get_gs_proxy_server_url, get_external_db_store_name from . import wms FLASK_PROXY_KEY = f'{__name__}:PROXY:{{workspace}}' @@ -58,8 +58,10 @@ def delete_layer(workspace, layername): return {} -def get_wfs_url(workspace, external_url=False): - base_url = get_gs_proxy_base_url() if external_url else settings.LAYMAN_GS_URL +def get_wfs_url(workspace, external_url=False, *, x_forwarded_prefix=None): + assert external_url or not x_forwarded_prefix + x_forwarded_prefix = x_forwarded_prefix or '' + base_url = urljoin(get_gs_proxy_server_url(), x_forwarded_prefix) + settings.LAYMAN_GS_PATH if external_url else settings.LAYMAN_GS_URL return urljoin(base_url, workspace + '/wfs') @@ -122,11 +124,11 @@ def clear_cache(workspace): mem_redis.delete(key) -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): wfs = get_wfs_proxy(workspace) if wfs is None: return {} - wfs_proxy_url = get_wfs_url(workspace, external_url=True) + wfs_proxy_url = get_wfs_url(workspace, external_url=True, x_forwarded_prefix=x_forwarded_prefix) wfs_layername = f"{workspace}:{layername}" if wfs_layername not in wfs.contents: diff --git a/src/layman/layer/geoserver/wms.py b/src/layman/layer/geoserver/wms.py index f5db868cc..569175704 100644 --- a/src/layman/layer/geoserver/wms.py +++ b/src/layman/layer/geoserver/wms.py @@ -14,7 +14,7 @@ from layman.layer import LAYER_TYPE from layman.layer.filesystem import gdal import requests_util.retry -from .util import get_gs_proxy_base_url, get_external_db_store_name +from .util import get_gs_proxy_server_url, get_external_db_store_name FLASK_PROXY_KEY = f'{__name__}:PROXY:{{workspace}}' DEFAULT_WMS_QGIS_STORE_PREFIX = 'qgis' @@ -82,9 +82,11 @@ def delete_layer(workspace, layername): return {} -def get_wms_url(workspace, external_url=False): +def get_wms_url(workspace, external_url=False, *, x_forwarded_prefix=None): + assert external_url or not x_forwarded_prefix + x_forwarded_prefix = x_forwarded_prefix or '' geoserver_workspace = get_geoserver_workspace(workspace) - base_url = get_gs_proxy_base_url() if external_url else settings.LAYMAN_GS_URL + base_url = urljoin(get_gs_proxy_server_url(), x_forwarded_prefix) + settings.LAYMAN_GS_PATH if external_url else settings.LAYMAN_GS_URL return urljoin(base_url, geoserver_workspace + '/ows') @@ -167,11 +169,11 @@ def get_timeregex_props(workspace, layername): return result -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): wms = get_wms_proxy(workspace) if wms is None: return {} - wms_proxy_url = get_wms_url(workspace, external_url=True) + wms_proxy_url = get_wms_url(workspace, external_url=True, x_forwarded_prefix=x_forwarded_prefix) if layername not in wms.contents: return {} diff --git a/src/layman/layer/micka/csw.py b/src/layman/layer/micka/csw.py index 1551ef37c..5d1c07e3f 100644 --- a/src/layman/layer/micka/csw.py +++ b/src/layman/layer/micka/csw.py @@ -28,7 +28,7 @@ post_layer = empty_method -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): uuid = get_layer_uuid(workspace, layername) try: csw = common_util.create_csw() @@ -50,7 +50,8 @@ def get_layer_info(workspace, layername): 'identifier': muuid, 'csw_url': settings.CSW_PROXY_URL, 'record_url': common_util.get_metadata_url(uuid, url_type=common_util.RecordUrlType.BASIC), - 'comparison_url': url_for('rest_workspace_layer_metadata_comparison.get', workspace=workspace, layername=layername), + 'comparison_url': url_for('rest_workspace_layer_metadata_comparison.get', workspace=workspace, layername=layername, + x_forwarded_prefix=x_forwarded_prefix), } } return {} diff --git a/src/layman/layer/micka/util_test_filled_template.xml b/src/layman/layer/micka/util_test_filled_template.xml index 5e826f341..b096fb613 100644 --- a/src/layman/layer/micka/util_test_filled_template.xml +++ b/src/layman/layer/micka/util_test_filled_template.xml @@ -122,7 +122,7 @@ - http://enjoychallenge.tech/rest/workspaces/browser/layers/layer/thumbnail + http://localhost:8000/rest/workspaces/browser/layers/layer/thumbnail PNG @@ -261,7 +261,7 @@ - http://enjoychallenge.tech/rest/workspaces/browser/layers/layer + http://localhost:8000/rest/workspaces/browser/layers/layer WWW:LINK-1.0-http--link diff --git a/src/layman/layer/qgis/wms.py b/src/layman/layer/qgis/wms.py index b5614c331..cce75d718 100644 --- a/src/layman/layer/qgis/wms.py +++ b/src/layman/layer/qgis/wms.py @@ -18,11 +18,11 @@ get_publication_uuid = empty_method_returns_none -def get_layer_info(workspace, layername): +def get_layer_info(workspace, layername, *, x_forwarded_prefix=None): input_file_dir = qgis.get_layer_dir(workspace, layername) result = {} if os.path.exists(input_file_dir): - url = layman_util.url_for('rest_workspace_layer_style.get', workspace=workspace, layername=layername) + url = layman_util.url_for('rest_workspace_layer_style.get', workspace=workspace, layername=layername, x_forwarded_prefix=x_forwarded_prefix) result = { 'name': layername, 'style': { diff --git a/src/layman/layer/rest_layers.py b/src/layman/layer/rest_layers.py index ad11abbe5..c5defe9eb 100644 --- a/src/layman/layer/rest_layers.py +++ b/src/layman/layer/rest_layers.py @@ -1,5 +1,6 @@ from flask import Blueprint, g, request, current_app as app +from layman import util as layman_util from layman.authn import authenticate, get_authn_username from layman.authz import authorize_publications_decorator from layman.common import rest as rest_common @@ -20,4 +21,5 @@ def get(): app.logger.info(f"GET Layers, actor={g.user}") actor = get_authn_username() - return rest_common.get_publications(LAYER_TYPE, actor, request_args=request.args) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + return rest_common.get_publications(LAYER_TYPE, actor, request_args=request.args, x_forwarded_prefix=x_forwarded_prefix) diff --git a/src/layman/layer/rest_workspace_layer.py b/src/layman/layer/rest_workspace_layer.py index 1354eb263..3b4434fa9 100644 --- a/src/layman/layer/rest_workspace_layer.py +++ b/src/layman/layer/rest_workspace_layer.py @@ -37,7 +37,8 @@ def get(workspace, layername): # pylint: disable=unused-argument app.logger.info(f"GET Layer, actor={g.user}") - info = util.get_complete_layer_info(workspace, layername) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + info = util.get_complete_layer_info(workspace, layername, x_forwarded_prefix=x_forwarded_prefix) return jsonify(info), 200 diff --git a/src/layman/layer/rest_workspace_layers.py b/src/layman/layer/rest_workspace_layers.py index cad5f65e9..c9eecd376 100644 --- a/src/layman/layer/rest_workspace_layers.py +++ b/src/layman/layer/rest_workspace_layers.py @@ -32,7 +32,8 @@ def get(workspace): app.logger.info(f"GET Layers, actor={g.user}") actor = get_authn_username() - return rest_common.get_publications(LAYER_TYPE, actor, request_args=request.args, workspace=workspace) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + return rest_common.get_publications(LAYER_TYPE, actor, request_args=request.args, workspace=workspace, x_forwarded_prefix=x_forwarded_prefix) @bp.route(f"/{LAYER_REST_PATH_NAME}", methods=['POST']) diff --git a/src/layman/layer/util.py b/src/layman/layer/util.py index e98079ddb..83dbfbbf9 100644 --- a/src/layman/layer/util.py +++ b/src/layman/layer/util.py @@ -1,6 +1,7 @@ from functools import wraps, partial from urllib import parse import re +import logging import psycopg2 from flask import current_app, request @@ -20,6 +21,7 @@ FLASK_SOURCES_KEY = f'{__name__}:SOURCES' EXTERNAL_TABLE_URI_PATTERN = 'postgresql://:@:/?schema=&table=&geo_column=' +logger = logging.getLogger(__name__) def to_safe_layer_name(value): @@ -82,8 +84,8 @@ def clear_publication_info(layer_info, file_type): return clear_info -def get_complete_layer_info(workspace, layername): - partial_info = get_layer_info(workspace, layername) +def get_complete_layer_info(workspace, layername, *, x_forwarded_prefix=None): + partial_info = get_layer_info(workspace, layername, context={'x_forwarded_prefix': x_forwarded_prefix}) if not any(partial_info): raise LaymanError(15, {'layername': layername}) @@ -98,7 +100,7 @@ def get_complete_layer_info(workspace, layername): complete_info.update({ 'name': layername, - 'url': url_for('rest_workspace_layer.get', layername=layername, workspace=workspace), + 'url': url_for('rest_workspace_layer.get', layername=layername, workspace=workspace, x_forwarded_prefix=x_forwarded_prefix), 'title': layername, 'description': '', }) diff --git a/src/layman/map/filesystem/input_file.py b/src/layman/map/filesystem/input_file.py index df99c6a1f..8e75d5dd2 100644 --- a/src/layman/map/filesystem/input_file.py +++ b/src/layman/map/filesystem/input_file.py @@ -36,7 +36,7 @@ def get_map_file(workspace, mapname): return mapfile_path -def get_map_info(workspace, mapname): +def get_map_info(workspace, mapname, *, x_forwarded_prefix=None): map_file_path = get_map_file(workspace, mapname) result = {} if os.path.exists(map_file_path): @@ -46,7 +46,7 @@ def get_map_info(workspace, mapname): result = { 'file': { 'path': map_file_path, - 'url': url_for('rest_workspace_map_file.get', mapname=mapname, workspace=workspace), + 'url': url_for('rest_workspace_map_file.get', mapname=mapname, workspace=workspace, x_forwarded_prefix=x_forwarded_prefix), }, '_file': { 'url': url_for('rest_workspace_map_file.get', mapname=mapname, workspace=workspace, internal=True), diff --git a/src/layman/map/filesystem/thumbnail.py b/src/layman/map/filesystem/thumbnail.py index 5263b0afb..af57ae44a 100644 --- a/src/layman/map/filesystem/thumbnail.py +++ b/src/layman/map/filesystem/thumbnail.py @@ -35,13 +35,13 @@ def ensure_map_thumbnail_dir(workspace, mapname): return thumbnail_dir -def get_map_info(workspace, mapname): +def get_map_info(workspace, mapname, *, x_forwarded_prefix=None): thumbnail_path = get_map_thumbnail_path(workspace, mapname) if os.path.exists(thumbnail_path): return { 'thumbnail': { 'url': url_for('rest_workspace_map_thumbnail.get', workspace=workspace, - mapname=mapname), + mapname=mapname, x_forwarded_prefix=x_forwarded_prefix), 'path': os.path.relpath(thumbnail_path, common_util.get_workspace_dir(workspace)) }, '_thumbnail': { diff --git a/src/layman/map/micka/csw.py b/src/layman/map/micka/csw.py index b1f4e3951..a48da5de7 100644 --- a/src/layman/map/micka/csw.py +++ b/src/layman/map/micka/csw.py @@ -24,7 +24,7 @@ post_map = empty_method -def get_map_info(workspace, mapname): +def get_map_info(workspace, mapname, *, x_forwarded_prefix=None): uuid = get_map_uuid(workspace, mapname) try: csw = common_util.create_csw() @@ -46,7 +46,8 @@ def get_map_info(workspace, mapname): 'identifier': muuid, 'csw_url': settings.CSW_PROXY_URL, 'record_url': common_util.get_metadata_url(uuid, url_type=common_util.RecordUrlType.BASIC), - 'comparison_url': url_for('rest_workspace_map_metadata_comparison.get', workspace=workspace, mapname=mapname), + 'comparison_url': url_for('rest_workspace_map_metadata_comparison.get', workspace=workspace, mapname=mapname, + x_forwarded_prefix=x_forwarded_prefix), } } return {} diff --git a/src/layman/map/micka/record-template-filled.xml b/src/layman/map/micka/record-template-filled.xml index e7be709e2..b1ac61cae 100644 --- a/src/layman/map/micka/record-template-filled.xml +++ b/src/layman/map/micka/record-template-filled.xml @@ -71,7 +71,7 @@ - svet + svet @@ -111,7 +111,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet/thumbnail + http://localhost:8000/rest/workspaces/testuser1/maps/svet/thumbnail PNG @@ -197,7 +197,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet + http://localhost:8000/rest/workspaces/testuser1/maps/svet WWW:LINK-1.0-http--link @@ -210,7 +210,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet/file + http://localhost:8000/rest/workspaces/testuser1/maps/svet/file WWW:LINK-1.0-http--link diff --git a/src/layman/map/micka/util_test_filled_template.xml b/src/layman/map/micka/util_test_filled_template.xml index d9aceae6e..5d92359cb 100644 --- a/src/layman/map/micka/util_test_filled_template.xml +++ b/src/layman/map/micka/util_test_filled_template.xml @@ -117,7 +117,7 @@ - http://enjoychallenge.tech/rest/workspaces/browser/maps/map/thumbnail + http://localhost:8000/rest/workspaces/browser/maps/map/thumbnail PNG @@ -201,7 +201,7 @@ - http://enjoychallenge.tech/rest/workspaces/browser/maps/map + http://localhost:8000/rest/workspaces/browser/maps/map WWW:LINK-1.0-http--link @@ -214,7 +214,7 @@ - http://enjoychallenge.tech/rest/workspaces/browser/maps/map/file + http://localhost:8000/rest/workspaces/browser/maps/map/file WWW:LINK-1.0-http--link diff --git a/src/layman/map/rest_maps.py b/src/layman/map/rest_maps.py index 0b28d0865..ca2437382 100644 --- a/src/layman/map/rest_maps.py +++ b/src/layman/map/rest_maps.py @@ -1,5 +1,6 @@ from flask import Blueprint, g, request, current_app as app +from layman import util as layman_util from layman.authn import authenticate, get_authn_username from layman.authz import authorize_publications_decorator from layman.common import rest as rest_common @@ -20,4 +21,5 @@ def get(): app.logger.info(f"GET Maps, actor={g.user}") actor = get_authn_username() - return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args, x_forwarded_prefix=x_forwarded_prefix) diff --git a/src/layman/map/rest_test_filled_template.xml b/src/layman/map/rest_test_filled_template.xml index 89ecda87c..4c9e4bb34 100644 --- a/src/layman/map/rest_test_filled_template.xml +++ b/src/layman/map/rest_test_filled_template.xml @@ -71,7 +71,7 @@ - svet + svet @@ -111,7 +111,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet/thumbnail + http://localhost:8000/rest/workspaces/testuser1/maps/svet/thumbnail PNG @@ -197,7 +197,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet + http://localhost:8000/rest/workspaces/testuser1/maps/svet WWW:LINK-1.0-http--link @@ -210,7 +210,7 @@ - http://enjoychallenge.tech/rest/workspaces/testuser1/maps/svet/file + http://localhost:8000/rest/workspaces/testuser1/maps/svet/file WWW:LINK-1.0-http--link diff --git a/src/layman/map/rest_workspace_map.py b/src/layman/map/rest_workspace_map.py index 33fafe972..df0da5ce5 100644 --- a/src/layman/map/rest_workspace_map.py +++ b/src/layman/map/rest_workspace_map.py @@ -35,7 +35,8 @@ def get(workspace, mapname): # pylint: disable=unused-argument app.logger.info(f"GET Map, actor={g.user}") - info = util.get_complete_map_info(workspace, mapname) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + info = util.get_complete_map_info(workspace, mapname, x_forwarded_prefix=x_forwarded_prefix) return jsonify(info), 200 diff --git a/src/layman/map/rest_workspace_maps.py b/src/layman/map/rest_workspace_maps.py index 8ec3d61c5..6c72a9a21 100644 --- a/src/layman/map/rest_workspace_maps.py +++ b/src/layman/map/rest_workspace_maps.py @@ -35,7 +35,8 @@ def get(workspace): app.logger.info(f"GET Maps, actor={g.user}") actor = get_authn_username() - return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args, workspace=workspace) + x_forwarded_prefix = layman_util.get_x_forwarded_prefix(request.headers) + return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args, workspace=workspace, x_forwarded_prefix=x_forwarded_prefix) @bp.route(f"/{MAP_REST_PATH_NAME}", methods=['POST']) diff --git a/src/layman/map/util.py b/src/layman/map/util.py index 69e07cf1b..2b627cb44 100644 --- a/src/layman/map/util.py +++ b/src/layman/map/util.py @@ -132,8 +132,8 @@ def clear_publication_info(layer_info): return clear_info -def get_complete_map_info(workspace, mapname): - partial_info = get_map_info(workspace, mapname) +def get_complete_map_info(workspace, mapname, *, x_forwarded_prefix=None): + partial_info = get_map_info(workspace, mapname, context={'x_forwarded_prefix': x_forwarded_prefix}) if not any(partial_info): raise LaymanError(26, {'mapname': mapname}) @@ -142,7 +142,7 @@ def get_complete_map_info(workspace, mapname): complete_info = { 'name': mapname, - 'url': url_for('rest_workspace_map.get', mapname=mapname, workspace=workspace), + 'url': url_for('rest_workspace_map.get', mapname=mapname, workspace=workspace, x_forwarded_prefix=x_forwarded_prefix), 'title': mapname, 'description': '', } diff --git a/src/layman/util.py b/src/layman/util.py index d8df40692..182c132af 100644 --- a/src/layman/util.py +++ b/src/layman/util.py @@ -277,7 +277,7 @@ def call_modules_fn(modules, fn_name, args=None, kwargs=None, omit_duplicate_cal DUMB_MAP_ADAPTER_DICT = {} -def url_for(endpoint, *, internal=False, x_forwarded_prefix=None, **values): +def _url_for(endpoint, *, server_name, proxy_server_name, internal=False, x_forwarded_prefix=None, **values): assert not (internal and values.get('_external')) assert not (internal and x_forwarded_prefix) x_forwarded_prefix = x_forwarded_prefix or '' @@ -285,21 +285,28 @@ def url_for(endpoint, *, internal=False, x_forwarded_prefix=None, **values): assert (current_app.config.get('SERVER_NAME', None) is not None) == (current_app.config['TESTING'] is True) # Flask does not accept SERVER_NAME without dot, and without SERVER_NAME url_for cannot be used # therefore DUMB_MAP_ADAPTER_DICT is created manually ... - dumb_map_adapter = DUMB_MAP_ADAPTER_DICT.get(x_forwarded_prefix) + dict_key = f"{proxy_server_name} {x_forwarded_prefix}" + dumb_map_adapter = DUMB_MAP_ADAPTER_DICT.get(dict_key) if dumb_map_adapter is None: dumb_map_adapter = current_app.url_map.bind( - settings.LAYMAN_PROXY_SERVER_NAME + x_forwarded_prefix, + proxy_server_name + x_forwarded_prefix, url_scheme=current_app.config['PREFERRED_URL_SCHEME'] ) - DUMB_MAP_ADAPTER_DICT[x_forwarded_prefix] = dumb_map_adapter + DUMB_MAP_ADAPTER_DICT[dict_key] = dumb_map_adapter result = dumb_map_adapter.build(endpoint, values=values, force_external=True) if internal: _, netloc, path, query, fragment = urllib.parse.urlsplit(result) - netloc = settings.LAYMAN_SERVER_NAME + netloc = server_name result = urllib.parse.urlunsplit(('http', netloc, path, query, fragment)) return result +def url_for(endpoint, *, internal=False, x_forwarded_prefix=None, **values): + return _url_for(endpoint, server_name=settings.LAYMAN_SERVER_NAME, + proxy_server_name=settings.LAYMAN_PROXY_SERVER_NAME, + internal=internal, x_forwarded_prefix=x_forwarded_prefix, **values) + + def get_internal_sources(publ_type): return get_modules_from_names(get_publication_types()[publ_type]['internal_sources']) @@ -348,6 +355,7 @@ def get_publication_info(workspace, publ_type, publ_name, context=None): }[publ_type] partial_infos = call_modules_fn(sources, info_method, [workspace, publ_name], kwargs={ 'extra_keys': context.get('extra_keys', []), + 'x_forwarded_prefix': context.get('x_forwarded_prefix'), }) result = {} diff --git a/src/layman/util_test.py b/src/layman/util_test.py index 2d372e637..3535f809c 100644 --- a/src/layman/util_test.py +++ b/src/layman/util_test.py @@ -108,12 +108,29 @@ def test_publication_interface_methods(): @pytest.mark.parametrize('endpoint, internal, params, expected_url', [ ('rest_workspace_maps.get', False, {'workspace': 'workspace_name'}, - f'http://enjoychallenge.tech/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/maps'), + f'http://localhost:8000/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/maps'), ('rest_workspace_layers.get', False, {'workspace': 'workspace_name'}, - f'http://enjoychallenge.tech/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/layers'), + f'http://localhost:8000/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/layers'), ('rest_about.get_version', True, {}, 'http://layman_test_run_1:8000/rest/about/version'), - ('rest_about.get_version', False, {}, 'http://enjoychallenge.tech/rest/about/version'), + ('rest_about.get_version', False, {}, 'http://localhost:8000/rest/about/version'), ]) def test_url_for(endpoint, internal, params, expected_url): with app.app_context(): assert util.url_for(endpoint, internal=internal, **params) == expected_url + + +@pytest.mark.parametrize('endpoint, internal, params, expected_url', [ + ('rest_workspace_maps.get', False, {'workspace': 'workspace_name'}, + f'http://enjoychallenge.tech/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/maps'), + ('rest_workspace_layers.get', False, {'workspace': 'workspace_name'}, + f'http://enjoychallenge.tech/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/layers'), + ('rest_about.get_version', True, {}, 'http://layman:8000/rest/about/version'), + ('rest_about.get_version', False, {}, 'http://enjoychallenge.tech/rest/about/version'), +]) +def test__url_for(endpoint, internal, params, expected_url): + server_name = 'layman:8000' + proxy_server_name = 'enjoychallenge.tech' + with app.app_context(): + # pylint: disable=protected-access + assert util._url_for(endpoint, server_name=server_name, proxy_server_name=proxy_server_name, internal=internal, + **params) == expected_url diff --git a/src/layman_settings.py b/src/layman_settings.py index 156e01ae2..930b29a36 100644 --- a/src/layman_settings.py +++ b/src/layman_settings.py @@ -239,7 +239,8 @@ class EnumWfsWmsStatus(Enum): LAYMAN_SERVER_NAME = os.environ['LAYMAN_SERVER_NAME'] LAYMAN_PROXY_SERVER_NAME = os.environ['LAYMAN_PROXY_SERVER_NAME'] -LAYMAN_GS_PROXY_BASE_URL = os.getenv('LAYMAN_GS_PROXY_BASE_URL', urljoin(LAYMAN_CLIENT_PUBLIC_URL, LAYMAN_GS_PATH)) +LAYMAN_PUBLIC_URL_SCHEME = urlparse(LAYMAN_CLIENT_PUBLIC_URL).scheme +LAYMAN_GS_PROXY_BASE_URL = urljoin(f'{LAYMAN_PUBLIC_URL_SCHEME}://{LAYMAN_PROXY_SERVER_NAME}', LAYMAN_GS_PATH) CSW_URL = os.getenv('CSW_URL', None) CSW_PROXY_URL = os.getenv('CSW_PROXY_URL', None) @@ -256,8 +257,6 @@ class EnumWfsWmsStatus(Enum): MICKA_ACCEPTED_VERSION = MICKA_REGEXP_MATCH.groups() micka.set_settings(DEFAULT_CONNECTION_TIMEOUT) -LAYMAN_PUBLIC_URL_SCHEME = urlparse(LAYMAN_CLIENT_PUBLIC_URL).scheme - REST_USERS_PREFIX = 'users' REST_WORKSPACES_PREFIX = 'workspaces' REST_PUBLICATIONS_PREFIX = 'publications' diff --git a/test_tools/data/micka/rest_test_filled_raster_template.xml b/test_tools/data/micka/rest_test_filled_raster_template.xml index cf1705dcc..e0d1d579d 100644 --- a/test_tools/data/micka/rest_test_filled_raster_template.xml +++ b/test_tools/data/micka/rest_test_filled_raster_template.xml @@ -148,7 +148,7 @@ - post_tif_rgba_4326 + post_tif_rgba_4326 @@ -180,7 +180,7 @@ - http://enjoychallenge.tech/rest/workspaces/test_workspace/layers/post_tif_rgba_4326/thumbnail + http://localhost:8000/rest/workspaces/test_workspace/layers/post_tif_rgba_4326/thumbnail PNG @@ -286,7 +286,7 @@ - http://enjoychallenge.tech/rest/workspaces/test_workspace/layers/post_tif_rgba_4326 + http://localhost:8000/rest/workspaces/test_workspace/layers/post_tif_rgba_4326 WWW:LINK-1.0-http--link diff --git a/test_tools/data/micka/rest_test_filled_template.xml b/test_tools/data/micka/rest_test_filled_template.xml index 0c7f89185..ab14b3af5 100644 --- a/test_tools/data/micka/rest_test_filled_template.xml +++ b/test_tools/data/micka/rest_test_filled_template.xml @@ -148,7 +148,7 @@ - test_publications_same_name_publ + test_publications_same_name_publ @@ -180,7 +180,7 @@ - http://enjoychallenge.tech/rest/workspaces/test_workspace_1/layers/test_publications_same_name_publ/thumbnail + http://localhost:8000/rest/workspaces/test_workspace_1/layers/test_publications_same_name_publ/thumbnail PNG @@ -347,7 +347,7 @@ - http://enjoychallenge.tech/rest/workspaces/test_workspace_1/layers/test_publications_same_name_publ + http://localhost:8000/rest/workspaces/test_workspace_1/layers/test_publications_same_name_publ WWW:LINK-1.0-http--link diff --git a/tests/asserts/final/publication/__init__.py b/tests/asserts/final/publication/__init__.py index 5f89b8ec0..dc7edd9aa 100644 --- a/tests/asserts/final/publication/__init__.py +++ b/tests/asserts/final/publication/__init__.py @@ -9,6 +9,7 @@ Action(internal_rest.same_values_in_internal_and_rest, {}), Action(rest.same_values_in_detail_and_multi, {}), Action(rest.multi_url_with_x_forwarded_prefix, {}), + Action(rest.get_layer_with_x_forwarded_prefix, {}), Action(internal.same_value_of_key_in_all_sources, {}), Action(internal.mandatory_keys_in_all_sources, {}), Action(internal.all_keys_assigned_to_source, {}), @@ -34,6 +35,7 @@ Action(internal_rest.same_values_in_internal_and_rest, {}), Action(rest.same_values_in_detail_and_multi, {}), Action(rest.multi_url_with_x_forwarded_prefix, {}), + Action(rest.get_map_with_x_forwarded_prefix, {}), Action(internal.same_value_of_key_in_all_sources, {}), Action(internal.mandatory_keys_in_all_sources, {}), Action(internal.all_keys_assigned_to_source, {}), diff --git a/tests/asserts/final/publication/internal.py b/tests/asserts/final/publication/internal.py index d7c848ecd..8ab954d85 100644 --- a/tests/asserts/final/publication/internal.py +++ b/tests/asserts/final/publication/internal.py @@ -218,7 +218,7 @@ def correct_values_in_detail(workspace, publ_type, name, *, exp_publication_deta table_uri = external_table_uri util.recursive_dict_update(expected_detail, { - 'wfs': {'url': f'http://localhost:8000/geoserver/{workspace}/wfs'}, + 'wfs': {'url': f'{settings.LAYMAN_GS_PROXY_BASE_URL}{workspace}/wfs'}, '_table_uri': table_uri, 'db': { 'external_uri': layer_util.redact_uri(table_uri.db_uri_str), @@ -238,7 +238,7 @@ def correct_values_in_detail(workspace, publ_type, name, *, exp_publication_deta ) util.recursive_dict_update(expected_detail, { - 'wfs': {'url': f'http://localhost:8000/geoserver/{workspace}/wfs'}, + 'wfs': {'url': f'{settings.LAYMAN_GS_PROXY_BASE_URL}{workspace}/wfs'}, '_file': {'file_type': 'vector'}, 'db': { 'schema': workspace, diff --git a/tests/asserts/final/publication/rest.py b/tests/asserts/final/publication/rest.py index aff3bb7bf..11db69e02 100644 --- a/tests/asserts/final/publication/rest.py +++ b/tests/asserts/final/publication/rest.py @@ -4,6 +4,8 @@ from layman import settings from test_tools import process_client, util as test_util +PROXY_PREFIX = '/layman-proxy' + def is_complete_in_rest(rest_publication_detail): assert 'layman_metadata' in rest_publication_detail, f'rest_publication_detail={rest_publication_detail}' @@ -72,7 +74,7 @@ def same_values_in_detail_and_multi(workspace, publ_type, name, rest_publication def multi_url_with_x_forwarded_prefix(workspace, publ_type, name, headers, ): - proxy_prefix = '/layman-proxy' + proxy_prefix = PROXY_PREFIX headers = { **(headers or {}), 'X-Forwarded-Prefix': proxy_prefix, @@ -80,6 +82,8 @@ def multi_url_with_x_forwarded_prefix(workspace, publ_type, name, headers, ): short_publ_type = publ_type.split('.')[1] multi_requests = [ (process_client.get_publications, [None], {'headers': headers}), + (process_client.get_publications, [publ_type], {'headers': headers}), + (process_client.get_publications, [publ_type], {'workspace': workspace, 'headers': headers}), ] for rest_multi_method, args, kwargs in multi_requests: rest_multi_response_json = rest_multi_method(*args, **kwargs) @@ -88,3 +92,34 @@ def multi_url_with_x_forwarded_prefix(workspace, publ_type, name, headers, ): == short_publ_type)) url = rest_multi_info['url'] assert url == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/{short_publ_type}s/{name}' + + +def get_layer_with_x_forwarded_prefix(workspace, name, headers, ): + proxy_prefix = PROXY_PREFIX + headers = { + **(headers or {}), + 'X-Forwarded-Prefix': proxy_prefix, + } + rest_layer_info = process_client.get_workspace_layer(workspace, name, headers=headers) + assert rest_layer_info['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/layers/{name}' + assert rest_layer_info['thumbnail']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/layers/{name}/thumbnail' + assert rest_layer_info['metadata']['comparison_url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/layers/{name}/metadata-comparison' + assert rest_layer_info['wms']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/geoserver/{workspace}_wms/ows' + geodata_type = rest_layer_info['geodata_type'] + if geodata_type == settings.GEODATA_TYPE_VECTOR: + assert rest_layer_info['wfs']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/geoserver/{workspace}/wfs' + assert rest_layer_info['sld']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/layers/{name}/style' + assert rest_layer_info['style']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/layers/{name}/style' + + +def get_map_with_x_forwarded_prefix(workspace, name, headers, ): + proxy_prefix = PROXY_PREFIX + headers = { + **(headers or {}), + 'X-Forwarded-Prefix': proxy_prefix, + } + rest_map_info = process_client.get_workspace_map(workspace, name, headers=headers) + assert rest_map_info['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/maps/{name}' + assert rest_map_info['file']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/maps/{name}/file' + assert rest_map_info['thumbnail']['url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/maps/{name}/thumbnail' + assert rest_map_info['metadata']['comparison_url'] == f'http://{settings.LAYMAN_PROXY_SERVER_NAME}{proxy_prefix}/rest/workspaces/{workspace}/maps/{name}/metadata-comparison'