CVE-2023-22903 (published 2023-01-10T06:15Z)

api/views/user.py in LibrePhotos before e19e539 has incorrect access control.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/LibrePhotos/librephotos
commit_details: repo_url: https://github.com/LibrePhotos/librephotos
author: derneuere
author_date: 2023-01-09 00:37:41+01:00
msg: Fix incorrect access controll
repository: repo_url: https://github.com/LibrePhotos/librephotos
repo_name: LibrePhotos/librephotos
description: A self-hosted open source photo management service. This is the repository of the backend.
repo_language: Python
owner: LibrePhotos
file_changes: filename: user.py
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 from rest_framework import permissions, status, viewsets
-from rest_framework.permissions import AllowAny, IsAdminUser
+from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
@@ -43,6 +43,15 @@ class RootPathTreeView(APIView):
             logger.exception(str(e))
             return Response({"message": str(e)})
 
+class IsFirstTimeSetupView(APIView):
+    permission_classes = (AllowAny,)
+
+    def get(self, request, format=None):
+        try:
+            return Response({"isFirstTimeSetup": not User.objects.filter(is_superuser=True).exists()})
+        except Exception as e:
+            logger.exception(str(e))
+            return Response({"message": str(e)})
 
 class FirstTimeSetupPermission(permissions.BasePermission):
     message = "Check if the first time setup is done"
@@ -55,7 +64,7 @@ class UserViewSet(viewsets.ModelViewSet):
 
     serializer_class = UserSerializer
 
-    permission_classes = (IsUserOrReadOnly,)
+    permission_classes = (IsAdminUser,)
 
     def get_queryset(self):
         queryset = (
@@ -92,14 +101,15 @@ class UserViewSet(viewsets.ModelViewSet):
             self.permission_classes = [
                 IsRegistrationAllowed | FirstTimeSetupPermission | IsAdminUser
             ]
-        elif self.action == "list":
-            self.permission_classes = (AllowAny,)
-        elif self.request.method == "GET" or self.request.method == "POST":
+        if self.request.method == "POST":
             self.permission_classes = (AllowAny,)
-        else:
-            self.permission_classes = (IsUserOrReadOnly,)
         return super(UserViewSet, self).get_permissions()
 
+    def create(self, request, *args, **kwargs):
+        if User.objects.filter(is_superuser=True).exists() and not request.user.is_superuser:
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
+        return super(UserViewSet, self).create(request, *args, **kwargs)
+
     def retrieve(self, *args, **kwargs):
         return super(UserViewSet, self).retrieve(*args, **kwargs)
 

programming_language: Python
filename: urls.py
change_type: ModificationType.MODIFY
diff: @@ -193,6 +193,7 @@ urlpatterns = [
     url(r"^", include(router.urls)),
     url(r"^admin/", admin.site.urls),
     url(r"^api/sitesettings", views.SiteSettingsView.as_view()),
+    url(r"^api/firsttimesetup", user.IsFirstTimeSetupView.as_view()),
     url(r"^api/dirtree", user.RootPathTreeView.as_view()),
     url(r"^api/labelfaces", faces.SetFacePersonLabel.as_view()),
     url(r"^api/deletefaces", faces.DeleteFaces.as_view()),

programming_language: Python