Skip to content

Commit

Permalink
[Fixes #11995] Implement POST and PATCH methods for the User API, ref…
Browse files Browse the repository at this point in the history
…actored validation in serializer
  • Loading branch information
RegisSinjari committed Mar 7, 2024
1 parent e6638cf commit ee071e2
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 16 deletions.
35 changes: 35 additions & 0 deletions geonode/base/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#
#########################################################################
import logging

from slugify import slugify
from urllib.parse import urljoin
import json
Expand All @@ -30,6 +31,9 @@
from django.db.models.query import QuerySet
from geonode.people import Roles
from django.http import QueryDict
from django.contrib.auth.hashers import make_password
from django.contrib.auth.password_validation import validate_password
from django.forms import ValidationError as ValidationErrorForm

from deprecated import deprecated
from rest_framework import serializers
Expand Down Expand Up @@ -358,6 +362,37 @@ class Meta:
view_name = "users-list"
fields = ("pk", "username", "first_name", "last_name", "avatar", "perms", "is_superuser", "is_staff", "email")

@staticmethod
def password_validation(password_payload):
try:
validate_password(password_payload)
except ValidationErrorForm as err:
raise serializers.ValidationError(detail=",".join(err.messages))
return make_password(password_payload)

def validate(self, data):
request = self.context["request"]
user = request.user
# only admins/staff can edit these permissions
if not (user.is_superuser or user.is_staff):
data.pop("is_superuser", None)
data.pop("is_staff", None)
# username cant be changed
if request.method in ("PUT", "PATCH"):
data.pop("username", None)
email = data.get("email")
# Email is required on post
if request.method in ("POST") and settings.ACCOUNT_EMAIL_REQUIRED and not email:
raise serializers.ValidationError(detail="email missing from payload")
# email should be unique
if get_user_model().objects.filter(email=email).exists():
raise serializers.ValidationError("A user is already registered with that email")
# password validation
password = request.data.get("password")
if password:
data["password"] = self.password_validation(password)
return data

@classmethod
def setup_eager_loading(cls, queryset):
"""Perform necessary eager loading of data."""
Expand Down
35 changes: 35 additions & 0 deletions geonode/people/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,38 @@ def test_users_register_email_verification(self):
# assert that an email was sent to the email provided in the payload
self.assertEqual(len(email_box), 1)
self.assertTrue(data["email"] in email_box[0].to)

def test_users_api_patch_password_from_admin(self):
bobby = get_user_model().objects.get(username="bobby")
admin = get_user_model().objects.get(username="admin")

self.assertTrue(self.client.login(username="admin", password="admin"))
self.assertTrue(admin.is_authenticated)

# admin wants to edit his bobby's data
data = {"password": "@!2XJSL_S&V^0nt000"}
# Admin is superuser or staff
self.assertTrue(admin.is_superuser or admin.is_staff)
old_pass = bobby.password

url = f"{reverse('users-list')}/{bobby.pk}"
response = self.client.patch(url, data=data, content_type="application/json")

# admin is permitted to update bobby's data
self.assertEqual(response.status_code, 200)
# bobbys password has changed
bobby.refresh_from_db()
# asserting not equal from the password salt
self.assertNotEqual(bobby.password, old_pass)

def test_users_api_add_existing_email(self):
data = {"username": "teddy", "password": "@!2XJSL_S&V^0nt", "email": "[email protected]"}
self.client.login(username="admin", password="admin")
response = self.client.post(reverse("users-list"), data=data, content_type="application/json")
self.assertEqual(response.status_code, 201)

# try to readd the same email
data = {"username": "teddy1", "password": "@!2XJSL_S&V^0nt", "email": "[email protected]"}
response = self.client.post(reverse("users-list"), data=data, content_type="application/json")
self.assertEqual(response.status_code, 400)
self.assertTrue("A user is already registered with that email" in response.json()["errors"])
21 changes: 5 additions & 16 deletions geonode/people/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,26 +204,15 @@ def perform_create(self, serializer):
user = self.request.user
if not (user.is_superuser or user.is_staff):
raise PermissionDenied()

email_payload = self.request.data.get("email", "")
password_payload = self.request.data.get("password", "")

if settings.ACCOUNT_EMAIL_REQUIRED and email_payload == "":
raise ValidationError(detail="email missing from payload")
self.request.data["password"] = password_validation(password_payload)
instance = serializer.save()
return instance

def update(self, request, *args, **kwargs):
kwargs["partial"] = True
user = self.request.user
if not (user.is_superuser or user.is_staff):
request.data.pop("is_superuser", None)
request.data.pop("is_staff", None)
password_payload = self.request.data.get("password", "")
if password_payload:
request.data["password"] = password_validation(password_payload)
return super().update(request, *args, **kwargs)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)

@extend_schema(
methods=["get"],
Expand Down

0 comments on commit ee071e2

Please sign in to comment.