You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2022/05/26 11:45:42 UTC

[superset] branch master updated: fix(temporary-cache): when user is anonymous (#20181)

This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 64c4226817 fix(temporary-cache): when user is anonymous (#20181)
64c4226817 is described below

commit 64c4226817b04ff598be29b52d8e2c4a679ef70a
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Thu May 26 14:45:20 2022 +0300

    fix(temporary-cache): when user is anonymous (#20181)
    
    * fix(temporary-cache): fail on anonymous user
    
    * make exceptions generic
    
    * fix test
    
    * remove redundant bool return
    
    * fix unit tests
---
 .../dashboards/filter_state/commands/create.py     | 17 ++++----
 .../dashboards/filter_state/commands/delete.py     | 22 +++++-----
 superset/dashboards/filter_state/commands/get.py   |  4 +-
 .../dashboards/filter_state/commands/update.py     | 45 +++++++++----------
 .../filter_state/commands/utils.py}                | 23 +++++++---
 superset/explore/form_data/api.py                  | 45 +++++--------------
 superset/explore/form_data/commands/create.py      |  6 +--
 superset/explore/form_data/commands/delete.py      |  5 ++-
 superset/explore/form_data/commands/get.py         |  2 +-
 superset/explore/form_data/commands/state.py       |  2 +-
 superset/explore/form_data/commands/update.py      | 10 ++---
 superset/explore/form_data/commands/utils.py       | 42 ++++++++++++++++++
 superset/explore/utils.py                          |  8 ++--
 superset/key_value/utils.py                        |  7 ++-
 superset/temporary_cache/api.py                    | 51 +++++-----------------
 superset/temporary_cache/commands/entry.py         |  4 +-
 superset/temporary_cache/commands/exceptions.py    |  4 ++
 tests/unit_tests/explore/utils_test.py             |  8 ++--
 18 files changed, 156 insertions(+), 149 deletions(-)

diff --git a/superset/dashboards/filter_state/commands/create.py b/superset/dashboards/filter_state/commands/create.py
index 137623027a..18dff8928f 100644
--- a/superset/dashboards/filter_state/commands/create.py
+++ b/superset/dashboards/filter_state/commands/create.py
@@ -14,11 +14,13 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from typing import cast
+
 from flask import session
 
-from superset.dashboards.dao import DashboardDAO
+from superset.dashboards.filter_state.commands.utils import check_access
 from superset.extensions import cache_manager
-from superset.key_value.utils import random_key
+from superset.key_value.utils import get_owner, random_key
 from superset.temporary_cache.commands.create import CreateTemporaryCacheCommand
 from superset.temporary_cache.commands.entry import Entry
 from superset.temporary_cache.commands.parameters import CommandParameters
@@ -34,10 +36,9 @@ class CreateFilterStateCommand(CreateTemporaryCacheCommand):
         key = cache_manager.filter_state_cache.get(contextual_key)
         if not key or not tab_id:
             key = random_key()
-        value = cmd_params.value
-        dashboard = DashboardDAO.get_by_id_or_slug(str(resource_id))
-        if dashboard and value:
-            entry: Entry = {"owner": actor.get_user_id(), "value": value}
-            cache_manager.filter_state_cache.set(cache_key(resource_id, key), entry)
-            cache_manager.filter_state_cache.set(contextual_key, key)
+        value = cast(str, cmd_params.value)  # schema ensures that value is not optional
+        check_access(resource_id)
+        entry: Entry = {"owner": get_owner(actor), "value": value}
+        cache_manager.filter_state_cache.set(cache_key(resource_id, key), entry)
+        cache_manager.filter_state_cache.set(contextual_key, key)
         return key
diff --git a/superset/dashboards/filter_state/commands/delete.py b/superset/dashboards/filter_state/commands/delete.py
index 155c63f108..3ddc08fc51 100644
--- a/superset/dashboards/filter_state/commands/delete.py
+++ b/superset/dashboards/filter_state/commands/delete.py
@@ -16,8 +16,9 @@
 # under the License.
 from flask import session
 
-from superset.dashboards.dao import DashboardDAO
+from superset.dashboards.filter_state.commands.utils import check_access
 from superset.extensions import cache_manager
+from superset.key_value.utils import get_owner
 from superset.temporary_cache.commands.delete import DeleteTemporaryCacheCommand
 from superset.temporary_cache.commands.entry import Entry
 from superset.temporary_cache.commands.exceptions import TemporaryCacheAccessDeniedError
@@ -30,14 +31,13 @@ class DeleteFilterStateCommand(DeleteTemporaryCacheCommand):
         resource_id = cmd_params.resource_id
         actor = cmd_params.actor
         key = cache_key(resource_id, cmd_params.key)
-        dashboard = DashboardDAO.get_by_id_or_slug(str(resource_id))
-        if dashboard:
-            entry: Entry = cache_manager.filter_state_cache.get(key)
-            if entry:
-                if entry["owner"] != actor.get_user_id():
-                    raise TemporaryCacheAccessDeniedError()
-                tab_id = cmd_params.tab_id
-                contextual_key = cache_key(session.get("_id"), tab_id, resource_id)
-                cache_manager.filter_state_cache.delete(contextual_key)
-                return cache_manager.filter_state_cache.delete(key)
+        check_access(resource_id)
+        entry: Entry = cache_manager.filter_state_cache.get(key)
+        if entry:
+            if entry["owner"] != get_owner(actor):
+                raise TemporaryCacheAccessDeniedError()
+            tab_id = cmd_params.tab_id
+            contextual_key = cache_key(session.get("_id"), tab_id, resource_id)
+            cache_manager.filter_state_cache.delete(contextual_key)
+            return cache_manager.filter_state_cache.delete(key)
         return False
diff --git a/superset/dashboards/filter_state/commands/get.py b/superset/dashboards/filter_state/commands/get.py
index 9cdd5bcddc..ca7ffa9879 100644
--- a/superset/dashboards/filter_state/commands/get.py
+++ b/superset/dashboards/filter_state/commands/get.py
@@ -18,7 +18,7 @@ from typing import Optional
 
 from flask import current_app as app
 
-from superset.dashboards.dao import DashboardDAO
+from superset.dashboards.filter_state.commands.utils import check_access
 from superset.extensions import cache_manager
 from superset.temporary_cache.commands.get import GetTemporaryCacheCommand
 from superset.temporary_cache.commands.parameters import CommandParameters
@@ -34,7 +34,7 @@ class GetFilterStateCommand(GetTemporaryCacheCommand):
     def get(self, cmd_params: CommandParameters) -> Optional[str]:
         resource_id = cmd_params.resource_id
         key = cache_key(resource_id, cmd_params.key)
-        DashboardDAO.get_by_id_or_slug(str(resource_id))
+        check_access(resource_id)
         entry = cache_manager.filter_state_cache.get(key) or {}
         if entry and self._refresh_timeout:
             cache_manager.filter_state_cache.set(key, entry)
diff --git a/superset/dashboards/filter_state/commands/update.py b/superset/dashboards/filter_state/commands/update.py
index d27277f9af..7f150aae6b 100644
--- a/superset/dashboards/filter_state/commands/update.py
+++ b/superset/dashboards/filter_state/commands/update.py
@@ -14,13 +14,13 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-from typing import Optional
+from typing import cast, Optional
 
 from flask import session
 
-from superset.dashboards.dao import DashboardDAO
+from superset.dashboards.filter_state.commands.utils import check_access
 from superset.extensions import cache_manager
-from superset.key_value.utils import random_key
+from superset.key_value.utils import get_owner, random_key
 from superset.temporary_cache.commands.entry import Entry
 from superset.temporary_cache.commands.exceptions import TemporaryCacheAccessDeniedError
 from superset.temporary_cache.commands.parameters import CommandParameters
@@ -33,28 +33,23 @@ class UpdateFilterStateCommand(UpdateTemporaryCacheCommand):
         resource_id = cmd_params.resource_id
         actor = cmd_params.actor
         key = cmd_params.key
-        value = cmd_params.value
-        dashboard = DashboardDAO.get_by_id_or_slug(str(resource_id))
-        if dashboard and value:
-            entry: Entry = cache_manager.filter_state_cache.get(
-                cache_key(resource_id, key)
-            )
-            if entry:
-                user_id = actor.get_user_id()
-                if entry["owner"] != user_id:
-                    raise TemporaryCacheAccessDeniedError()
+        value = cast(str, cmd_params.value)  # schema ensures that value is not optional
+        check_access(resource_id)
+        entry: Entry = cache_manager.filter_state_cache.get(cache_key(resource_id, key))
+        owner = get_owner(actor)
+        if entry:
+            if entry["owner"] != owner:
+                raise TemporaryCacheAccessDeniedError()
 
-                # Generate a new key if tab_id changes or equals 0
-                contextual_key = cache_key(
-                    session.get("_id"), cmd_params.tab_id, resource_id
-                )
-                key = cache_manager.filter_state_cache.get(contextual_key)
-                if not key or not cmd_params.tab_id:
-                    key = random_key()
-                    cache_manager.filter_state_cache.set(contextual_key, key)
+            # Generate a new key if tab_id changes or equals 0
+            contextual_key = cache_key(
+                session.get("_id"), cmd_params.tab_id, resource_id
+            )
+            key = cache_manager.filter_state_cache.get(contextual_key)
+            if not key or not cmd_params.tab_id:
+                key = random_key()
+                cache_manager.filter_state_cache.set(contextual_key, key)
 
-                new_entry: Entry = {"owner": actor.get_user_id(), "value": value}
-                cache_manager.filter_state_cache.set(
-                    cache_key(resource_id, key), new_entry
-                )
+            new_entry: Entry = {"owner": owner, "value": value}
+            cache_manager.filter_state_cache.set(cache_key(resource_id, key), new_entry)
         return key
diff --git a/superset/explore/form_data/commands/state.py b/superset/dashboards/filter_state/commands/utils.py
similarity index 56%
copy from superset/explore/form_data/commands/state.py
copy to superset/dashboards/filter_state/commands/utils.py
index 2aba14a8cb..35f940f434 100644
--- a/superset/explore/form_data/commands/state.py
+++ b/superset/dashboards/filter_state/commands/utils.py
@@ -14,13 +14,22 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-from typing import Optional
 
-from typing_extensions import TypedDict
+from superset.dashboards.commands.exceptions import (
+    DashboardAccessDeniedError,
+    DashboardNotFoundError,
+)
+from superset.dashboards.dao import DashboardDAO
+from superset.temporary_cache.commands.exceptions import (
+    TemporaryCacheAccessDeniedError,
+    TemporaryCacheResourceNotFoundError,
+)
 
 
-class TemporaryExploreState(TypedDict):
-    owner: int
-    dataset_id: int
-    chart_id: Optional[int]
-    form_data: str
+def check_access(resource_id: int) -> None:
+    try:
+        DashboardDAO.get_by_id_or_slug(str(resource_id))
+    except DashboardNotFoundError as ex:
+        raise TemporaryCacheResourceNotFoundError from ex
+    except DashboardAccessDeniedError as ex:
+        raise TemporaryCacheAccessDeniedError from ex
diff --git a/superset/explore/form_data/api.py b/superset/explore/form_data/api.py
index dc6ee7ea94..ea2b38658b 100644
--- a/superset/explore/form_data/api.py
+++ b/superset/explore/form_data/api.py
@@ -21,15 +21,7 @@ from flask import g, request, Response
 from flask_appbuilder.api import BaseApi, expose, protect, safe
 from marshmallow import ValidationError
 
-from superset.charts.commands.exceptions import (
-    ChartAccessDeniedError,
-    ChartNotFoundError,
-)
 from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP, RouteMethod
-from superset.datasets.commands.exceptions import (
-    DatasetAccessDeniedError,
-    DatasetNotFoundError,
-)
 from superset.explore.form_data.commands.create import CreateFormDataCommand
 from superset.explore.form_data.commands.delete import DeleteFormDataCommand
 from superset.explore.form_data.commands.get import GetFormDataCommand
@@ -37,7 +29,10 @@ from superset.explore.form_data.commands.parameters import CommandParameters
 from superset.explore.form_data.commands.update import UpdateFormDataCommand
 from superset.explore.form_data.schemas import FormDataPostSchema, FormDataPutSchema
 from superset.extensions import event_logger
-from superset.temporary_cache.commands.exceptions import TemporaryCacheAccessDeniedError
+from superset.temporary_cache.commands.exceptions import (
+    TemporaryCacheAccessDeniedError,
+    TemporaryCacheResourceNotFoundError,
+)
 from superset.views.base_api import requires_json
 
 logger = logging.getLogger(__name__)
@@ -118,13 +113,9 @@ class ExploreFormDataRestApi(BaseApi, ABC):
             return self.response(201, key=key)
         except ValidationError as ex:
             return self.response(400, message=ex.messages)
-        except (
-            ChartAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     @expose("/form_data/<string:key>", methods=["PUT"])
@@ -195,13 +186,9 @@ class ExploreFormDataRestApi(BaseApi, ABC):
             return self.response(200, key=result)
         except ValidationError as ex:
             return self.response(400, message=ex.messages)
-        except (
-            ChartAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     @expose("/form_data/<string:key>", methods=["GET"])
@@ -250,13 +237,9 @@ class ExploreFormDataRestApi(BaseApi, ABC):
             if not form_data:
                 return self.response_404()
             return self.response(200, form_data=form_data)
-        except (
-            ChartAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     @expose("/form_data/<string:key>", methods=["DELETE"])
@@ -306,11 +289,7 @@ class ExploreFormDataRestApi(BaseApi, ABC):
             if not result:
                 return self.response_404()
             return self.response(200, message="Deleted successfully")
-        except (
-            ChartAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
diff --git a/superset/explore/form_data/commands/create.py b/superset/explore/form_data/commands/create.py
index 7b1f866c50..84ad0f0078 100644
--- a/superset/explore/form_data/commands/create.py
+++ b/superset/explore/form_data/commands/create.py
@@ -22,9 +22,9 @@ from sqlalchemy.exc import SQLAlchemyError
 from superset.commands.base import BaseCommand
 from superset.explore.form_data.commands.parameters import CommandParameters
 from superset.explore.form_data.commands.state import TemporaryExploreState
-from superset.explore.utils import check_access
+from superset.explore.form_data.commands.utils import check_access
 from superset.extensions import cache_manager
-from superset.key_value.utils import random_key
+from superset.key_value.utils import get_owner, random_key
 from superset.temporary_cache.commands.exceptions import TemporaryCacheCreateFailedError
 from superset.temporary_cache.utils import cache_key
 from superset.utils.schema import validate_json
@@ -51,7 +51,7 @@ class CreateFormDataCommand(BaseCommand):
                 key = random_key()
             if form_data:
                 state: TemporaryExploreState = {
-                    "owner": actor.get_user_id(),
+                    "owner": get_owner(actor),
                     "dataset_id": dataset_id,
                     "chart_id": chart_id,
                     "form_data": form_data,
diff --git a/superset/explore/form_data/commands/delete.py b/superset/explore/form_data/commands/delete.py
index ec537313d2..69d5a79b8f 100644
--- a/superset/explore/form_data/commands/delete.py
+++ b/superset/explore/form_data/commands/delete.py
@@ -23,8 +23,9 @@ from sqlalchemy.exc import SQLAlchemyError
 from superset.commands.base import BaseCommand
 from superset.explore.form_data.commands.parameters import CommandParameters
 from superset.explore.form_data.commands.state import TemporaryExploreState
-from superset.explore.utils import check_access
+from superset.explore.form_data.commands.utils import check_access
 from superset.extensions import cache_manager
+from superset.key_value.utils import get_owner
 from superset.temporary_cache.commands.exceptions import (
     TemporaryCacheAccessDeniedError,
     TemporaryCacheDeleteFailedError,
@@ -49,7 +50,7 @@ class DeleteFormDataCommand(BaseCommand, ABC):
                 dataset_id = state["dataset_id"]
                 chart_id = state["chart_id"]
                 check_access(dataset_id, chart_id, actor)
-                if state["owner"] != actor.get_user_id():
+                if state["owner"] != get_owner(actor):
                     raise TemporaryCacheAccessDeniedError()
                 tab_id = self._cmd_params.tab_id
                 contextual_key = cache_key(
diff --git a/superset/explore/form_data/commands/get.py b/superset/explore/form_data/commands/get.py
index 5b58200821..809672f32f 100644
--- a/superset/explore/form_data/commands/get.py
+++ b/superset/explore/form_data/commands/get.py
@@ -24,7 +24,7 @@ from sqlalchemy.exc import SQLAlchemyError
 from superset.commands.base import BaseCommand
 from superset.explore.form_data.commands.parameters import CommandParameters
 from superset.explore.form_data.commands.state import TemporaryExploreState
-from superset.explore.utils import check_access
+from superset.explore.form_data.commands.utils import check_access
 from superset.extensions import cache_manager
 from superset.temporary_cache.commands.exceptions import TemporaryCacheGetFailedError
 
diff --git a/superset/explore/form_data/commands/state.py b/superset/explore/form_data/commands/state.py
index 2aba14a8cb..c8061e81f5 100644
--- a/superset/explore/form_data/commands/state.py
+++ b/superset/explore/form_data/commands/state.py
@@ -20,7 +20,7 @@ from typing_extensions import TypedDict
 
 
 class TemporaryExploreState(TypedDict):
-    owner: int
+    owner: Optional[int]
     dataset_id: int
     chart_id: Optional[int]
     form_data: str
diff --git a/superset/explore/form_data/commands/update.py b/superset/explore/form_data/commands/update.py
index 596c5f6e27..76dfee1dad 100644
--- a/superset/explore/form_data/commands/update.py
+++ b/superset/explore/form_data/commands/update.py
@@ -24,9 +24,9 @@ from sqlalchemy.exc import SQLAlchemyError
 from superset.commands.base import BaseCommand
 from superset.explore.form_data.commands.parameters import CommandParameters
 from superset.explore.form_data.commands.state import TemporaryExploreState
-from superset.explore.utils import check_access
+from superset.explore.form_data.commands.utils import check_access
 from superset.extensions import cache_manager
-from superset.key_value.utils import random_key
+from superset.key_value.utils import get_owner, random_key
 from superset.temporary_cache.commands.exceptions import (
     TemporaryCacheAccessDeniedError,
     TemporaryCacheUpdateFailedError,
@@ -56,9 +56,9 @@ class UpdateFormDataCommand(BaseCommand, ABC):
             state: TemporaryExploreState = cache_manager.explore_form_data_cache.get(
                 key
             )
+            owner = get_owner(actor)
             if state and form_data:
-                user_id = actor.get_user_id()
-                if state["owner"] != user_id:
+                if state["owner"] != owner:
                     raise TemporaryCacheAccessDeniedError()
 
                 # Generate a new key if tab_id changes or equals 0
@@ -72,7 +72,7 @@ class UpdateFormDataCommand(BaseCommand, ABC):
                     cache_manager.explore_form_data_cache.set(contextual_key, key)
 
                 new_state: TemporaryExploreState = {
-                    "owner": actor.get_user_id(),
+                    "owner": owner,
                     "dataset_id": dataset_id,
                     "chart_id": chart_id,
                     "form_data": form_data,
diff --git a/superset/explore/form_data/commands/utils.py b/superset/explore/form_data/commands/utils.py
new file mode 100644
index 0000000000..5d09657fbe
--- /dev/null
+++ b/superset/explore/form_data/commands/utils.py
@@ -0,0 +1,42 @@
+# 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 typing import Optional
+
+from flask_appbuilder.security.sqla.models import User
+
+from superset.charts.commands.exceptions import (
+    ChartAccessDeniedError,
+    ChartNotFoundError,
+)
+from superset.datasets.commands.exceptions import (
+    DatasetAccessDeniedError,
+    DatasetNotFoundError,
+)
+from superset.explore.utils import check_access as explore_check_access
+from superset.temporary_cache.commands.exceptions import (
+    TemporaryCacheAccessDeniedError,
+    TemporaryCacheResourceNotFoundError,
+)
+
+
+def check_access(dataset_id: int, chart_id: Optional[int], actor: User) -> None:
+    try:
+        explore_check_access(dataset_id, chart_id, actor)
+    except (ChartNotFoundError, DatasetNotFoundError) as ex:
+        raise TemporaryCacheResourceNotFoundError from ex
+    except (ChartAccessDeniedError, DatasetAccessDeniedError) as ex:
+        raise TemporaryCacheAccessDeniedError from ex
diff --git a/superset/explore/utils.py b/superset/explore/utils.py
index 3eeb1bab99..7ab29de2f7 100644
--- a/superset/explore/utils.py
+++ b/superset/explore/utils.py
@@ -44,12 +44,10 @@ def check_dataset_access(dataset_id: int) -> Optional[bool]:
     raise DatasetNotFoundError()
 
 
-def check_access(
-    dataset_id: int, chart_id: Optional[int], actor: User
-) -> Optional[bool]:
+def check_access(dataset_id: int, chart_id: Optional[int], actor: User) -> None:
     check_dataset_access(dataset_id)
     if not chart_id:
-        return True
+        return
     chart = ChartDAO.find_by_id(chart_id)
     if chart:
         can_access_chart = (
@@ -58,6 +56,6 @@ def check_access(
             or security_manager.can_access("can_read", "Chart")
         )
         if can_access_chart:
-            return True
+            return
         raise ChartAccessDeniedError()
     raise ChartNotFoundError()
diff --git a/superset/key_value/utils.py b/superset/key_value/utils.py
index b2e8e729b0..db27e505fb 100644
--- a/superset/key_value/utils.py
+++ b/superset/key_value/utils.py
@@ -18,10 +18,11 @@ from __future__ import annotations
 
 from hashlib import md5
 from secrets import token_urlsafe
-from typing import Union
+from typing import Optional, Union
 from uuid import UUID
 
 import hashids
+from flask_appbuilder.security.sqla.models import User
 from flask_babel import gettext as _
 
 from superset.key_value.exceptions import KeyValueParseKeyError
@@ -63,3 +64,7 @@ def get_uuid_namespace(seed: str) -> UUID:
     md5_obj = md5()
     md5_obj.update(seed.encode("utf-8"))
     return UUID(md5_obj.hexdigest())
+
+
+def get_owner(user: User) -> Optional[int]:
+    return user.get_user_id() if not user.is_anonymous else None
diff --git a/superset/temporary_cache/api.py b/superset/temporary_cache/api.py
index e91a288669..bdbdda302e 100644
--- a/superset/temporary_cache/api.py
+++ b/superset/temporary_cache/api.py
@@ -24,20 +24,11 @@ from flask import g, request, Response
 from flask_appbuilder.api import BaseApi
 from marshmallow import ValidationError
 
-from superset.charts.commands.exceptions import (
-    ChartAccessDeniedError,
-    ChartNotFoundError,
-)
 from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP, RouteMethod
-from superset.dashboards.commands.exceptions import (
-    DashboardAccessDeniedError,
-    DashboardNotFoundError,
-)
-from superset.datasets.commands.exceptions import (
-    DatasetAccessDeniedError,
-    DatasetNotFoundError,
+from superset.temporary_cache.commands.exceptions import (
+    TemporaryCacheAccessDeniedError,
+    TemporaryCacheResourceNotFoundError,
 )
-from superset.temporary_cache.commands.exceptions import TemporaryCacheAccessDeniedError
 from superset.temporary_cache.commands.parameters import CommandParameters
 from superset.temporary_cache.schemas import (
     TemporaryCachePostSchema,
@@ -86,14 +77,9 @@ class TemporaryCacheRestApi(BaseApi, ABC):
             return self.response(201, key=key)
         except ValidationError as ex:
             return self.response(400, message=ex.messages)
-        except (
-            ChartAccessDeniedError,
-            DashboardAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DashboardNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     @requires_json
@@ -112,14 +98,9 @@ class TemporaryCacheRestApi(BaseApi, ABC):
             return self.response(200, key=key)
         except ValidationError as ex:
             return self.response(400, message=ex.messages)
-        except (
-            ChartAccessDeniedError,
-            DashboardAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DashboardNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     def get(self, pk: int, key: str) -> Response:
@@ -129,14 +110,9 @@ class TemporaryCacheRestApi(BaseApi, ABC):
             if not value:
                 return self.response_404()
             return self.response(200, value=value)
-        except (
-            ChartAccessDeniedError,
-            DashboardAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DashboardNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     def delete(self, pk: int, key: str) -> Response:
@@ -146,14 +122,9 @@ class TemporaryCacheRestApi(BaseApi, ABC):
             if not result:
                 return self.response_404()
             return self.response(200, message="Deleted successfully")
-        except (
-            ChartAccessDeniedError,
-            DashboardAccessDeniedError,
-            DatasetAccessDeniedError,
-            TemporaryCacheAccessDeniedError,
-        ) as ex:
+        except TemporaryCacheAccessDeniedError as ex:
             return self.response(403, message=str(ex))
-        except (ChartNotFoundError, DashboardNotFoundError, DatasetNotFoundError) as ex:
+        except TemporaryCacheResourceNotFoundError as ex:
             return self.response(404, message=str(ex))
 
     @abstractmethod
diff --git a/superset/temporary_cache/commands/entry.py b/superset/temporary_cache/commands/entry.py
index 0e9ad0a735..90aa8e1beb 100644
--- a/superset/temporary_cache/commands/entry.py
+++ b/superset/temporary_cache/commands/entry.py
@@ -14,9 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from typing import Optional
+
 from typing_extensions import TypedDict
 
 
 class Entry(TypedDict):
-    owner: int
+    owner: Optional[int]
     value: str
diff --git a/superset/temporary_cache/commands/exceptions.py b/superset/temporary_cache/commands/exceptions.py
index 0f8c44cb18..8652a732f7 100644
--- a/superset/temporary_cache/commands/exceptions.py
+++ b/superset/temporary_cache/commands/exceptions.py
@@ -43,3 +43,7 @@ class TemporaryCacheUpdateFailedError(UpdateFailedError):
 
 class TemporaryCacheAccessDeniedError(ForbiddenError):
     message = _("You don't have permission to modify the value.")
+
+
+class TemporaryCacheResourceNotFoundError(ForbiddenError):
+    message = _("Resource was not found.")
diff --git a/tests/unit_tests/explore/utils_test.py b/tests/unit_tests/explore/utils_test.py
index 3d12f5e911..11e2906ed8 100644
--- a/tests/unit_tests/explore/utils_test.py
+++ b/tests/unit_tests/explore/utils_test.py
@@ -75,7 +75,7 @@ def test_unsaved_chart_authorized_dataset(
 
     mocker.patch(dataset_find_by_id, return_value=SqlaTable())
     mocker.patch(can_access_datasource, return_value=True)
-    assert check_access(dataset_id=1, chart_id=0, actor=User()) == True
+    check_access(dataset_id=1, chart_id=0, actor=User())
 
 
 def test_saved_chart_unknown_chart_id(
@@ -112,7 +112,7 @@ def test_saved_chart_is_admin(mocker: MockFixture, app_context: AppContext) -> N
     mocker.patch(can_access_datasource, return_value=True)
     mocker.patch(is_user_admin, return_value=True)
     mocker.patch(chart_find_by_id, return_value=Slice())
-    assert check_access(dataset_id=1, chart_id=1, actor=User()) is True
+    check_access(dataset_id=1, chart_id=1, actor=User())
 
 
 def test_saved_chart_is_owner(mocker: MockFixture, app_context: AppContext) -> None:
@@ -125,7 +125,7 @@ def test_saved_chart_is_owner(mocker: MockFixture, app_context: AppContext) -> N
     mocker.patch(is_user_admin, return_value=False)
     mocker.patch(is_owner, return_value=True)
     mocker.patch(chart_find_by_id, return_value=Slice())
-    assert check_access(dataset_id=1, chart_id=1, actor=User()) == True
+    check_access(dataset_id=1, chart_id=1, actor=User())
 
 
 def test_saved_chart_has_access(mocker: MockFixture, app_context: AppContext) -> None:
@@ -139,7 +139,7 @@ def test_saved_chart_has_access(mocker: MockFixture, app_context: AppContext) ->
     mocker.patch(is_owner, return_value=False)
     mocker.patch(can_access, return_value=True)
     mocker.patch(chart_find_by_id, return_value=Slice())
-    assert check_access(dataset_id=1, chart_id=1, actor=User()) == True
+    check_access(dataset_id=1, chart_id=1, actor=User())
 
 
 def test_saved_chart_no_access(mocker: MockFixture, app_context: AppContext) -> None: