Skip to content

Commit

Permalink
Merge pull request #37 from agstack/im-ui-integration
Browse files Browse the repository at this point in the history
added auth headers in requests
  • Loading branch information
jntare-tns authored Dec 19, 2024
2 parents 76871ca + f345aba commit 0f4cd5d
Show file tree
Hide file tree
Showing 12 changed files with 436 additions and 187 deletions.
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

0 comments on commit 0f4cd5d

Please sign in to comment.