From 1e796dbc76ab0cbd9d8f4ce29997b8948d50c80a Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Tue, 13 Aug 2024 11:08:05 -0400 Subject: [PATCH] Add UserView.get() to retrieve logged-in user details re #11261 --- arches/app/templates/arches_urls.htm | 1 + arches/app/views/api/user.py | 25 +++++++++++++ arches/urls.py | 3 +- tests/views/api/test_user.py | 52 ++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 arches/app/views/api/user.py create mode 100644 tests/views/api/test_user.py diff --git a/arches/app/templates/arches_urls.htm b/arches/app/templates/arches_urls.htm index 370f60737c1..d834a95c16f 100644 --- a/arches/app/templates/arches_urls.htm +++ b/arches/app/templates/arches_urls.htm @@ -120,6 +120,7 @@ api_bulk_disambiguated_resource_instance="{% url 'api_bulk_disambiguated_resource_instance' %}" api_resources='(resourceid)=>{return "{% url "resources" "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" %}".replace("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", resourceid)}' api_search_component_data='"{% url "api_search_component_data" "aaaa"%}".replace("aaaa", "")' + api_user="{% url 'api_user' %}" api_user_incomplete_workflows="{% url 'api_user_incomplete_workflows' %}" api_get_frontend_i18n_data="{% url 'get_frontend_i18n_data' %}" geojson="{% url 'geojson' %}" diff --git a/arches/app/views/api/user.py b/arches/app/views/api/user.py new file mode 100644 index 00000000000..d1dd0f42df5 --- /dev/null +++ b/arches/app/views/api/user.py @@ -0,0 +1,25 @@ +from http import HTTPStatus + +from django.utils.translation import gettext as _ + +from arches.app.utils.betterJSONSerializer import JSONSerializer +from arches.app.utils.response import JSONErrorResponse, JSONResponse +from arches.app.views.api import APIBase + + +class UserView(APIBase): + http_method_names = ["get"] + + def get(self, request): + if not request.user.is_active: + return JSONErrorResponse( + title=_("Login required"), + message=_("This account is no longer active."), + status=HTTPStatus.FORBIDDEN, + ) + + # N.B.: SetAnonymousUser middleware provides an anonymous User, + # so don't infer from a 200 OK (or even is_authenticated, if we + # later serialize that) that you have an authenticated user. + fields = {"first_name", "last_name", "username"} + return JSONResponse(JSONSerializer().serialize(request.user, fields=fields)) diff --git a/arches/urls.py b/arches/urls.py index c40ec940c16..10acff98a83 100644 --- a/arches/urls.py +++ b/arches/urls.py @@ -21,7 +21,7 @@ from django.contrib.auth import views as auth_views from django.urls import include, path, re_path from arches.app.views import concept, main, map, search, graph, api -from arches.app.views.api import auth as api_auth +from arches.app.views.api import auth as api_auth, user as api_user from arches.app.views.admin import ReIndexResources, ClearUserPermissionCache from arches.app.views.etl_manager import ETLManagerView from arches.app.views.file import FileView, TempFileView @@ -572,6 +572,7 @@ ), path("api/login", api_auth.Login.as_view(), name="api_login"), path("api/logout", api_auth.Logout.as_view(), name="api_logout"), + path("api/user", api_user.UserView.as_view(), name="api_user"), re_path( r"^api/tiles/(?P%s|())$" % (uuid_regex), api.Tile.as_view(), diff --git a/tests/views/api/test_user.py b/tests/views/api/test_user.py new file mode 100644 index 00000000000..d19bf22c9cf --- /dev/null +++ b/tests/views/api/test_user.py @@ -0,0 +1,52 @@ +from http import HTTPStatus + +from django.contrib.auth.models import User +from django.test import TestCase +from django.urls import reverse + +# these tests can be run from the command line via +# python manage.py test tests.views.api.test_user --settings="tests.test_settings" + + +class UserAPITests(TestCase): + @classmethod + def setUpTestData(cls): + cls.visitor = User.objects.create( + username="visitor", + first_name="Esperanza", + last_name="Spalding", + ) + cls.visitor.set_password("jazz") + cls.visitor.save() + + def test_logged_in_user(self): + self.client.force_login(self.visitor) + response = self.client.get(reverse("api_user")) + self.assertContains( + response, + '{"first_name": "Esperanza", "last_name": "Spalding", "username": "visitor"}', + status_code=HTTPStatus.OK, + ) + self.assertNotContains(response, "password") + + def test_anonymous_user(self): + response = self.client.get(reverse("api_user")) + self.assertContains( + response, + '{"first_name": "", "last_name": "", "username": "anonymous"}', + status_code=HTTPStatus.OK, + ) + self.assertNotContains(response, "password") + + def test_inactive_user(self): + self.visitor.is_active = False + self.visitor.save() + + self.client.force_login(self.visitor) + with self.assertLogs("django.request", level="WARNING"): + response = self.client.get(reverse("api_user")) + self.assertContains( + response, + "This account is no longer active.", + status_code=HTTPStatus.FORBIDDEN, + )