Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added auth headers in requests #37

Merged
merged 1 commit into from
Dec 19, 2024
Merged
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
6 changes: 3 additions & 3 deletions eudr_backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,17 @@ def extract_data_from_file(file, data_format):
raise e


def store_failed_file_in_s3(file, user, file_name):
def store_file_in_s3(file, user, file_name, is_failed=False):
# Store the file in the AWS S3 bucket's failed directory
if file:
s3 = boto3.client('s3', aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY)
s3.upload_fileobj(file, settings.AWS_STORAGE_BUCKET_NAME,
f'failed/{user.username}_{file_name}', ExtraArgs={'ACL': 'public-read'})
f'{'failed' if is_failed else 'processed'}/{user.username}_{file_name}', ExtraArgs={'ACL': 'public-read'})


def handle_failed_file_entry(file_serializer, file, user):
if "id" in file_serializer.data:
EUDRUploadedFilesModel.objects.get(
id=file_serializer.data.get("id")).delete()
store_failed_file_in_s3(file, user, file_serializer.data.get('file_name'))
store_file_in_s3(file, user, file_serializer.data.get('file_name'))
52 changes: 44 additions & 8 deletions eudr_backend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from datetime import timedelta
from eudr_backend.tasks import update_geoid
from eudr_backend.util_classes import IsSuperUser
from eudr_backend.utils import extract_data_from_file, flatten_multipolygon_coordinates, generate_access_code, handle_failed_file_entry, store_failed_file_in_s3, transform_csv_to_json, transform_db_data_to_geojson
from eudr_backend.utils import extract_data_from_file, flatten_multipolygon_coordinates, generate_access_code, handle_failed_file_entry, store_file_in_s3, transform_csv_to_json, transform_db_data_to_geojson
from eudr_backend.validators import validate_csv, validate_geojson
from .serializers import (
EUDRCollectionSiteModelSerializer,
Expand Down Expand Up @@ -571,7 +571,7 @@ def create_farm_data(request):

if errors:
# Custom function to handle S3 upload
store_failed_file_in_s3(file, request.user, file_name)
store_file_in_s3(file, request.user, file_name)
return Response({'errors': errors}, status=status.HTTP_400_BAD_REQUEST)

if data_format == 'csv':
Expand Down Expand Up @@ -609,6 +609,7 @@ def create_farm_data(request):
# Proceed with other operations...
update_geoid(repeat=60,
user_id=request.user.username if request.user.is_authenticated else "admin")
store_file_in_s3(file, request.user, file_name, True) if file else None
return Response({'message': 'File/data processed successfully', 'file_id': file_id}, status=status.HTTP_201_CREATED)


Expand Down Expand Up @@ -1475,14 +1476,32 @@ def retrieve_map_data(request):
},
),
),
404: openapi.Response(
description="Farm does not exist",
schema=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
"message": openapi.Schema(type=openapi.TYPE_STRING),
},
),
examples={
"application/json": {
"message": "Farm does not exist",
},
},
),
},
tags=["Farm Data Management"]
)
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def retrieve_farm_detail(request, pk):
data = EUDRFarmModel.objects.get(id=pk)
serializer = EUDRFarmModelSerializer(data, many=False)
return Response(serializer.data)
try:
data = EUDRFarmModel.objects.get(id=pk)
serializer = EUDRFarmModelSerializer(data, many=False)
return Response(serializer.data)
except EUDRFarmModel.DoesNotExist:
return Response({'message': 'Farm does not exist'}, status=status.HTTP_404_NOT_FOUND)


@swagger_auto_schema(
Expand Down Expand Up @@ -1517,15 +1536,32 @@ def retrieve_farm_detail(request, pk):
),
),
),
404: openapi.Response(
description="Farm does not exist",
schema=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
"message": openapi.Schema(type=openapi.TYPE_STRING),
},
),
examples={
"application/json": {
"message": "Farm does not exist",
},
},
),
},
tags=["Farm Data Management"]
)
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def retrieve_farm_data_from_file_id(request, pk):
data = EUDRFarmModel.objects.filter(file_id=pk)
serializer = EUDRFarmModelSerializer(data, many=True)
return Response(serializer.data)
try:
data = EUDRFarmModel.objects.filter(file_id=pk)
serializer = EUDRFarmModelSerializer(data, many=True)
return Response(serializer.data)
except EUDRFarmModel.DoesNotExist:
return Response({'message': 'Farm does not exist'}, status=status.HTTP_404_NOT_FOUND)


@swagger_auto_schema(
Expand Down
96 changes: 59 additions & 37 deletions my_eudr_app/auth_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import redirect, render
from django.contrib.auth import login, logout, update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm, PasswordResetForm, SetPasswordForm
from django.contrib import messages
from django.contrib.auth.models import User
Expand All @@ -14,14 +13,13 @@
from django.core.mail import send_mail
from django.urls import reverse
from django.conf import settings
from rest_framework.decorators import api_view
from rest_framework.decorators import api_view, permission_classes
from rest_framework.authtoken.models import Token
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework import status
from rest_framework.response import Response

from eudr_backend.serializers import EUDRUserModelSerializer
from rest_framework.permissions import IsAuthenticated


@swagger_auto_schema(method='post', request_body=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
Expand Down Expand Up @@ -188,9 +186,9 @@ def signup_view(request):
tags=["Auth Management"],
operation_summary="Endpoint that logs in a user"
)
@ swagger_auto_schema(method='get', operation_summary="Endpoint that returns sign up page", security=[],
tags=["Auth Management"])
@ api_view(['GET', 'POST'])
@swagger_auto_schema(method='get', operation_summary="Endpoint that returns login page", security=[],
tags=["Auth Management"])
@api_view(['GET', 'POST'])
def login_view(request):
"""
Handle user login for both HTML rendering and API requests.
Expand All @@ -201,52 +199,51 @@ def login_view(request):
return render(request, 'auth/login.html', {'form': form})

if request.method == 'POST':
# Handle form submission for POST requests
if request.content_type == 'application/json':
# JSON API request
form = AuthenticationForm(request, data=request.data)
else:
# Form submission (HTML POST)
form = AuthenticationForm(request, data=request.POST)
# Determine data source based on content type
data = request.data if request.content_type == 'application/json' else request.POST
form = AuthenticationForm(request, data=data)

if form.is_valid():
user = form.get_user()
token = Token.objects.get_or_create(user=user)[0]
# Generate or retrieve the token for the authenticated user
token, _ = Token.objects.get_or_create(user=user)

# Handle form-based login
login(request, user)
# Handle API login
if request.content_type == 'application/json':
# Respond with JSON for API requests
return Response({
"message": "Login successful",
"user": {
"username": user.username
},
"token": token.key
}, status=status.HTTP_200_OK)
else:
login(request, user)
# Redirect or render another page for HTML form submission
return redirect('index')

return redirect('index')

else:
# Invalid credentials
if request.content_type == 'application/json':
# Respond with JSON for invalid API request
return Response({
"message": "Invalid username or password"
"message": "Invalid username or password",
"errors": form.errors
}, status=status.HTTP_400_BAD_REQUEST)
else:
messages.error(request, 'Invalid username or password')
# Re-render the login page with form errors for HTML
return render(request, 'auth/login.html', {'form': form})

messages.error(request, 'Invalid username or password')
return render(request, 'auth/login.html', {'form': form})


@ login_required
@ swagger_auto_schema(method='post', request_body=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
@permission_classes([IsAuthenticated])
@swagger_auto_schema(method='post', request_body=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
'first_name': openapi.Schema(type=openapi.TYPE_STRING, description='First Name'),
'last_name': openapi.Schema(type=openapi.TYPE_STRING, description='Last Name'),
'email': openapi.Schema(type=openapi.TYPE_STRING, description='Email')
}, default={'first_name': 'John', 'last_name': 'Doe', 'email': '[email protected]'}),
responses={200: openapi.Response(description="Successful Response", schema=openapi.Schema(
type=openapi.TYPE_OBJECT, properties={'message': openapi.Schema(type=openapi.TYPE_STRING, description='Message')}))},
tags=["User Management"], operation_summary="Endpoint that allows a user to update their password")
@ api_view(['POST'])
@api_view(['POST'])
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
Expand All @@ -264,14 +261,39 @@ def change_password(request):
return render(request, 'change_password.html', {'form': form})


@ login_required
@swagger_auto_schema(method='post', tags=["Auth Management"], operation_summary="Endpoint that logs out a user")
@permission_classes([IsAuthenticated])
@api_view(['POST'])
def logout_view(request):
if request.method == 'POST':
"""
Handle logout for token-based users.
"""
try:
# Perform operations that require a valid token
user = request.user

# For API requests
if request.content_type == 'application/json':
response_data = {"message": "Logged out successfully"}
status_code = status.HTTP_200_OK
else:
# For non-API requests, prepare a redirect response
response_data = redirect('login')

# Delete the token after preparing the response
logout(request)
return redirect('login')
Token.objects.get(user=user).delete()

# Return the appropriate response
return Response(response_data, status=status_code) if isinstance(response_data, dict) else response_data

@ swagger_auto_schema(method='post', request_body=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
except Token.DoesNotExist:
return Response({"message": "Token does not exist or already invalidated"}, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return Response({"message": f"An error occurred: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


@swagger_auto_schema(method='post', request_body=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
'email': openapi.Schema(type=openapi.TYPE_STRING, description='Email')
}, default={'email': '[email protected]'}), security=[],
tags=["User Management"], operation_summary="Endpoint that sends a password reset link to a user's email", responses={
Expand Down Expand Up @@ -305,7 +327,7 @@ def logout_view(request):
)
}
)
@ api_view(['POST'])
@api_view(['POST'])
def password_reset_request(request):
if request.method == "POST":
password_reset_form = PasswordResetForm(request.POST)
Expand Down Expand Up @@ -341,9 +363,9 @@ def password_reset_request(request):
return render(request, "auth/password_reset.html", {"form": password_reset_form})


@ swagger_auto_schema(method='get', security=[],
tags=["User Management"], operation_summary="Endpoint that allows a user to reset their password")
@ api_view(['GET'])
@swagger_auto_schema(method='get', security=[],
tags=["User Management"], operation_summary="Endpoint that allows a user to reset their password")
@api_view(['GET'])
def password_reset_confirm(request, uidb64=None, token=None):
try:
uid = urlsafe_base64_decode(uidb64).decode()
Expand Down
19 changes: 13 additions & 6 deletions my_eudr_app/map_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
import geemap.foliumap as geemap
from django.http import JsonResponse
from django.utils import timezone
from django.contrib.auth.decorators import login_required
import requests
from shapely import Polygon
from eudr_backend.utils import flatten_multipolygon_coordinates, is_valid_polygon, reverse_polygon_points
from eudr_backend.settings import initialize_earth_engine
from my_eudr_app.ee_images import combine_commodities_images, combine_disturbances_after_2020_images, combine_disturbances_before_2020_images, combine_forest_cover_images
from eudr_backend.models import EUDRSharedMapAccessCodeModel
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated


@login_required
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def map_view(request):
fileId = request.GET.get('file-id')
accessCode = request.GET.get('access-code')
Expand Down Expand Up @@ -58,8 +60,8 @@ def map_view(request):
try:
# Fetch data from the RESTful API endpoint.
base_url = f"{request.scheme}://{request.get_host()}"
response = requests.get(f"""{base_url}/api/farm/map/list/""") if not fileId and not farmId else requests.get(f"""{base_url}/api/farm/list/{farmId}""") if farmId else requests.get(
f"""{base_url}/api/farm/list/file/{fileId}/""") if not overLap else requests.get(f"""{base_url}/api/farm/overlapping/{fileId}/""")
response = requests.get(f"""{base_url}/api/farm/map/list/""", headers={'Authorization': f"Token {request.user.auth_token}"}) if not fileId and not farmId else requests.get(f"""{base_url}/api/farm/list/{farmId}""", headers={'Authorization': f"Token {request.user.auth_token}"}) if farmId else requests.get(
f"""{base_url}/api/farm/list/file/{fileId}/""", headers={'Authorization': f"Token {request.user.auth_token}"}) if not overLap else requests.get(f"""{base_url}/api/farm/overlapping/{fileId}/""", headers={'Authorization': f"Token {request.user.auth_token}"})
if response.status_code == 200:
farms = [response.json()] if farmId else response.json()
if len(farms) > 0:
Expand Down Expand Up @@ -155,8 +157,11 @@ def map_view(request):

if farm['polygon_type'] != 'Point':
farm_polygon = Polygon(polygon[0])
is_overlapping = any(farm_polygon.overlaps(
Polygon(other_farm['polygon'][0])) for other_farm in farms)
try:
is_overlapping = any(farm_polygon.overlaps(
Polygon(other_farm['polygon'][0])) for other_farm in farms)
except BaseException as e:
is_overlapping = False

# Define GeoJSON data for Folium
js = {
Expand Down Expand Up @@ -240,6 +245,8 @@ def map_view(request):
print("Failed to fetch data from the API")
except BaseException:
return JsonResponse({"message": "Failed to fetch data from the API"}, status=500)
except Exception as e:
return JsonResponse({"message": "An error occurred"}, status=500)

# Add protected areas layer
protected_areas_vis = {'palette': ['#585858']}
Expand Down
Loading
Loading