implement more of the API
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
28e1c08800
commit
4114c757b9
|
@ -1,9 +1,10 @@
|
||||||
"""AuthenticatorMobileStage API Views"""
|
"""AuthenticatorMobileStage API Views"""
|
||||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||||
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import CharField, UUIDField
|
from rest_framework.fields import CharField
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
from rest_framework.permissions import IsAdminUser
|
from rest_framework.permissions import IsAdminUser
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
|
@ -13,8 +14,9 @@ from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||||
|
|
||||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.stages.authenticator_mobile.api.auth import MobileDeviceTokenAuthentication
|
from authentik.stages.authenticator_mobile.api.auth import MobileDeviceTokenAuthentication
|
||||||
from authentik.stages.authenticator_mobile.models import MobileDevice
|
from authentik.stages.authenticator_mobile.models import MobileDevice, MobileDeviceToken
|
||||||
|
|
||||||
|
|
||||||
class MobileDeviceSerializer(ModelSerializer):
|
class MobileDeviceSerializer(ModelSerializer):
|
||||||
|
@ -26,6 +28,14 @@ class MobileDeviceSerializer(ModelSerializer):
|
||||||
depth = 2
|
depth = 2
|
||||||
|
|
||||||
|
|
||||||
|
class MobileDeviceEnrollmentSerializer(PassiveSerializer):
|
||||||
|
device_uid = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MobileDeviceSetPushKeySerializer(PassiveSerializer):
|
||||||
|
firebase_key = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class MobileDeviceViewSet(
|
class MobileDeviceViewSet(
|
||||||
mixins.RetrieveModelMixin,
|
mixins.RetrieveModelMixin,
|
||||||
mixins.UpdateModelMixin,
|
mixins.UpdateModelMixin,
|
||||||
|
@ -44,47 +54,10 @@ class MobileDeviceViewSet(
|
||||||
permission_classes = [OwnerPermissions]
|
permission_classes = [OwnerPermissions]
|
||||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||||
|
|
||||||
@extend_schema(
|
|
||||||
responses={
|
|
||||||
204: "",
|
|
||||||
},
|
|
||||||
request=inline_serializer(
|
|
||||||
"MobileDeviceSetPushKeySerializer",
|
|
||||||
{
|
|
||||||
"firebase_key": CharField(required=True),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@action(
|
|
||||||
methods=["POST"],
|
|
||||||
detail=True,
|
|
||||||
permission_classes=[],
|
|
||||||
authentication_classes=[MobileDeviceTokenAuthentication],
|
|
||||||
)
|
|
||||||
def set_notification_key(self):
|
|
||||||
"""Called by the phone whenever the firebase key changes and we need to update it"""
|
|
||||||
device = self.get_object()
|
|
||||||
print(self.request.user)
|
|
||||||
|
|
||||||
@action(
|
|
||||||
methods=["POST"],
|
|
||||||
detail=True,
|
|
||||||
permission_classes=[],
|
|
||||||
authentication_classes=[MobileDeviceTokenAuthentication],
|
|
||||||
)
|
|
||||||
def receive_response():
|
|
||||||
"""Get response from notification on phone"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: inline_serializer(
|
||||||
"MobileDeviceEnrollmentCallbackSerializer",
|
"MobileDeviceEnrollmentCallbackSerializer",
|
||||||
{"device_token": CharField(required=True), "device_uuid": UUIDField(required=True)},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
request=inline_serializer(
|
|
||||||
"MobileDeviceEnrollmentSerializer",
|
|
||||||
{
|
{
|
||||||
# New API token (that will be rotated at some point)
|
# New API token (that will be rotated at some point)
|
||||||
# also used by the backend to sign requests to the cloud broker
|
# also used by the backend to sign requests to the cloud broker
|
||||||
|
@ -92,6 +65,8 @@ class MobileDeviceViewSet(
|
||||||
"token": CharField(required=True),
|
"token": CharField(required=True),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
},
|
||||||
|
request=MobileDeviceEnrollmentSerializer,
|
||||||
)
|
)
|
||||||
@action(
|
@action(
|
||||||
methods=["POST"],
|
methods=["POST"],
|
||||||
|
@ -101,6 +76,53 @@ class MobileDeviceViewSet(
|
||||||
)
|
)
|
||||||
def enrollment_callback(self, request: Request, pk: str) -> Response:
|
def enrollment_callback(self, request: Request, pk: str) -> Response:
|
||||||
"""Enrollment callback"""
|
"""Enrollment callback"""
|
||||||
|
device: MobileDevice = self.get_object()
|
||||||
|
data = MobileDeviceEnrollmentSerializer(data=request.data)
|
||||||
|
data.is_valid(raise_exception=True)
|
||||||
|
device.device_id = data.validated_data["device_uid"]
|
||||||
|
device.save()
|
||||||
|
MobileDeviceToken.objects.filter(
|
||||||
|
device=device,
|
||||||
|
).delete()
|
||||||
|
new_token = MobileDeviceToken.objects.create(
|
||||||
|
device=device,
|
||||||
|
user=device.user,
|
||||||
|
)
|
||||||
|
return Response(
|
||||||
|
data={
|
||||||
|
"token": new_token,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
204: OpenApiTypes.STR,
|
||||||
|
},
|
||||||
|
request=MobileDeviceSetPushKeySerializer,
|
||||||
|
)
|
||||||
|
@action(
|
||||||
|
methods=["POST"],
|
||||||
|
detail=True,
|
||||||
|
permission_classes=[],
|
||||||
|
authentication_classes=[MobileDeviceTokenAuthentication],
|
||||||
|
)
|
||||||
|
def set_notification_key(self, request: Request) -> Response:
|
||||||
|
"""Called by the phone whenever the firebase key changes and we need to update it"""
|
||||||
|
device: MobileDevice = self.get_object()
|
||||||
|
data = MobileDeviceSetPushKeySerializer(data=request)
|
||||||
|
data.is_valid(raise_exception=True)
|
||||||
|
device.firebase_token = data.validated_data["firebase_key"]
|
||||||
|
device.save()
|
||||||
|
return Response(status=204)
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=["POST"],
|
||||||
|
detail=True,
|
||||||
|
permission_classes=[],
|
||||||
|
authentication_classes=[MobileDeviceTokenAuthentication],
|
||||||
|
)
|
||||||
|
def receive_response(self, request: Request) -> Response:
|
||||||
|
"""Get response from notification on phone"""
|
||||||
print(request.data)
|
print(request.data)
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.4 on 2023-09-04 11:59
|
# Generated by Django 4.2.4 on 2023-09-04 13:21
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
("device_id", models.TextField(unique=True)),
|
("device_id", models.TextField(unique=True)),
|
||||||
|
("firebase_token", models.TextField(blank=True)),
|
||||||
(
|
(
|
||||||
"stage",
|
"stage",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
@ -105,7 +106,6 @@ class Migration(migrations.Migration):
|
||||||
default=authentik.stages.authenticator_mobile.models.default_token_key
|
default=authentik.stages.authenticator_mobile.models.default_token_key
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("firebase_token", models.TextField(blank=True)),
|
|
||||||
(
|
(
|
||||||
"device",
|
"device",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
|
|
@ -69,6 +69,7 @@ class MobileDevice(SerializerModel, Device):
|
||||||
stage = models.ForeignKey(AuthenticatorMobileStage, on_delete=models.CASCADE)
|
stage = models.ForeignKey(AuthenticatorMobileStage, on_delete=models.CASCADE)
|
||||||
|
|
||||||
device_id = models.TextField(unique=True)
|
device_id = models.TextField(unique=True)
|
||||||
|
firebase_token = models.TextField(blank=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> Serializer:
|
def serializer(self) -> Serializer:
|
||||||
|
@ -90,5 +91,3 @@ class MobileDeviceToken(ExpiringModel):
|
||||||
device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE, null=True)
|
device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE, null=True)
|
||||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
||||||
token = models.TextField(default=default_token_key)
|
token = models.TextField(default=default_token_key)
|
||||||
|
|
||||||
firebase_token = models.TextField(blank=True)
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ from authentik.flows.challenge import (
|
||||||
WithUserInfoChallenge,
|
WithUserInfoChallenge,
|
||||||
)
|
)
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.stages.authenticator_mobile.models import (
|
from authentik.stages.authenticator_mobile.models import MobileDevice, MobileDeviceToken
|
||||||
MobileDevice,
|
|
||||||
MobileDeviceToken,
|
|
||||||
)
|
|
||||||
|
|
||||||
FLOW_PLAN_MOBILE_ENROLL = "authentik/stages/authenticator_mobile/enroll"
|
FLOW_PLAN_MOBILE_ENROLL = "authentik/stages/authenticator_mobile/enroll"
|
||||||
|
|
||||||
|
@ -51,6 +48,7 @@ class AuthenticatorMobileStageView(ChallengeStageView):
|
||||||
device = MobileDevice.objects.create(
|
device = MobileDevice.objects.create(
|
||||||
user=self.get_pending_user(),
|
user=self.get_pending_user(),
|
||||||
stage=self.executor.current_stage,
|
stage=self.executor.current_stage,
|
||||||
|
confirmed=False,
|
||||||
)
|
)
|
||||||
token = MobileDeviceToken.objects.create(
|
token = MobileDeviceToken.objects.create(
|
||||||
user=self.get_pending_user(),
|
user=self.get_pending_user(),
|
||||||
|
|
12
schema.yml
12
schema.yml
|
@ -35218,22 +35218,18 @@ components:
|
||||||
MobileDeviceEnrollmentCallback:
|
MobileDeviceEnrollmentCallback:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
device_token:
|
token:
|
||||||
type: string
|
type: string
|
||||||
device_uuid:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
required:
|
required:
|
||||||
- device_token
|
- token
|
||||||
- device_uuid
|
|
||||||
MobileDeviceEnrollmentRequest:
|
MobileDeviceEnrollmentRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
token:
|
device_uid:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
required:
|
required:
|
||||||
- token
|
- device_uid
|
||||||
MobileDeviceRequest:
|
MobileDeviceRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for Mobile authenticator devices
|
description: Serializer for Mobile authenticator devices
|
||||||
|
|
Reference in New Issue