Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer 2023-07-24 23:34:00 +02:00
parent 86b69e83e0
commit ccb2d0aaed
No known key found for this signature in database
4 changed files with 59 additions and 19 deletions

View File

@ -1,12 +1,10 @@
"""AuthenticatorDuoStage API Views""" """AuthenticatorMobileStage API Views"""
from django.http import Http404
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.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer from drf_spectacular.utils import extend_schema, inline_serializer
from guardian.shortcuts import get_objects_for_user
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, ChoiceField, IntegerField 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
@ -15,7 +13,6 @@ from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions from authentik.api.authorization import OwnerFilter, OwnerPermissions
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.flows.api.stages import StageSerializer from authentik.flows.api.stages import StageSerializer
from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage, MobileDevice from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage, MobileDevice

View File

@ -1,9 +1,12 @@
# Generated by Django 4.1.10 on 2023-07-24 18:48 # Generated by Django 4.1.10 on 2023-07-24 21:33
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import authentik.core.models
import authentik.stages.authenticator_mobile.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
@ -85,4 +88,43 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Mobile Devices", "verbose_name_plural": "Mobile Devices",
}, },
), ),
migrations.CreateModel(
name="MobileDeviceToken",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
(
"expires",
models.DateTimeField(default=authentik.core.models.default_token_duration),
),
("expiring", models.BooleanField(default=True)),
(
"token",
models.TextField(
default=authentik.stages.authenticator_mobile.models.default_token_key
),
),
(
"device",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="authentik_stages_authenticator_mobile.mobiledevice",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"abstract": False,
},
),
] ]

View File

@ -7,8 +7,8 @@ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django_otp.models import Device from django_otp.models import Device
from rest_framework.serializers import BaseSerializer, Serializer from rest_framework.serializers import BaseSerializer, Serializer
from authentik.core.models import ExpiringModel
from authentik.core.models import ExpiringModel
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
@ -21,7 +21,7 @@ def default_token_key():
class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage): class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Setup Duo authenticator devices""" """Setup Mobile authenticator devices"""
@property @property
def serializer(self) -> type[BaseSerializer]: def serializer(self) -> type[BaseSerializer]:
@ -78,8 +78,8 @@ class MobileDevice(SerializerModel, Device):
verbose_name = _("Mobile Device") verbose_name = _("Mobile Device")
verbose_name_plural = _("Mobile Devices") verbose_name_plural = _("Mobile Devices")
class MobileDeviceToken(ExpiringModel):
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)

View File

@ -1,10 +1,8 @@
"""Mobile stage""" """Mobile stage"""
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.timezone import now
from rest_framework.fields import CharField from rest_framework.fields import CharField
from authentik.core.api.utils import PassiveSerializer
from authentik.events.models import Event, EventAction from authentik.core.api.utils import PassiveSerializer
from authentik.flows.challenge import ( from authentik.flows.challenge import (
Challenge, Challenge,
ChallengeResponse, ChallengeResponse,
@ -24,6 +22,7 @@ class AuthenticatorMobilePayloadChallenge(PassiveSerializer):
s = CharField(required=False, help_text="Stage UUID") s = CharField(required=False, help_text="Stage UUID")
t = CharField(required=False, help_text="Initial Token") t = CharField(required=False, help_text="Initial Token")
class AuthenticatorMobileChallenge(WithUserInfoChallenge): class AuthenticatorMobileChallenge(WithUserInfoChallenge):
"""Mobile Challenge""" """Mobile Challenge"""
@ -53,12 +52,14 @@ class AuthenticatorMobileStageView(ChallengeStageView):
def get_challenge(self, *args, **kwargs) -> Challenge: def get_challenge(self, *args, **kwargs) -> Challenge:
stage: AuthenticatorMobileStage = self.executor.current_stage stage: AuthenticatorMobileStage = self.executor.current_stage
self.prepare() self.prepare()
payload = AuthenticatorMobilePayloadChallenge(data={ payload = AuthenticatorMobilePayloadChallenge(
data={
# TODO: use cloud gateway? # TODO: use cloud gateway?
"u": self.request.get_host(), "u": self.request.get_host(),
"s": str(stage.stage_uuid), "s": str(stage.stage_uuid),
"t": self.executor.plan[FLOW_PLAN_MOBILE_ENROLL].token, "t": self.executor.plan[FLOW_PLAN_MOBILE_ENROLL].token,
}) }
)
payload.is_valid() payload.is_valid()
return AuthenticatorMobileChallenge( return AuthenticatorMobileChallenge(
data={ data={