Skip to content

Commit abe558e

Browse files
committed
WIP Login api
fixes #5932
1 parent 61120d4 commit abe558e

File tree

8 files changed

+132
-2
lines changed

8 files changed

+132
-2
lines changed

CHANGES/5932.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added a login api endpoint to result in an authorization cookie from any other sort of feasible authentication.

pulpcore/app/apps.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ def ready(self):
265265

266266
def _populate_access_policies(sender, apps, verbosity, **kwargs):
267267
from pulpcore.app.util import get_view_urlpattern
268+
from pulpcore.app.viewsets import LoginViewSet
268269

269270
try:
270271
AccessPolicy = apps.get_model("core", "AccessPolicy")
@@ -273,7 +274,8 @@ def _populate_access_policies(sender, apps, verbosity, **kwargs):
273274
print(_("AccessPolicy model does not exist. Skipping initialization."))
274275
return
275276

276-
for viewset_batch in sender.named_viewsets.values():
277+
extra_viewsets = [LoginViewSet]
278+
for viewset_batch in list(sender.named_viewsets.values()) + [extra_viewsets]:
277279
for viewset in viewset_batch:
278280
access_policy = getattr(viewset, "DEFAULT_ACCESS_POLICY", None)
279281
if access_policy is not None:

pulpcore/app/serializers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
GroupRoleSerializer,
114114
GroupSerializer,
115115
GroupUserSerializer,
116+
LoginSerializer,
116117
NestedRoleSerializer,
117118
RoleSerializer,
118119
UserRoleSerializer,

pulpcore/app/serializers/user.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from gettext import gettext as _
44

55
from django.contrib.auth import get_user_model
6+
from django.contrib.auth import login as auth_login
67
from django.contrib.auth.models import Permission
78
from django.contrib.auth.hashers import make_password
89
from django.contrib.auth.password_validation import validate_password
@@ -490,3 +491,14 @@ def validate(self, data):
490491
)
491492
self.group_role_pks.append(qs.get().pk)
492493
return data
494+
495+
496+
class LoginSerializer(serializers.Serializer):
497+
pulp_href = IdentityField(view_name="users-detail")
498+
prn = PRNField()
499+
username = serializers.CharField(read_only=True)
500+
501+
def create(self, validated_data):
502+
user = self.context["request"].user
503+
auth_login(self.context["request"], user)
504+
return user

pulpcore/app/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from pulpcore.app.viewsets import (
2424
ListRepositoryVersionViewSet,
25+
LoginViewSet,
2526
OrphansCleanupViewset,
2627
ReclaimSpaceViewSet,
2728
)
@@ -152,6 +153,7 @@ class PulpDefaultRouter(routers.DefaultRouter):
152153
vs_tree.add_decendent(ViewSetNode(viewset))
153154

154155
special_views = [
156+
path("login/", LoginViewSet.as_view()),
155157
path("repair/", RepairView.as_view()),
156158
path(
157159
"orphans/cleanup/",

pulpcore/app/viewsets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
GroupViewSet,
7777
GroupRoleViewSet,
7878
GroupUserViewSet,
79+
LoginViewSet,
7980
RoleViewSet,
8081
UserViewSet,
8182
UserRoleViewSet,

pulpcore/app/viewsets/user.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from gettext import gettext as _
22

33
from django.contrib.auth import get_user_model
4+
from django.contrib.auth import logout as auth_logout
45
from django.contrib.contenttypes.models import ContentType
56
from django.core.exceptions import FieldError
67
from django.shortcuts import get_object_or_404
78
from django_filters.rest_framework import filters
89
from django.db.models import Q, Count
910
from django.contrib.auth.models import Permission
1011

11-
from rest_framework import mixins, status
12+
from rest_framework import generics, mixins, status
1213
from rest_framework.exceptions import PermissionDenied
1314
from rest_framework.permissions import SAFE_METHODS
1415
from rest_framework.response import Response
@@ -24,6 +25,7 @@
2425
GroupSerializer,
2526
GroupUserSerializer,
2627
GroupRoleSerializer,
28+
LoginSerializer,
2729
RoleSerializer,
2830
UserSerializer,
2931
UserRoleSerializer,
@@ -422,3 +424,35 @@ class GroupRoleViewSet(
422424
serializer_class = GroupRoleSerializer
423425
queryset = GroupRole.objects.all()
424426
ordering = ("-pulp_created",)
427+
428+
429+
class LoginViewSet(generics.CreateAPIView):
430+
serializer_class = LoginSerializer
431+
432+
DEFAULT_ACCESS_POLICY = {
433+
"statements": [
434+
{
435+
"action": ["*"],
436+
"principal": "authenticated",
437+
"effect": "allow",
438+
},
439+
],
440+
"creation_hooks": [],
441+
}
442+
443+
@staticmethod
444+
def urlpattern():
445+
return "login"
446+
447+
@extend_schema(operation_id="login_read")
448+
def get(self, request):
449+
return Response(self.get_serializer(request.user).data)
450+
451+
@extend_schema(operation_id="logout")
452+
def delete(self, request):
453+
auth_logout(request)
454+
return Response(status=204)
455+
456+
457+
# Annotate without redefining the post method.
458+
extend_schema(operation_id="login")(LoginViewSet.post)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import http
2+
import pytest
3+
4+
pytestmark = [pytest.mark.parallel]
5+
6+
7+
def test_login_read_denies_anonymous(pulpcore_bindings, anonymous_user):
8+
with anonymous_user:
9+
with pytest.raises(pulpcore_bindings.module.ApiException) as exc:
10+
pulpcore_bindings.LoginApi.login_read()
11+
assert exc.value.status == 401
12+
13+
14+
def test_login_read_returns_admin(pulpcore_bindings):
15+
result = pulpcore_bindings.LoginApi.login_read()
16+
assert result.username == "admin"
17+
18+
19+
def test_login_read_returns_username(pulpcore_bindings, gen_user):
20+
user = gen_user()
21+
with user:
22+
result = pulpcore_bindings.LoginApi.login_read()
23+
assert result.username == user.username
24+
25+
26+
def test_login_denies_anonymous(pulpcore_bindings, anonymous_user):
27+
with anonymous_user:
28+
with pytest.raises(pulpcore_bindings.module.ApiException) as exc:
29+
pulpcore_bindings.LoginApi.login()
30+
assert exc.value.status == 401
31+
32+
33+
def test_login_returns_username(pulpcore_bindings, gen_user):
34+
user = gen_user()
35+
with user:
36+
pulpcore_bindings.LoginApi.login()
37+
response = pulpcore_bindings.LoginApi.login_with_http_info()
38+
if isinstance(response, tuple):
39+
# old bindings
40+
result, status, headers = response
41+
else:
42+
# new bindings
43+
result = response.data
44+
status = response.status
45+
headers = response.headers
46+
assert status == 201
47+
assert result.username == user.username
48+
cookie_jar = http.cookies.SimpleCookie(headers["set-cookie"])
49+
assert cookie_jar["sessionid"] != ""
50+
assert cookie_jar["csrftoken"] != ""
51+
52+
53+
def test_logout_removes_sessionid(pulpcore_bindings, gen_user):
54+
user = gen_user()
55+
with user:
56+
response = pulpcore_bindings.LoginApi.logout_with_http_info()
57+
if isinstance(response, tuple):
58+
# old bindings
59+
_, status, headers = response
60+
else:
61+
# new bindings
62+
status = response.status
63+
headers = response.headers
64+
assert status == 204
65+
if "set-cookie" in headers:
66+
# TODO It is indeed a bit unclear how to use a session cookie with the bindings.
67+
# Revisit after updating the bindings container may be worth a shot.
68+
cookie_jar = http.cookies.SimpleCookie(headers["set-cookie"])
69+
assert cookie_jar["sessionid"] == ""
70+
assert cookie_jar["csrftoken"] != ""
71+
72+
73+
def test_logout_denies_anonymous(pulpcore_bindings, anonymous_user):
74+
with anonymous_user:
75+
with pytest.raises(pulpcore_bindings.module.ApiException) as exc:
76+
pulpcore_bindings.LoginApi.logout()
77+
assert exc.value.status == 401

0 commit comments

Comments
 (0)