From 909d0c345603d196c5a09703cc68baee85b706aa Mon Sep 17 00:00:00 2001 From: Mathieu Leplatre Date: Fri, 12 Jan 2024 17:55:42 +0100 Subject: [PATCH] Add a new ``kinto.admin_assets_path`` to specify the location of the Admin UI assets (#3343) * Add a new ``kinto.admin_assets_path`` to specify the location of the Admin UI assets * Add setting to kinto.tpl * Add tests --- CHANGELOG.rst | 4 +++- docs/kinto-admin.rst | 7 +++++++ kinto/__init__.py | 1 + kinto/config/kinto.tpl | 4 ++++ kinto/plugins/admin/__init__.py | 5 ++++- kinto/plugins/admin/views.py | 15 +++++++++++-- tests/plugins/test_admin.py | 35 +++++++++++++++++++++++++++++++ tests/test_configuration/test.ini | 4 ++++ 8 files changed, 71 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9a110a2a0..c4e2e30d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,9 @@ This document describes changes between each past release. 16.3.0 (unreleased) ------------------- -- Nothing changed yet. +**New features** + +- Add a new ``kinto.admin_assets_path`` setting to specify the location on the Admin UI assets. 16.2.3 (2023-12-05) diff --git a/docs/kinto-admin.rst b/docs/kinto-admin.rst index de99b27ce..0db0b383c 100644 --- a/docs/kinto-admin.rst +++ b/docs/kinto-admin.rst @@ -6,6 +6,13 @@ Kinto Admin When the built-in plugin ``kinto.plugins.admin`` is enabled in configuration, a Web admin UI is available at ``/v1/admin/``. ++-------------------------+----------+-------------------------------------------------+ +| Setting name | Default | What does it do? | ++=========================+==========+=================================================+ +| kinto.admin_assets_path | None | Absolute path to the Admin UI assets files. | +| | | The folder must contain an ``index.html`` file. | ++-------------------------+----------+-------------------------------------------------+ + * `See dedicated repo `_ diff --git a/kinto/__init__.py b/kinto/__init__.py index 84aa9ced8..f18996352 100644 --- a/kinto/__init__.py +++ b/kinto/__init__.py @@ -37,6 +37,7 @@ "group_id_generator": "kinto.views.NameGenerator", "record_id_generator": "kinto.views.RelaxedUUID", "project_name": "kinto", + "admin_assets_path": None, } diff --git a/kinto/config/kinto.tpl b/kinto/config/kinto.tpl index c333024de..7813b3013 100644 --- a/kinto/config/kinto.tpl +++ b/kinto/config/kinto.tpl @@ -201,6 +201,10 @@ kinto.bucket_create_principals = account:admin # kinto.group_id_generator = name_generator.GroupGenerator # kinto.record_id_generator = name_generator.RecordGenerator +# Kinto admin +# Absolute path to UI assets +# kinto.admin_assets_path = /app/kinto/plugins/admin/build/ + # Enabling or disabling endpoints # https://kinto.readthedocs.io/en/latest/configuration/settings.html#enabling-or-disabling-endpoints # diff --git a/kinto/plugins/admin/__init__.py b/kinto/plugins/admin/__init__.py index 2307b4e55..9a36d3766 100644 --- a/kinto/plugins/admin/__init__.py +++ b/kinto/plugins/admin/__init__.py @@ -23,7 +23,10 @@ def includeme(config): config.add_route("admin_home", "/admin/") config.add_view(admin_home_view, route_name="admin_home") - build_dir = static_view("kinto.plugins.admin:build", use_subpath=True) + admin_assets_path = ( + config.registry.settings["admin_assets_path"] or "kinto.plugins.admin:build" + ) + build_dir = static_view(admin_assets_path, use_subpath=True) config.add_route("catchall_static", "/admin/*subpath") config.add_view(build_dir, route_name="catchall_static") diff --git a/kinto/plugins/admin/views.py b/kinto/plugins/admin/views.py index 06acde231..eb68e36ea 100644 --- a/kinto/plugins/admin/views.py +++ b/kinto/plugins/admin/views.py @@ -9,11 +9,22 @@ # Configured home page @cache_forever def admin_home_view(request): + """ + This view reads the ``index.html`` file from the Admin assets path folder + and serves it. + + This requires the Admin UI to be built with ``ASSET_PATH="/v1/admin/"``. + """ + # Default location of the Admin UI is relative to this plugin source folder, + # as built with the ``make build-kinto-admin`` command. + admin_assets_path = request.registry.settings["admin_assets_path"] or os.path.join( + HERE, "build" + ) try: - with open(os.path.join(HERE, "build/index.html")) as f: + with open(os.path.join(admin_assets_path, "index.html")) as f: page_content = f.read() except FileNotFoundError: # pragma: no cover - with open(os.path.join(HERE, "public/help.html")) as f: + with open(os.path.join(HERE, "public", "help.html")) as f: page_content = f.read() # Add Content-Security-Policy HTTP response header to protect against XSS: diff --git a/tests/plugins/test_admin.py b/tests/plugins/test_admin.py index 3f617781f..2c9e1daf2 100644 --- a/tests/plugins/test_admin.py +++ b/tests/plugins/test_admin.py @@ -1,4 +1,5 @@ import os +import tempfile import unittest from kinto.plugins.admin import views as admin_views @@ -70,3 +71,37 @@ def test_admin_has_csp_header(self): # The cached version too. resp = self.app.get("/admin/") assert "default-src 'self'" in resp.headers["Content-Security-Policy"] + + +class OverriddenAdminViewTest(BaseWebTest, unittest.TestCase): + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls.tmp_dir.cleanup() + + @classmethod + def get_app_settings(cls, extras=None): + cls.tmp_dir = tempfile.TemporaryDirectory() + + settings = super().get_app_settings(extras) + settings["includes"] = "kinto.plugins.admin" + settings["admin_assets_path"] = cls.tmp_dir.name + return settings + + def setUp(self) -> None: + super().setUp() + with open(os.path.join(self.tmp_dir.name, "index.html"), "w") as f: + f.write("mine!") + with open(os.path.join(self.tmp_dir.name, "script.js"), "w") as f: + f.write("kiddy") + + def test_admin_ui_is_served_from_configured_folder(self): + resp = self.app.get("/admin/") + self.assertIn("mine!", resp.body.decode("utf-8")) + + def test_assets_are_served_from_configured_folder(self): + resp = self.app.get("/admin/script.js") + self.assertIn("kiddy", resp.body.decode("utf-8")) + + def test_original_assets_are_not_available(self): + self.app.get("/admin/favicon.png", status=404) diff --git a/tests/test_configuration/test.ini b/tests/test_configuration/test.ini index 5a86fa58d..5255f5522 100644 --- a/tests/test_configuration/test.ini +++ b/tests/test_configuration/test.ini @@ -201,6 +201,10 @@ kinto.bucket_create_principals = account:admin # kinto.group_id_generator = name_generator.GroupGenerator # kinto.record_id_generator = name_generator.RecordGenerator +# Kinto admin +# Absolute path to UI assets +# kinto.admin_assets_path = /app/kinto/plugins/admin/build/ + # Enabling or disabling endpoints # https://kinto.readthedocs.io/en/latest/configuration/settings.html#enabling-or-disabling-endpoints #