Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from .notification import Notification
from .friendship import Friendship
from .like import Like
from .profile import Profile
from .profile import Profile
from .statustypes import StatusTypes
19 changes: 19 additions & 0 deletions api/models/friendship.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ def save(self, *args, **kwargs):
if self.requesting == self.requested:
raise ValidationError("User cannot send friendship requests to themselves.")
super().save(*args, **kwargs)

def accept(self):
accepted_status = StatusTypes.objects.get(status='ACCEPTED') # pylint: disable=no-member
self.status = accepted_status
self.save()

def reject(self):
rejected_status = StatusTypes.objects.get(status='REJECTED') # pylint: disable=no-member
self.status = rejected_status
self.save()

def terminate(self):
terminated_status = StatusTypes.objects.get(status='TERMINATED') # pylint: disable=no-member
self.status = terminated_status
self.save()

class Meta:
constraints = [
Expand All @@ -38,4 +53,8 @@ class Meta:
fields=['requested_id', 'requesting_id'],
name='unique_reverse_friendship'
)
]
indexes = [
models.Index(fields=['status'], name='friendship_status_idx'),
models.Index(fields=['requesting', 'requested'], name='friendship_users_idx')
]
2 changes: 2 additions & 0 deletions api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from api import views
from api.views import PostViewSet, Profiles, Users, test_view #Edwin Moz added Post import
from .views.auth import AuthViewSet
from .views.friendship import Friendships

router = routers.DefaultRouter(trailing_slash=False)

Expand All @@ -18,6 +19,7 @@
## ///////////END CODE ADDED BY EDWIN MOZ ##
router.register(r"profiles", Profiles, "profile")
router.register(r'auth', AuthViewSet, basename='auth')
router.register(r'friendships', Friendships, basename='friendship')

urlpatterns = [
path('test/', views.test_view),
Expand Down
1 change: 1 addition & 0 deletions api/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
## END CODE ADDED BY EDWIN MOZ/////////// ##
from .profile import Profiles
from .user import Users
from .friendship import Friendships
from .test import test_view
169 changes: 169 additions & 0 deletions api/views/friendship.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from django.db.models import Q
from django.shortcuts import get_object_or_404
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from django.db import IntegrityError
from api.models import Friendship, StatusTypes
from rest_framework import serializers, permissions

class FriendshipSerializer(serializers.ModelSerializer):
class Meta:
model = Friendship
fields = ('id', 'requesting', 'requested', 'created_at', 'updated_at', 'status')
read_only_fields = ('requesting', 'created_at', 'updated_at', 'status')

def to_representation(self, instance):
representation = super().to_representation(instance)
# Add additional user details
representation['requesting'] = {
'id': instance.requesting.id,
'username': instance.requesting.username,
}
representation['requested'] = {
'id': instance.requested.id,
'username': instance.requested.username,
}
return representation

class Friendships(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
queryset = Friendship.objects.all() # pylint: disable=no-member
serializer_class = FriendshipSerializer

def get_queryset(self):
"""
Filter friendships based on the authenticated user
"""
user = self.request.user

# Filter by status if provided in query params
status_filter = self.request.query_params.get('status', None)
queryset = Friendship.objects.filter( # pylint: disable=no-member
Q(requesting=user) | Q(requested=user)
)

if status_filter:
queryset = queryset.filter(status__status=status_filter.upper())

return queryset

def create(self, request):
try:
# Get pending status
pending_status = StatusTypes.objects.get(status='PENDING') # pylint: disable=no-member

# Validate the input data
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

# Save with the requesting user and status
friendship = serializer.save(
requesting=request.user,
status=pending_status
)

return Response(
self.get_serializer(friendship).data,
status=status.HTTP_201_CREATED
)

except IntegrityError:
return Response(
{"detail": "Friendship request already exists"},
status=status.HTTP_400_BAD_REQUEST
)
except ValidationError as e:
return Response(
{"detail": str(e)},

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that detailed exception messages are not exposed to the end user. Instead, we should log the detailed error message on the server and return a generic error message to the user. This can be achieved by modifying the exception handling blocks to log the exception details and return a generic error message.

Specifically, we will:

  1. Import the logging module to log the exception details.
  2. Replace the detailed error message in the response with a generic message.
Suggested changeset 1
api/views/friendship.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/views/friendship.py b/api/views/friendship.py
--- a/api/views/friendship.py
+++ b/api/views/friendship.py
@@ -9,3 +9,3 @@
 from rest_framework import serializers, permissions
-
+import logging
 class FriendshipSerializer(serializers.ModelSerializer):
@@ -77,4 +77,5 @@
         except ValidationError as e:
+            logging.error(f"Validation error: {str(e)}")
             return Response(
-                {"detail": str(e)},
+                {"detail": "Invalid input data"},
                 status=status.HTTP_400_BAD_REQUEST
@@ -87,4 +88,5 @@
         except Exception as e:
+            logging.error(f"Unexpected error: {str(e)}")
             return Response(
-                {"detail": f"Failed to create friendship request: {str(e)}"},
+                {"detail": "Failed to create friendship request"},
                 status=status.HTTP_500_INTERNAL_SERVER_ERROR
EOF
@@ -9,3 +9,3 @@
from rest_framework import serializers, permissions

import logging
class FriendshipSerializer(serializers.ModelSerializer):
@@ -77,4 +77,5 @@
except ValidationError as e:
logging.error(f"Validation error: {str(e)}")
return Response(
{"detail": str(e)},
{"detail": "Invalid input data"},
status=status.HTTP_400_BAD_REQUEST
@@ -87,4 +88,5 @@
except Exception as e:
logging.error(f"Unexpected error: {str(e)}")
return Response(
{"detail": f"Failed to create friendship request: {str(e)}"},
{"detail": "Failed to create friendship request"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
Copilot is powered by AI and may make mistakes. Always verify output.
status=status.HTTP_400_BAD_REQUEST
)
except StatusTypes.DoesNotExist: # pylint: disable=no-member
return Response(
{"detail": "Pending status type not found"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
except Exception as e:
return Response(
{"detail": f"Failed to create friendship request: {str(e)}"},

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that detailed exception messages are not exposed to the end user. Instead, we should log the detailed error message on the server and return a generic error message to the user. This can be achieved by modifying the exception handling block to log the exception and return a generic error message.

  1. Import the logging module to enable logging of exceptions.
  2. Replace the detailed error message in the response with a generic error message.
  3. Log the detailed exception message on the server.
Suggested changeset 1
api/views/friendship.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/views/friendship.py b/api/views/friendship.py
--- a/api/views/friendship.py
+++ b/api/views/friendship.py
@@ -9,2 +9,5 @@
 from rest_framework import serializers, permissions
+import logging
+
+logger = logging.getLogger(__name__)
 
@@ -87,4 +90,5 @@
         except Exception as e:
+            logger.error("Failed to create friendship request", exc_info=True)
             return Response(
-                {"detail": f"Failed to create friendship request: {str(e)}"},
+                {"detail": "An internal error has occurred."},
                 status=status.HTTP_500_INTERNAL_SERVER_ERROR
EOF
@@ -9,2 +9,5 @@
from rest_framework import serializers, permissions
import logging

logger = logging.getLogger(__name__)

@@ -87,4 +90,5 @@
except Exception as e:
logger.error("Failed to create friendship request", exc_info=True)
return Response(
{"detail": f"Failed to create friendship request: {str(e)}"},
{"detail": "An internal error has occurred."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
Copilot is powered by AI and may make mistakes. Always verify output.
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

def update(self, request, pk=None):
friendship = self.get_object()

# Only the requested user can update the friendship status
if request.user != friendship.requested:
return Response(
{"detail": "Only the requested user can update friendship status"},
status=status.HTTP_403_FORBIDDEN
)

try:
new_status = StatusTypes.objects.get(status=request.data.get('status', '').upper()) # pylint: disable=no-member
friendship.status = new_status
friendship.save()

serializer = self.get_serializer(friendship)
return Response(serializer.data)

except StatusTypes.DoesNotExist: # pylint: disable=no-member
return Response(
{"detail": "Invalid status"},
status=status.HTTP_400_BAD_REQUEST
)

@action(detail=True, methods=['post'])
def accept(self, request, pk=None):
"""Accept a friend request"""
friendship = self.get_object()
if request.user != friendship.requested:
return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN)

try:
accepted_status = StatusTypes.objects.get(status='ACCEPTED') # pylint: disable=no-member
friendship.status = accepted_status
friendship.save()
serializer = self.get_serializer(friendship)
return Response(serializer.data)
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that detailed exception messages are not exposed to the user. Instead, we should log the detailed error message on the server and return a generic error message to the user. This can be achieved by modifying the exception handling in the accept, reject, and terminate methods.

  1. Import the logging module to enable logging of detailed error messages.
  2. Replace the current exception handling code to log the detailed error message and return a generic error message to the user.
Suggested changeset 1
api/views/friendship.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/views/friendship.py b/api/views/friendship.py
--- a/api/views/friendship.py
+++ b/api/views/friendship.py
@@ -9,3 +9,3 @@
 from rest_framework import serializers, permissions
-
+import logging
 class FriendshipSerializer(serializers.ModelSerializer):
@@ -130,3 +130,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error accepting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -149,3 +150,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error rejecting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -168,2 +170,3 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
+            logging.error("Error terminating friendship: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
EOF
@@ -9,3 +9,3 @@
from rest_framework import serializers, permissions

import logging
class FriendshipSerializer(serializers.ModelSerializer):
@@ -130,3 +130,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error accepting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -149,3 +150,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error rejecting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -168,2 +170,3 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error terminating friendship: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
Copilot is powered by AI and may make mistakes. Always verify output.

@action(detail=True, methods=['post'])
def reject(self, request, pk=None):
"""Reject a friend request"""
friendship = self.get_object()
if request.user != friendship.requested:
return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN)

if friendship.status.status != "PENDING":
return Response({"detail": "This request is not pending."}, status=status.HTTP_400_BAD_REQUEST)

try:
rejected_status = StatusTypes.objects.get(status='REJECTED') # pylint: disable=no-member
friendship.status = rejected_status
friendship.save()
serializer = self.get_serializer(friendship)
return Response(serializer.data)
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that detailed exception messages are not exposed to the user. Instead, we should log the detailed error message on the server and return a generic error message to the user. This can be achieved by modifying the exception handling blocks to log the exception and return a generic error message.

  1. Import the logging module to enable logging of exceptions.
  2. Replace the lines that return the exception message with lines that log the exception and return a generic error message.
Suggested changeset 1
api/views/friendship.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/views/friendship.py b/api/views/friendship.py
--- a/api/views/friendship.py
+++ b/api/views/friendship.py
@@ -9,2 +9,3 @@
 from rest_framework import serializers, permissions
+import logging
 
@@ -130,3 +131,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error accepting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -149,3 +151,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error rejecting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -168,2 +171,3 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
+            logging.error("Error terminating friendship: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
EOF
@@ -9,2 +9,3 @@
from rest_framework import serializers, permissions
import logging

@@ -130,3 +131,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error accepting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -149,3 +151,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error rejecting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -168,2 +171,3 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error terminating friendship: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
Copilot is powered by AI and may make mistakes. Always verify output.

@action(detail=True, methods=['post'])
def terminate(self, request, pk=None):
"""Terminate an existing friendship"""
friendship = self.get_object()
if request.user not in [friendship.requesting, friendship.requested]:
return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN)

if friendship.status.status != "ACCEPTED":
return Response({"detail": "Only accepted friendships can be terminated."}, status=status.HTTP_400_BAD_REQUEST)

try:
terminated_status = StatusTypes.objects.get(status='TERMINATED') # pylint: disable=no-member
friendship.status = terminated_status
friendship.save()
serializer = self.get_serializer(friendship)
return Response(serializer.data)
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that detailed exception messages are not exposed to the end user. Instead, we should log the detailed error message on the server and return a generic error message to the user. This can be achieved by modifying the exception handling code to log the exception and return a generic error message.

  1. Import the logging module to enable logging of exceptions.
  2. Replace the lines that return the exception message with code that logs the exception and returns a generic error message.
Suggested changeset 1
api/views/friendship.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/views/friendship.py b/api/views/friendship.py
--- a/api/views/friendship.py
+++ b/api/views/friendship.py
@@ -9,2 +9,3 @@
 from rest_framework import serializers, permissions
+import logging
 
@@ -130,3 +131,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error accepting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -149,3 +151,4 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+            logging.error("Error rejecting friendship request: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
     
@@ -168,2 +171,3 @@
         except Exception as e:
-            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
+            logging.error("Error terminating friendship: %s", str(e))
+            return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
EOF
@@ -9,2 +9,3 @@
from rest_framework import serializers, permissions
import logging

@@ -130,3 +131,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error accepting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -149,3 +151,4 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error rejecting friendship request: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)

@@ -168,2 +171,3 @@
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
logging.error("Error terminating friendship: %s", str(e))
return Response({"detail": "An internal error has occurred."}, status=status.HTTP_400_BAD_REQUEST)
Copilot is powered by AI and may make mistakes. Always verify output.
2 changes: 2 additions & 0 deletions codefire_python_backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from dotenv import load_dotenv
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

Expand Down
Loading