core: allow admins to create tokens with all parameters, re-add user to token form
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
663dffd8be
commit
c85484fc00
|
@ -2,15 +2,19 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.http.response import Http404
|
from django.http.response import Http404
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
|
from guardian.shortcuts import get_anonymous_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
|
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
|
from authentik.api.authorization import OwnerPermissions
|
||||||
from authentik.api.decorators import permission_required
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.users import UserSerializer
|
from authentik.core.api.users import UserSerializer
|
||||||
|
@ -79,13 +83,23 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
|
||||||
"expires",
|
"expires",
|
||||||
"expiring",
|
"expiring",
|
||||||
]
|
]
|
||||||
ordering = ["expires"]
|
ordering = ["identifier", "expires"]
|
||||||
|
permission_classes = [OwnerPermissions]
|
||||||
|
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user if self.request else get_anonymous_user()
|
||||||
|
if user.is_superuser:
|
||||||
|
return super().get_queryset()
|
||||||
|
return super().get_queryset().filter(user=user.pk)
|
||||||
|
|
||||||
def perform_create(self, serializer: TokenSerializer):
|
def perform_create(self, serializer: TokenSerializer):
|
||||||
serializer.save(
|
if not self.request.user.is_superuser:
|
||||||
|
return serializer.save(
|
||||||
user=self.request.user,
|
user=self.request.user,
|
||||||
expiring=self.request.user.attributes.get(USER_ATTRIBUTE_TOKEN_EXPIRING, True),
|
expiring=self.request.user.attributes.get(USER_ATTRIBUTE_TOKEN_EXPIRING, True),
|
||||||
)
|
)
|
||||||
|
return super().perform_create(serializer)
|
||||||
|
|
||||||
@permission_required("authentik_core.view_token_key")
|
@permission_required("authentik_core.view_token_key")
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Test token API"""
|
"""Test token API"""
|
||||||
|
from json import loads
|
||||||
|
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
|
@ -13,7 +15,8 @@ class TestTokenAPI(APITestCase):
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.user = User.objects.get(username="akadmin")
|
self.user = User.objects.create(username="testuser")
|
||||||
|
self.admin = User.objects.get(username="akadmin")
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
def test_token_create(self):
|
def test_token_create(self):
|
||||||
|
@ -55,3 +58,29 @@ class TestTokenAPI(APITestCase):
|
||||||
clean_expired_models.delay().get()
|
clean_expired_models.delay().get()
|
||||||
token.refresh_from_db()
|
token.refresh_from_db()
|
||||||
self.assertNotEqual(key, token.key)
|
self.assertNotEqual(key, token.key)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
"""Test Token List (Test normal authentication)"""
|
||||||
|
token_should: Token = Token.objects.create(
|
||||||
|
identifier="test", expiring=False, user=self.user
|
||||||
|
)
|
||||||
|
Token.objects.create(identifier="test-2", expiring=False, user=get_anonymous_user())
|
||||||
|
response = self.client.get(reverse(("authentik_api:token-list")))
|
||||||
|
body = loads(response.content)
|
||||||
|
self.assertEqual(len(body["results"]), 1)
|
||||||
|
self.assertEqual(body["results"][0]["identifier"], token_should.identifier)
|
||||||
|
|
||||||
|
def test_list_admin(self):
|
||||||
|
"""Test Token List (Test with admin auth)"""
|
||||||
|
self.client.force_login(self.admin)
|
||||||
|
token_should: Token = Token.objects.create(
|
||||||
|
identifier="test", expiring=False, user=self.user
|
||||||
|
)
|
||||||
|
token_should_not: Token = Token.objects.create(
|
||||||
|
identifier="test-2", expiring=False, user=get_anonymous_user()
|
||||||
|
)
|
||||||
|
response = self.client.get(reverse(("authentik_api:token-list")))
|
||||||
|
body = loads(response.content)
|
||||||
|
self.assertEqual(len(body["results"]), 2)
|
||||||
|
self.assertEqual(body["results"][0]["identifier"], token_should.identifier)
|
||||||
|
self.assertEqual(body["results"][1]["identifier"], token_should_not.identifier)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/forms/HorizontalFormElement";
|
||||||
import "../../elements/forms/FormGroup";
|
import "../../elements/forms/FormGroup";
|
||||||
import { dateTimeLocal, first } from "../../utils";
|
import { dateTimeLocal, first } from "../../utils";
|
||||||
import { ModelForm } from "../../elements/forms/ModelForm";
|
import { ModelForm } from "../../elements/forms/ModelForm";
|
||||||
|
import { until } from "lit-html/directives/until";
|
||||||
|
|
||||||
@customElement("ak-token-form")
|
@customElement("ak-token-form")
|
||||||
export class TokenForm extends ModelForm<Token, string> {
|
export class TokenForm extends ModelForm<Token, string> {
|
||||||
|
@ -50,6 +51,27 @@ export class TokenForm extends ModelForm<Token, string> {
|
||||||
${t`Unique identifier the token is referenced by.`}
|
${t`Unique identifier the token is referenced by.`}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
${until(
|
||||||
|
new CoreApi(DEFAULT_CONFIG)
|
||||||
|
.coreUsersList({
|
||||||
|
ordering: "username",
|
||||||
|
})
|
||||||
|
.then((users) => {
|
||||||
|
return users.results.map((user) => {
|
||||||
|
return html`<option
|
||||||
|
value=${user.pk}
|
||||||
|
?selected=${this.instance?.user === user.pk}
|
||||||
|
>
|
||||||
|
${user.username}
|
||||||
|
</option>`;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
html`<option>${t`Loading...`}</option>`,
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal label=${t`Intent`} ?required=${true} name="intent">
|
<ak-form-element-horizontal label=${t`Intent`} ?required=${true} name="intent">
|
||||||
<select class="pf-c-form-control">
|
<select class="pf-c-form-control">
|
||||||
<option
|
<option
|
||||||
|
|
Reference in New Issue