From cfcda2e9bd223eeb407002889b6e10fa48d6863c Mon Sep 17 00:00:00 2001 From: Martin Varga Date: Thu, 8 Jan 2026 10:00:08 +0100 Subject: [PATCH 1/6] Update docker images for EE to use dockerhub registry --- deployment/enterprise/README.md | 13 ++++--------- deployment/enterprise/docker-compose.maps.yml | 4 ++-- deployment/enterprise/docker-compose.yml | 8 ++++---- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/deployment/enterprise/README.md b/deployment/enterprise/README.md index 7d93ce42..05485d71 100644 --- a/deployment/enterprise/README.md +++ b/deployment/enterprise/README.md @@ -3,22 +3,17 @@ Suitable for Ubuntu servers, single-node deployment using Docker Compose and system NGINX as a reverse proxy. > [!IMPORTANT] -> Docker images for Mergin Maps Enterprise Edition are stored in a private AWS ECR repository. +> Docker images for Mergin Maps Enterprise Edition are stored in private Dockerhub repositories. > To access them, you need a Mergin Maps Enterprise [subscription](https://merginmaps.com/pricing). > Please contact the Mergin Maps [sales team](https://merginmaps.com/contact-sales)! -## Login to Mergin Maps AWS ECR Repository - -```shell -aws ecr --region eu-west-1 get-login-password | docker login --username AWS --password-stdin 433835555346.dkr.ecr.eu-west-1.amazonaws.com -``` - ## Load Docker Images, Configure, and Run Mergin Maps Stack -To run Mergin Maps, you need to load local Docker images (if any). Make sure you have access to Lutra's ECR repository. You can check this by running: +Login to dockerhub (you should have already received your access token from Mergin Maps team). +To run Mergin Maps, you need to load local Docker images (if any). Make sure you have access to Lutra's repositories. You can check this by running: ```shell -docker pull 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.3.0 +docker pull lutraconsulting/merginmaps-backend-ee:2025.7.3 ``` Then modify the [docker-compose file](docker-compose.yml) and create the environment file `.prod.env` from `.env.template`. Details about configuration can be found in the [docs](https://merginmaps.com/docs/server/install/). diff --git a/deployment/enterprise/docker-compose.maps.yml b/deployment/enterprise/docker-compose.maps.yml index 47d8cdc0..677a07ca 100644 --- a/deployment/enterprise/docker-compose.maps.yml +++ b/deployment/enterprise/docker-compose.maps.yml @@ -6,7 +6,7 @@ networks: services: qgis: container_name: mergin-qgis - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-server-ee:2025.1.0 + image: lutraconsulting/qgis-server:2025.1.0 user: 1000:999 networks: - mergin-net @@ -30,7 +30,7 @@ services: - ./qgis_nginx.conf:/etc/nginx/conf.d/default.conf qgis_extractor: container_name: mergin-qgis-extractor - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-extractor-ee:2025.3.0 + image: lutraconsulting/qgis-extractor:2025.3.1 user: 901:999 networks: - mergin-net diff --git a/deployment/enterprise/docker-compose.yml b/deployment/enterprise/docker-compose.yml index a9f33023..7381b904 100644 --- a/deployment/enterprise/docker-compose.yml +++ b/deployment/enterprise/docker-compose.yml @@ -5,7 +5,7 @@ networks: services: server: - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3 + image: lutraconsulting/merginmaps-backend-ee:2025.7.3 container_name: merginmaps-server restart: always user: 901:999 @@ -22,7 +22,7 @@ services: networks: - mergin web: - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-front:2025.7.3 + image: lutraconsulting/merginmaps-frontend-ee:2025.7.3 container_name: merginmaps-web restart: always depends_on: @@ -52,7 +52,7 @@ services: - server celery-beat: - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3 + image: lutraconsulting/merginmaps-backend-ee:2025.7.3 container_name: merginmaps-celery-beat restart: always user: 901:999 @@ -66,7 +66,7 @@ services: - mergin celery-worker: - image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3 + image: lutraconsulting/merginmaps-backend-ee:2025.7.3 container_name: merginmaps-celery-worker restart: always user: 901:999 From 44ed2bc4e7b3e5f496e1959bc055a3edc5967610 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Mon, 9 Feb 2026 16:27:06 +0100 Subject: [PATCH 2/6] Fix link to local Mergin Maps instance documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d3a912b..d1dcbbe0 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Try Mergin Maps at https://merginmaps.com/ - the SaaS service run by Lutra Consu ### Running locally -A step-by-step guide how to run local Mergin Maps instance can be found in our [documentation](https://merginmaps.com/docs/dev/mergince/). +A step-by-step guide how to run local Mergin Maps instance can be found in our [documentation](https://merginmaps.com/docs/server/install/). ### Manage Mergin Maps From 03e9d2c6f198d09f04db7ad49f3256783f8786e8 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Mon, 9 Feb 2026 15:31:31 +0000 Subject: [PATCH 3/6] add myself to CLA signed list --- LICENSES/CLA-signed-list.md | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSES/CLA-signed-list.md b/LICENSES/CLA-signed-list.md index bd7ccdfe..de5e8e86 100644 --- a/LICENSES/CLA-signed-list.md +++ b/LICENSES/CLA-signed-list.md @@ -21,3 +21,4 @@ C/ My company has custom contribution contract with Lutra Consulting Ltd. or I a * luxusko, 25th August 2023 * jozef-budac, 30th January 2024 * fernandinand, 13th March 2025 +* wonder-sk, 9th February 2026 From 3eb8dd5dbd1db548bd55666a176bfc758946d68a Mon Sep 17 00:00:00 2001 From: "marcel.kocisek" Date: Mon, 16 Feb 2026 12:40:42 +0100 Subject: [PATCH 4/6] Introduce UPLOAD_FILES_WHITELIST - this file paths will be ignored during files extensions and mime type checks --- server/mergin/sync/config.py | 2 ++ server/mergin/sync/utils.py | 15 ++++++++++ server/mergin/tests/test_utils.py | 46 +++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/server/mergin/sync/config.py b/server/mergin/sync/config.py index e616a0ca..093927d2 100644 --- a/server/mergin/sync/config.py +++ b/server/mergin/sync/config.py @@ -78,3 +78,5 @@ class Configuration(object): EXCLUDED_CLONE_FILENAMES = config( "EXCLUDED_CLONE_FILENAMES", default="qgis_cfg.xml", cast=Csv() ) + # files that should be ignored during extension and mime type check + UPLOAD_FILES_WHITELIST = config("UPLOAD_FILES_WHITELIST", default="", cast=Csv()) diff --git a/server/mergin/sync/utils.py b/server/mergin/sync/utils.py index de0fbe94..dd2d6a2c 100644 --- a/server/mergin/sync/utils.py +++ b/server/mergin/sync/utils.py @@ -27,6 +27,8 @@ from flask import current_app from pathlib import Path +from .config import Configuration + def generate_checksum(file, chunk_size=4096): """ @@ -349,6 +351,8 @@ def has_trailing_space(filepath: str) -> bool: def is_supported_extension(filepath) -> bool: """Check whether file's extension is supported.""" + if check_skip_validation(filepath): + return True ext = os.path.splitext(filepath)[1].lower() return ext and ext not in FORBIDDEN_EXTENSIONS @@ -491,6 +495,15 @@ def is_supported_extension(filepath) -> bool: ".xnk", } + +def check_skip_validation(file_path: str) -> bool: + """ + Check if we can skip validation for this file path. + Some files are allowed even if they have forbidden extension or mime type. + """ + return file_path in Configuration.UPLOAD_FILES_WHITELIST + + FORBIDDEN_MIME_TYPES = { "application/x-msdownload", "application/x-sh", @@ -515,6 +528,8 @@ def is_supported_extension(filepath) -> bool: def is_supported_type(filepath) -> bool: """Check whether the file mimetype is supported.""" + if check_skip_validation(filepath): + return True mime_type = get_mimetype(filepath) return mime_type.startswith("image/") or mime_type not in FORBIDDEN_MIME_TYPES diff --git a/server/mergin/tests/test_utils.py b/server/mergin/tests/test_utils.py index 00b3e1c6..424e1e01 100644 --- a/server/mergin/tests/test_utils.py +++ b/server/mergin/tests/test_utils.py @@ -22,10 +22,13 @@ has_valid_characters, has_valid_first_character, check_filename, + is_supported_extension, + is_supported_type, is_valid_path, get_x_accel_uri, wkb2wkt, has_trailing_space, + check_skip_validation, ) from ..auth.models import LoginHistory, User from . import json_headers @@ -322,3 +325,46 @@ class TestSchema(Schema): "size": "disk_usage", } assert schema_map == expected_map + + +def test_check_skip_validation(): + ALLOWED_FILES = ["script.js", "config/script.js"] + + # We patch the Configuration class attribute directly + with patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES): + + # Test allowed files + for file_path in ALLOWED_FILES: + assert check_skip_validation(file_path) + + # Test not allowed files + assert not check_skip_validation("test.py") + assert not check_skip_validation("/some/path/test.py") + assert not check_skip_validation("image.png") + + +def test_is_supported_extension(): + ALLOWED_FILES = ["script.js", "config/script.js"] + + with patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES): + for file_path in ALLOWED_FILES: + assert is_supported_extension(file_path) + + # Allowed normal file + assert is_supported_extension("image.png") + + # Forbidden file + assert not is_supported_extension("test.js") + + +def test_mime_type_validation_skip(): + ALLOWED_FILES = ["script.js", "config/script.js"] + # Mocking get_mimetype to return forbidden mime type + with patch( + "mergin.sync.utils.get_mimetype", return_value="application/x-python-code" + ), patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES): + for file_path in ALLOWED_FILES: + assert is_supported_extension(file_path) + + # Should be forbidden + assert not is_supported_type("other.js") From 5c31a3d396a8c16b37458d91f2fbfae654e7f726 Mon Sep 17 00:00:00 2001 From: Marcel Kocisek Date: Tue, 24 Feb 2026 09:52:41 +0100 Subject: [PATCH 5/6] Update server/mergin/sync/config.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- server/mergin/sync/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mergin/sync/config.py b/server/mergin/sync/config.py index 093927d2..bc735d4a 100644 --- a/server/mergin/sync/config.py +++ b/server/mergin/sync/config.py @@ -78,5 +78,5 @@ class Configuration(object): EXCLUDED_CLONE_FILENAMES = config( "EXCLUDED_CLONE_FILENAMES", default="qgis_cfg.xml", cast=Csv() ) - # files that should be ignored during extension and mime type check + # files that should be ignored during extension and MIME type checks UPLOAD_FILES_WHITELIST = config("UPLOAD_FILES_WHITELIST", default="", cast=Csv()) From fa1fc8416f25f5c1c69a082c196422e78cbeac3a Mon Sep 17 00:00:00 2001 From: "marcel.kocisek" Date: Tue, 24 Feb 2026 09:54:44 +0100 Subject: [PATCH 6/6] fix tests for mimetype --- server/mergin/tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mergin/tests/test_utils.py b/server/mergin/tests/test_utils.py index 424e1e01..410c902a 100644 --- a/server/mergin/tests/test_utils.py +++ b/server/mergin/tests/test_utils.py @@ -364,7 +364,7 @@ def test_mime_type_validation_skip(): "mergin.sync.utils.get_mimetype", return_value="application/x-python-code" ), patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES): for file_path in ALLOWED_FILES: - assert is_supported_extension(file_path) + assert is_supported_type(file_path) # Should be forbidden assert not is_supported_type("other.js")