authentik.stages.authenticator_duo.models

Duo stage

  1"""Duo stage"""
  2
  3from django.contrib.auth import get_user_model
  4from django.db import models
  5from django.utils.translation import gettext_lazy as _
  6from django.views import View
  7from duo_client.admin import Admin
  8from duo_client.auth import Auth
  9from rest_framework.serializers import BaseSerializer, Serializer
 10
 11from authentik.core.types import UserSettingSerializer
 12from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
 13from authentik.lib.models import SerializerModel
 14from authentik.lib.utils.http import authentik_user_agent
 15from authentik.stages.authenticator.models import Device
 16
 17
 18class AuthenticatorDuoStage(ConfigurableStage, FriendlyNamedStage, Stage):
 19    """Setup Duo authentication for the user."""
 20
 21    api_hostname = models.TextField()
 22
 23    client_id = models.TextField()
 24    client_secret = models.TextField()
 25
 26    admin_integration_key = models.TextField(blank=True, default="")
 27    admin_secret_key = models.TextField(blank=True, default="")
 28
 29    @property
 30    def serializer(self) -> type[BaseSerializer]:
 31        from authentik.stages.authenticator_duo.api import AuthenticatorDuoStageSerializer
 32
 33        return AuthenticatorDuoStageSerializer
 34
 35    @property
 36    def view(self) -> type[View]:
 37        from authentik.stages.authenticator_duo.stage import AuthenticatorDuoStageView
 38
 39        return AuthenticatorDuoStageView
 40
 41    def auth_client(self) -> Auth:
 42        """Get an API Client to talk to duo"""
 43        return Auth(
 44            self.client_id,
 45            self.client_secret,
 46            self.api_hostname,
 47            user_agent=authentik_user_agent(),
 48        )
 49
 50    def admin_client(self) -> Admin:
 51        """Get an API Client to talk to duo"""
 52        if self.admin_integration_key == "" or self.admin_secret_key == "":  # nosec
 53            raise ValueError("Admin credentials not configured")
 54        client = Admin(
 55            self.admin_integration_key,
 56            self.admin_secret_key,
 57            self.api_hostname,
 58            user_agent=authentik_user_agent(),
 59        )
 60        return client
 61
 62    @property
 63    def component(self) -> str:
 64        return "ak-stage-authenticator-duo-form"
 65
 66    def ui_user_settings(self) -> UserSettingSerializer | None:
 67        return UserSettingSerializer(
 68            data={
 69                "title": self.friendly_name or str(self._meta.verbose_name),
 70                "component": "ak-user-settings-authenticator-duo",
 71            }
 72        )
 73
 74    def __str__(self) -> str:
 75        return f"Duo Authenticator Setup Stage {self.name}"
 76
 77    class Meta:
 78        verbose_name = _("Duo Authenticator Setup Stage")
 79        verbose_name_plural = _("Duo Authenticator Setup Stages")
 80
 81
 82class DuoDevice(SerializerModel, Device):
 83    """Duo Device for a single user"""
 84
 85    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
 86
 87    # Connect to the stage to when validating access we know the API Credentials
 88    stage = models.ForeignKey(AuthenticatorDuoStage, on_delete=models.CASCADE)
 89    duo_user_id = models.TextField()
 90    last_t = models.DateTimeField(auto_now=True)
 91
 92    @property
 93    def serializer(self) -> Serializer:
 94        from authentik.stages.authenticator_duo.api import DuoDeviceSerializer
 95
 96        return DuoDeviceSerializer
 97
 98    def __str__(self):
 99        return str(self.name) or str(self.user_id)
100
101    class Meta:
102        verbose_name = _("Duo Device")
103        verbose_name_plural = _("Duo Devices")
19class AuthenticatorDuoStage(ConfigurableStage, FriendlyNamedStage, Stage):
20    """Setup Duo authentication for the user."""
21
22    api_hostname = models.TextField()
23
24    client_id = models.TextField()
25    client_secret = models.TextField()
26
27    admin_integration_key = models.TextField(blank=True, default="")
28    admin_secret_key = models.TextField(blank=True, default="")
29
30    @property
31    def serializer(self) -> type[BaseSerializer]:
32        from authentik.stages.authenticator_duo.api import AuthenticatorDuoStageSerializer
33
34        return AuthenticatorDuoStageSerializer
35
36    @property
37    def view(self) -> type[View]:
38        from authentik.stages.authenticator_duo.stage import AuthenticatorDuoStageView
39
40        return AuthenticatorDuoStageView
41
42    def auth_client(self) -> Auth:
43        """Get an API Client to talk to duo"""
44        return Auth(
45            self.client_id,
46            self.client_secret,
47            self.api_hostname,
48            user_agent=authentik_user_agent(),
49        )
50
51    def admin_client(self) -> Admin:
52        """Get an API Client to talk to duo"""
53        if self.admin_integration_key == "" or self.admin_secret_key == "":  # nosec
54            raise ValueError("Admin credentials not configured")
55        client = Admin(
56            self.admin_integration_key,
57            self.admin_secret_key,
58            self.api_hostname,
59            user_agent=authentik_user_agent(),
60        )
61        return client
62
63    @property
64    def component(self) -> str:
65        return "ak-stage-authenticator-duo-form"
66
67    def ui_user_settings(self) -> UserSettingSerializer | None:
68        return UserSettingSerializer(
69            data={
70                "title": self.friendly_name or str(self._meta.verbose_name),
71                "component": "ak-user-settings-authenticator-duo",
72            }
73        )
74
75    def __str__(self) -> str:
76        return f"Duo Authenticator Setup Stage {self.name}"
77
78    class Meta:
79        verbose_name = _("Duo Authenticator Setup Stage")
80        verbose_name_plural = _("Duo Authenticator Setup Stages")

Setup Duo authentication for the user.

def api_hostname(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def client_id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def client_secret(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def admin_integration_key(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def admin_secret_key(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

serializer: type[rest_framework.serializers.BaseSerializer]
30    @property
31    def serializer(self) -> type[BaseSerializer]:
32        from authentik.stages.authenticator_duo.api import AuthenticatorDuoStageSerializer
33
34        return AuthenticatorDuoStageSerializer

Get serializer for this model

view: type[django.views.generic.base.View]
36    @property
37    def view(self) -> type[View]:
38        from authentik.stages.authenticator_duo.stage import AuthenticatorDuoStageView
39
40        return AuthenticatorDuoStageView

Return StageView class that implements logic for this stage

def auth_client(self) -> duo_client.auth.Auth:
42    def auth_client(self) -> Auth:
43        """Get an API Client to talk to duo"""
44        return Auth(
45            self.client_id,
46            self.client_secret,
47            self.api_hostname,
48            user_agent=authentik_user_agent(),
49        )

Get an API Client to talk to duo

def admin_client(self) -> duo_client.admin.Admin:
51    def admin_client(self) -> Admin:
52        """Get an API Client to talk to duo"""
53        if self.admin_integration_key == "" or self.admin_secret_key == "":  # nosec
54            raise ValueError("Admin credentials not configured")
55        client = Admin(
56            self.admin_integration_key,
57            self.admin_secret_key,
58            self.api_hostname,
59            user_agent=authentik_user_agent(),
60        )
61        return client

Get an API Client to talk to duo

component: str
63    @property
64    def component(self) -> str:
65        return "ak-stage-authenticator-duo-form"

Return component used to edit this object

def ui_user_settings(self) -> authentik.core.types.UserSettingSerializer | None:
67    def ui_user_settings(self) -> UserSettingSerializer | None:
68        return UserSettingSerializer(
69            data={
70                "title": self.friendly_name or str(self._meta.verbose_name),
71                "component": "ak-user-settings-authenticator-duo",
72            }
73        )

Entrypoint to integrate with User settings. Can either return None if no user settings are available, or a challenge.

configure_flow

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def friendly_name(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

configure_flow_id
stage_ptr_id
stage_ptr

Accessor to the related object on the forward side of a one-to-one relation.

In the example::

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

duodevice_set

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

class AuthenticatorDuoStage.DoesNotExist(authentik.flows.models.Stage.DoesNotExist):

The requested object does not exist

class AuthenticatorDuoStage.MultipleObjectsReturned(authentik.flows.models.Stage.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

 83class DuoDevice(SerializerModel, Device):
 84    """Duo Device for a single user"""
 85
 86    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
 87
 88    # Connect to the stage to when validating access we know the API Credentials
 89    stage = models.ForeignKey(AuthenticatorDuoStage, on_delete=models.CASCADE)
 90    duo_user_id = models.TextField()
 91    last_t = models.DateTimeField(auto_now=True)
 92
 93    @property
 94    def serializer(self) -> Serializer:
 95        from authentik.stages.authenticator_duo.api import DuoDeviceSerializer
 96
 97        return DuoDeviceSerializer
 98
 99    def __str__(self):
100        return str(self.name) or str(self.user_id)
101
102    class Meta:
103        verbose_name = _("Duo Device")
104        verbose_name_plural = _("Duo Devices")

Duo Device for a single user

user

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

stage

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def duo_user_id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def last_t(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

serializer: rest_framework.serializers.Serializer
93    @property
94    def serializer(self) -> Serializer:
95        from authentik.stages.authenticator_duo.api import DuoDeviceSerializer
96
97        return DuoDeviceSerializer

Get serializer for this model

def name(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def confirmed(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def last_used(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def created(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def last_updated(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

user_id
stage_id
def get_next_by_last_t(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_previous_by_last_t(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_next_by_created(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_previous_by_created(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_next_by_last_updated(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_previous_by_last_updated(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

class DuoDevice.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class DuoDevice.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.