diff --git a/superset/available_domains/__init__.py b/superset/available_domains/__init__.py new file mode 100644 index 000000000..13a83393a --- /dev/null +++ b/superset/available_domains/__init__.py @@ -0,0 +1,16 @@ +# 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. diff --git a/superset/available_domains/api.py b/superset/available_domains/api.py new file mode 100644 index 000000000..533f240af --- /dev/null +++ b/superset/available_domains/api.py @@ -0,0 +1,75 @@ +# 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. +import logging + +from flask import Response +from flask_appbuilder.api import BaseApi, expose, protect, safe + +from superset import conf +from superset.available_domains.schemas import AvailableDomainsSchema +from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP, RouteMethod +from superset.extensions import event_logger + +logger = logging.getLogger(__name__) + + +class AvailableDomainsRestApi(BaseApi): + available_domains_schema = AvailableDomainsSchema() + + method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP + include_route_methods = {RouteMethod.GET} + allow_browser_login = True + class_permission_name = "AvailableDomains" + resource_name = "available_domains" + openapi_spec_tag = "Available Domains" + openapi_spec_component_schemas = (AvailableDomainsSchema,) + + @expose("/", methods=["GET"]) + @protect() + @safe + @event_logger.log_this_with_context( + action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.get", + log_to_statsd=True, + ) + def get(self) -> Response: + """ + Returns the list of available Superset Webserver domains (if any) + defined in config. This enables charts embedded in other apps to + leverage domain sharding if appropriately configured. + --- + get: + description: >- + Get all available domains + responses: + 200: + description: a list of available domains + content: + application/json: + schema: + type: object + properties: + result: + $ref: '#/components/schemas/AvailableDomainsSchema' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + """ + result = self.available_domains_schema.dump( + {"domains": conf.get("SUPERSET_WEBSERVER_DOMAINS")} + ) + return self.response(200, result=result) diff --git a/superset/available_domains/schemas.py b/superset/available_domains/schemas.py new file mode 100644 index 000000000..e9aa1b0a0 --- /dev/null +++ b/superset/available_domains/schemas.py @@ -0,0 +1,21 @@ +# 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. +from marshmallow import fields, Schema + + +class AvailableDomainsSchema(Schema): + domains = fields.List(fields.String()) diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index 2fe5591da..a4dcfc929 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -116,6 +116,7 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods from superset.annotation_layers.annotations.api import AnnotationRestApi from superset.annotation_layers.api import AnnotationLayerRestApi from superset.async_events.api import AsyncEventsRestApi + from superset.available_domains.api import AvailableDomainsRestApi from superset.cachekeys.api import CacheRestApi from superset.charts.api import ChartRestApi from superset.charts.data.api import ChartDataRestApi @@ -193,6 +194,7 @@ class SupersetAppInitializer: # pylint: disable=too-many-public-methods appbuilder.add_api(AnnotationLayerRestApi) appbuilder.add_api(AsyncEventsRestApi) appbuilder.add_api(AdvancedDataTypeRestApi) + appbuilder.add_api(AvailableDomainsRestApi) appbuilder.add_api(CacheRestApi) appbuilder.add_api(ChartRestApi) appbuilder.add_api(ChartDataRestApi) diff --git a/superset/views/core.py b/superset/views/core.py index 4f3923379..93818f311 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1570,6 +1570,11 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods defined in config. This enables charts embedded in other apps to leverage domain sharding if appropriately configured. """ + logger.warning( + "%s.available_domains " + "This API endpoint is deprecated and will be removed in version 3.0.0", + self.__class__.__name__, + ) return Response( json.dumps(conf.get("SUPERSET_WEBSERVER_DOMAINS")), mimetype="text/json" ) diff --git a/tests/integration_tests/available_domains/__init__.py b/tests/integration_tests/available_domains/__init__.py new file mode 100644 index 000000000..13a83393a --- /dev/null +++ b/tests/integration_tests/available_domains/__init__.py @@ -0,0 +1,16 @@ +# 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. diff --git a/tests/integration_tests/available_domains/api_tests.py b/tests/integration_tests/available_domains/api_tests.py new file mode 100644 index 000000000..8838207d2 --- /dev/null +++ b/tests/integration_tests/available_domains/api_tests.py @@ -0,0 +1,30 @@ +# 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. +import json + +from tests.integration_tests.test_app import app + + +def test_get_available_domains(test_client, login_as_admin): + cached = app.config["SUPERSET_WEBSERVER_DOMAINS"] + app.config["SUPERSET_WEBSERVER_DOMAINS"] = ["a", "b"] + resp = test_client.get("api/v1/available_domains/") + assert resp.status_code == 200 + data = json.loads(resp.data.decode("utf-8")) + result = data.get("result") + assert result == {"domains": ["a", "b"]} + app.config["SUPERSET_WEBSERVER_DOMAINS"] = cached diff --git a/tests/integration_tests/security_tests.py b/tests/integration_tests/security_tests.py index b46baa508..d55f9a003 100644 --- a/tests/integration_tests/security_tests.py +++ b/tests/integration_tests/security_tests.py @@ -82,6 +82,7 @@ GAMMA_ROLE_PERMISSIONS = { ["can_write", "CssTemplate"], ["can_read", "ReportSchedule"], ["can_write", "ReportSchedule"], + ["can_read", "AvailableDomains"], ["can_read", "Chart"], ["can_write", "Chart"], ["can_read", "Annotation"],