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

feb-r3 #356

Merged
merged 1 commit into from
Feb 11, 2025
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
2 changes: 1 addition & 1 deletion src/stores/vespaStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ export const useVespaStore = defineStore('vespaStore', {
async authCheck() {
this.loadingAuth = true;
try {
const response = await ApiService.get("/auth-check");
const response = await ApiService.get("/auth-check/");
const data = response.data;
if (data.isAuthenticated && data.user) {
this.user = data.user;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.4 on 2025-02-10 19:37

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('observations', '0034_remove_observation_species'),
]

operations = [
migrations.AlterModelOptions(
name='observation',
options={'ordering': ['id']},
),
migrations.AlterField(
model_name='observation',
name='observation_datetime',
field=models.DateTimeField(blank=True, help_text='Datetime when the observation was made', null=True),
),
]
2 changes: 1 addition & 1 deletion vespadb/observations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class Observation(models.Model):
observer_email = models.EmailField(blank=True, null=True, help_text="Email of the observer")
observer_received_email = models.BooleanField(default=False, help_text="Flag indicating if observer received email")
observer_name = models.CharField(max_length=255, blank=True, null=True, help_text="Name of the observer")
observation_datetime = models.DateTimeField(help_text="Datetime when the observation was made")
observation_datetime = models.DateTimeField(null=True, blank=True, help_text="Datetime when the observation was made")

wn_cluster_id = models.IntegerField(blank=True, null=True, help_text="Cluster ID of the observation")
admin_notes = models.TextField(blank=True, null=True, help_text="Admin notes for the observation")
Expand Down
6 changes: 3 additions & 3 deletions vespadb/observations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,10 @@ def process_data(self, data: list[dict[str, Any]]) -> tuple[list[dict[str, Any]]
errors.append({"record": idx, "error": f"Observation with id {observation_id} not found"})
continue
else: # New record
data_item['created_by'] = self.request.user
data_item['created_by'] = self.request.user.pk if self.request.user else None
if 'created_datetime' not in data_item:
data_item['created_datetime'] = current_time
data_item['modified_by'] = self.request.user
data_item['modified_by'] = self.request.user.pk if self.request.user else None
data_item['modified_datetime'] = current_time

if 'longitude' in data_item and 'latitude' in data_item:
Expand Down Expand Up @@ -719,7 +719,7 @@ def clean_data(self, data_dict: dict[str, Any]) -> dict[str, Any]:
except (ValueError, TypeError):
logger.exception(f"Invalid datetime format for {field}: {data_dict[field]}")
data_dict.pop(field, None)
elif isinstance(data_dict[field], datetime):
elif isinstance(data_dict[field], datetime.datetime):
data_dict[field] = data_dict[field].isoformat()
else:
data_dict.pop(field, None)
Expand Down
2 changes: 1 addition & 1 deletion vespadb/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

urlpatterns = [
path("", include(router.urls)),
path("auth-check", AuthCheck.as_view(), name="auth_check"),
path("auth-check/", AuthCheck.as_view(), name="auth_check"),
path("login/", LoginView.as_view(), name="login"),
path("logout/", LogoutView.as_view(), name="logout"),
path("change-password/", ChangePasswordView.as_view(), name="change_password"),
Expand Down
79 changes: 54 additions & 25 deletions vespadb/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.authtoken.models import Token
from django.middleware.csrf import get_token

from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
Expand Down Expand Up @@ -87,40 +88,68 @@ class LoginView(APIView):

@swagger_auto_schema(
operation_summary="User Login",
operation_description="Authenticate a user by username and password, log them in and return a bearer token.",
operation_description="Authenticate a user with a username and password, log them in, and return a CSRF token.",
request_body=LoginSerializer,
responses={
200: openapi.Response(
description="Login successful",
examples={
"application/json": {
"detail": "Login successful.",
"token": "0123456789abcdef0123456789abcdef01234567"
}
},
schema=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
"detail": openapi.Schema(
type=openapi.TYPE_STRING,
example="Login successful."
),
"csrftoken": openapi.Schema(
type=openapi.TYPE_STRING,
example="abc123csrf"
)
},
),
),
400: openapi.Response(
description="Bad Request",
schema=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
"error": openapi.Schema(
type=openapi.TYPE_STRING,
example="Invalid username or password."
)
},
),
),
400: "Bad Request",
},
)
def post(self, request: Request) -> Response:
"""
Authenticate a user based on username and password, log them in and return a bearer token.
"""
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
user = serializer.validated_data
login(request, user)
# Create or retrieve the token for the user
token, _ = Token.objects.get_or_create(user=user)
return Response(
{
"detail": "Login successful.",
"token": token.key,
},
status=status.HTTP_200_OK,
)
"""
Authenticate a user based on username and password, log them in, and return a CSRF token.
"""
serializer = LoginSerializer(data=request.data)

if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


user = serializer.validated_data
login(request, user)

# Generate CSRF token
csrf_token = get_token(request)

# Prepare response
response_data = {
"detail": "Login successful.",
"csrftoken": csrf_token
}

# Create the response and set CSRF token in both header and cookie
response = Response(response_data, status=status.HTTP_200_OK)
response.set_cookie(
"csrftoken", csrf_token, httponly=False, secure=True, samesite="Lax"
)
response["X-CSRFToken"] = csrf_token # Set CSRF token in response headers

return response

class LogoutView(APIView):
"""API view for user logout."""
Expand Down