mirror of
https://github.com/apache/superset.git
synced 2026-04-20 00:24:38 +00:00
feat: Implement UI-based system theme administration (#34560)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
f50cbd7958
commit
5309edf3a5
@@ -92,13 +92,14 @@ class TestThemeApi(SupersetTestCase):
|
||||
"created_on",
|
||||
"id",
|
||||
"is_system",
|
||||
"is_system_default",
|
||||
"is_system_dark",
|
||||
"json_data",
|
||||
"theme_name",
|
||||
"uuid",
|
||||
]
|
||||
result_columns = list(data["result"][0].keys())
|
||||
result_columns.sort()
|
||||
assert expected_columns == result_columns
|
||||
assert set(expected_columns) == set(result_columns)
|
||||
|
||||
@pytest.mark.usefixtures("create_themes")
|
||||
def test_get_list_sort_theme(self):
|
||||
@@ -207,6 +208,8 @@ class TestThemeApi(SupersetTestCase):
|
||||
"theme_name": "theme_name1",
|
||||
"json_data": '{"color": "theme1"}',
|
||||
"is_system": False,
|
||||
"is_system_default": False,
|
||||
"is_system_dark": False,
|
||||
"uuid": str(theme.uuid),
|
||||
"changed_by": {
|
||||
"first_name": theme.created_by.first_name,
|
||||
|
||||
260
tests/integration_tests/themes/test_theme_api_permissions.py
Normal file
260
tests/integration_tests/themes/test_theme_api_permissions.py
Normal file
@@ -0,0 +1,260 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Integration tests for theme API permissions"""
|
||||
|
||||
import uuid
|
||||
|
||||
from superset import db
|
||||
from superset.models.core import Theme
|
||||
from superset.utils import json
|
||||
from tests.conftest import with_config
|
||||
from tests.integration_tests.base_tests import SupersetTestCase
|
||||
from tests.integration_tests.constants import ADMIN_USERNAME, GAMMA_USERNAME
|
||||
|
||||
|
||||
class TestThemeAPIPermissions(SupersetTestCase):
|
||||
"""Test theme API permissions and system theme functionality"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures"""
|
||||
super().setUp()
|
||||
|
||||
# Generate unique identifier for this test
|
||||
self.test_id = str(uuid.uuid4())[:8]
|
||||
|
||||
# Create test themes
|
||||
self.regular_theme = Theme(
|
||||
theme_name=f"Regular Theme {self.test_id}",
|
||||
json_data=json.dumps({"colors": {"primary": "#1890ff"}}),
|
||||
is_system=False,
|
||||
created_by=self.get_user("admin"),
|
||||
changed_by=self.get_user("admin"),
|
||||
)
|
||||
|
||||
self.system_theme = Theme(
|
||||
theme_name=f"System Theme {self.test_id}",
|
||||
json_data=json.dumps({"colors": {"primary": "#000000"}}),
|
||||
is_system=True,
|
||||
created_by=self.get_user("admin"),
|
||||
changed_by=self.get_user("admin"),
|
||||
)
|
||||
|
||||
db.session.add_all([self.regular_theme, self.system_theme])
|
||||
db.session.commit()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test fixtures"""
|
||||
# Clean up themes
|
||||
for theme in [self.regular_theme, self.system_theme]:
|
||||
if theme:
|
||||
try:
|
||||
db.session.delete(theme)
|
||||
except Exception:
|
||||
db.session.rollback()
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
db.session.rollback()
|
||||
|
||||
super().tearDown()
|
||||
|
||||
def test_admin_can_set_system_default(self):
|
||||
"""Test that admin can set a theme as system default"""
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Set theme as system default
|
||||
response = self.client.put(
|
||||
f"/api/v1/theme/{self.regular_theme.id}/set_system_default"
|
||||
)
|
||||
|
||||
# Should succeed
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify theme is now system default
|
||||
theme = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
assert theme.is_system_default is True
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": True})
|
||||
def test_non_admin_cannot_set_system_default(self):
|
||||
"""Test that non-admin users cannot set system themes"""
|
||||
# Login as gamma user
|
||||
self.login(GAMMA_USERNAME)
|
||||
|
||||
# Try to set theme as system default
|
||||
response = self.client.put(
|
||||
f"/api/v1/theme/{self.regular_theme.id}/set_system_default"
|
||||
)
|
||||
|
||||
# Should be forbidden
|
||||
assert response.status_code == 403
|
||||
data = response.get_json()
|
||||
assert "Only administrators can set system themes" in data["message"]
|
||||
|
||||
# Verify theme is not system default
|
||||
theme = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
assert theme.is_system_default is False
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": False})
|
||||
def test_system_theme_requires_config_enabled(self):
|
||||
"""Test that system theme APIs require configuration to be enabled"""
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Try to set theme as system default
|
||||
response = self.client.put(
|
||||
f"/api/v1/theme/{self.regular_theme.id}/set_system_default"
|
||||
)
|
||||
|
||||
# Should be forbidden
|
||||
assert response.status_code == 403
|
||||
data = response.get_json()
|
||||
assert "UI theme administration is not enabled" in data["message"]
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": True})
|
||||
def test_admin_can_set_system_dark(self):
|
||||
"""Test that admin can set a theme as system dark"""
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Set theme as system dark
|
||||
response = self.client.put(
|
||||
f"/api/v1/theme/{self.regular_theme.id}/set_system_dark"
|
||||
)
|
||||
|
||||
# Should succeed
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify theme is now system dark
|
||||
theme = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
assert theme.is_system_dark is True
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": True})
|
||||
def test_admin_can_unset_system_default(self):
|
||||
"""Test that admin can unset system default theme"""
|
||||
# First set a theme as system default
|
||||
self.regular_theme.is_system_default = True
|
||||
db.session.commit()
|
||||
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Unset system default
|
||||
response = self.client.delete("/api/v1/theme/unset_system_default")
|
||||
|
||||
# Should succeed
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify no theme is system default
|
||||
theme = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
assert theme.is_system_default is False
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": True})
|
||||
def test_admin_can_unset_system_dark(self):
|
||||
"""Test that admin can unset system dark theme"""
|
||||
# First set a theme as system dark
|
||||
self.regular_theme.is_system_dark = True
|
||||
db.session.commit()
|
||||
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Unset system dark
|
||||
response = self.client.delete("/api/v1/theme/unset_system_dark")
|
||||
|
||||
# Should succeed
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify no theme is system dark
|
||||
theme = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
assert theme.is_system_dark is False
|
||||
|
||||
@with_config({"ENABLE_UI_THEME_ADMINISTRATION": True})
|
||||
def test_only_one_system_default_allowed(self):
|
||||
"""Test that only one theme can be system default at a time"""
|
||||
# Create another theme
|
||||
theme2 = Theme(
|
||||
theme_name=f"Another Theme {self.test_id}",
|
||||
json_data=json.dumps({"colors": {"primary": "#ff0000"}}),
|
||||
created_by=self.get_user("admin"),
|
||||
changed_by=self.get_user("admin"),
|
||||
)
|
||||
db.session.add(theme2)
|
||||
db.session.commit()
|
||||
|
||||
try:
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Set first theme as system default
|
||||
response = self.client.put(
|
||||
f"/api/v1/theme/{self.regular_theme.id}/set_system_default"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Set second theme as system default
|
||||
response = self.client.put(f"/api/v1/theme/{theme2.id}/set_system_default")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify only the second theme is system default
|
||||
theme1 = db.session.query(Theme).filter_by(id=self.regular_theme.id).first()
|
||||
theme2_check = db.session.query(Theme).filter_by(id=theme2.id).first()
|
||||
|
||||
assert theme1.is_system_default is False
|
||||
assert theme2_check.is_system_default is True
|
||||
|
||||
finally:
|
||||
# Clean up
|
||||
db.session.delete(theme2)
|
||||
db.session.commit()
|
||||
|
||||
def test_system_theme_cannot_be_deleted(self):
|
||||
"""Test that themes set as system themes cannot be deleted"""
|
||||
# Set theme as system default
|
||||
self.regular_theme.is_system_default = True
|
||||
db.session.commit()
|
||||
|
||||
# Login as admin
|
||||
self.login(ADMIN_USERNAME)
|
||||
|
||||
# Try to delete the theme
|
||||
response = self.client.delete(f"/api/v1/theme/{self.regular_theme.id}")
|
||||
|
||||
# Should fail with appropriate error
|
||||
assert response.status_code == 422
|
||||
data = response.get_json()
|
||||
assert (
|
||||
"Cannot delete theme" in data["message"]
|
||||
or "system" in data["message"].lower()
|
||||
)
|
||||
|
||||
def test_gamma_user_can_read_themes(self):
|
||||
"""Test that gamma users can read themes"""
|
||||
# Login as gamma user
|
||||
self.login(GAMMA_USERNAME)
|
||||
|
||||
# Should be able to read themes
|
||||
response = self.client.get("/api/v1/theme/")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Should be able to read individual theme
|
||||
response = self.client.get(f"/api/v1/theme/{self.regular_theme.id}")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Note: Gamma users' ability to create/update/delete themes
|
||||
# depends on the specific permissions configuration
|
||||
Reference in New Issue
Block a user