Repository: goauthentik/authentik

Issue:
False version status in admin dashboard for AirGapped environments
**Describe the bug**
In an AirGapped environment with `AUTHENTIK_DISABLE_UPDATE_CHECK=true`, or when the version check has not yet been performed, the version tile on the admin dashboard will always state its `Up-to-date!` which may not actually be the case.

**To Reproduce**
Steps to reproduce the behavior:

1. Deploy a fresh Authentik installation on an older version (e.g. `2023.10.7`) and ensure  `AUTHENTIK_DISABLE_UPDATE_CHECK=true` is set.
2. Go to the admin dashboard
3. Observe the version tile stating proudly that its up-to-date.

**Expected behavior**
Not incorrectly stating its up-to-date as that would give a false sense of security.

**Screenshots**
![image](https://github.com/goauthentik/authentik/assets/68224306/0a470c2b-73f7-4ba1-ad83-7e35a7f9a053)

**Logs**
N/a

**Version and Deployment (please complete the following information):**

-   authentik version: 2024.2.0
-   Deployment: docker-compose

**Additional context**
N/a



Discussion:

Resolving patch:
diff --git a/authentik/admin/api/version.py b/authentik/admin/api/version.py
--- a/authentik/admin/api/version.py
+++ b/authentik/admin/api/version.py
@@ -10,7 +10,7 @@
 from rest_framework.views import APIView
 
 from authentik import __version__, get_build_hash
-from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
+from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
 from authentik.core.api.utils import PassiveSerializer
 
 
@@ -19,6 +19,7 @@ class VersionSerializer(PassiveSerializer):
 
     version_current = SerializerMethodField()
     version_latest = SerializerMethodField()
+    version_latest_valid = SerializerMethodField()
     build_hash = SerializerMethodField()
     outdated = SerializerMethodField()
 
@@ -38,6 +39,10 @@ def get_version_latest(self, _) -> str:
             return __version__
         return version_in_cache
 
+    def get_version_latest_valid(self, _) -> str:
+        """Check if latest version is valid"""
+        return cache.get(VERSION_CACHE_KEY) != VERSION_NULL
+
     def get_outdated(self, instance) -> bool:
         """Check if we're running the latest version"""
         return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
diff --git a/authentik/admin/tasks.py b/authentik/admin/tasks.py
--- a/authentik/admin/tasks.py
+++ b/authentik/admin/tasks.py
@@ -18,6 +18,7 @@
 from authentik.root.celery import CELERY_APP
 
 LOGGER = get_logger()
+VERSION_NULL = "0.0.0"
 VERSION_CACHE_KEY = "authentik_latest_version"
 VERSION_CACHE_TIMEOUT = 8 * 60 * 60  # 8 hours
 # Chop of the first ^ because we want to search the entire string
@@ -55,7 +56,7 @@ def clear_update_notifications():
 def update_latest_version(self: SystemTask):
     """Update latest version info"""
     if CONFIG.get_bool("disable_update_check"):
-        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
+        cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
         self.set_status(TaskStatus.WARNING, "Version check disabled.")
         return
     try:
@@ -82,7 +83,7 @@ def update_latest_version(self: SystemTask):
                 event_dict["message"] = f"Changelog: {match.group()}"
             Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
     except (RequestException, IndexError) as exc:
-        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
+        cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
         self.set_error(exc)
 
 


Test patch: