mirror of
https://github.com/goauthentik/authentik.git
synced 2026-08-30 18:51:39 -07:00
126 lines
4.4 KiB
Python
126 lines
4.4 KiB
Python
"""Radius Provider"""
|
|
|
|
from collections.abc import Iterable
|
|
|
|
from django.db import models, transaction
|
|
from django.templatetags.static import static
|
|
from django.utils.translation import gettext_lazy as _
|
|
from rest_framework.serializers import Serializer
|
|
|
|
from authentik.core.models import PropertyMapping, Provider
|
|
from authentik.crypto.models import CertificateKeyPair
|
|
from authentik.lib.generators import generate_id
|
|
from authentik.outposts.models import OutpostModel
|
|
from authentik.secrets.models import create_named_secret
|
|
|
|
|
|
class RadiusProvider(OutpostModel, Provider):
|
|
"""Allow applications to authenticate against authentik's users using Radius."""
|
|
|
|
secret = models.ForeignKey(
|
|
"authentik_secrets.Secret",
|
|
verbose_name=_("Shared Secret"),
|
|
help_text=_("Shared secret between clients and server to hash packets."),
|
|
on_delete=models.PROTECT,
|
|
null=True,
|
|
blank=True,
|
|
default=None,
|
|
related_name="radius_providers",
|
|
)
|
|
_shared_secret = models.TextField(
|
|
default=generate_id,
|
|
help_text=_("Shared secret between clients and server to hash packets."),
|
|
db_column="shared_secret",
|
|
)
|
|
|
|
client_networks = models.TextField(
|
|
default="0.0.0.0/0, ::/0",
|
|
help_text=_(
|
|
"List of CIDRs (comma-separated) that clients can connect from. A more specific "
|
|
"CIDR will match before a looser one. Clients connecting from a non-specified CIDR "
|
|
"will be dropped."
|
|
),
|
|
)
|
|
|
|
mfa_support = models.BooleanField(
|
|
default=True,
|
|
verbose_name="MFA Support",
|
|
help_text=_(
|
|
"When enabled, code-based multi-factor authentication can be used by appending a "
|
|
"semicolon and the TOTP code to the password. This should only be enabled if all "
|
|
"users that will bind to this provider have a TOTP device configured, as otherwise "
|
|
"a password may incorrectly be rejected if it contains a semicolon."
|
|
),
|
|
)
|
|
|
|
certificate = models.ForeignKey(
|
|
CertificateKeyPair, on_delete=models.CASCADE, default=None, null=True
|
|
)
|
|
|
|
@property
|
|
def launch_url(self) -> str | None:
|
|
"""Radius never has a launch URL"""
|
|
return None
|
|
|
|
def save(self, *args, **kwargs):
|
|
with transaction.atomic():
|
|
if not self.secret_id:
|
|
self.secret = create_named_secret(f"{self.name} shared secret")
|
|
if (update_fields := kwargs.get("update_fields")) is not None:
|
|
kwargs["update_fields"] = set(update_fields) | {"secret"}
|
|
return super().save(*args, **kwargs)
|
|
|
|
@property
|
|
def component(self) -> str:
|
|
return "ak-provider-radius-form"
|
|
|
|
@property
|
|
def icon_url(self) -> str | None:
|
|
return static("authentik/sources/radius.svg")
|
|
|
|
@property
|
|
def serializer(self) -> type[Serializer]:
|
|
from authentik.providers.radius.api.providers import RadiusProviderSerializer
|
|
|
|
return RadiusProviderSerializer
|
|
|
|
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
|
|
required = [self, "authentik_stages_mtls.pass_outpost_certificate"]
|
|
if self.certificate is not None:
|
|
required.append(("authentik_crypto.view_certificatekeypair", self.certificate))
|
|
required.append(
|
|
("authentik_crypto.view_certificatekeypair_certificate", self.certificate)
|
|
)
|
|
required.append(("authentik_crypto.view_certificatekeypair_key", self.certificate))
|
|
return required
|
|
|
|
def __str__(self):
|
|
return f"Radius Provider {self.name}"
|
|
|
|
class Meta:
|
|
verbose_name = _("Radius Provider")
|
|
verbose_name_plural = _("Radius Providers")
|
|
|
|
|
|
class RadiusProviderPropertyMapping(PropertyMapping):
|
|
"""Add additional attributes to Radius authentication responses."""
|
|
|
|
@property
|
|
def component(self) -> str:
|
|
return "ak-property-mapping-provider-radius-form"
|
|
|
|
@property
|
|
def serializer(self) -> type[Serializer]:
|
|
from authentik.providers.radius.api.property_mappings import (
|
|
RadiusProviderPropertyMappingSerializer,
|
|
)
|
|
|
|
return RadiusProviderPropertyMappingSerializer
|
|
|
|
def __str__(self):
|
|
return f"Radius Provider Property Mapping {self.name}"
|
|
|
|
class Meta:
|
|
verbose_name = _("Radius Provider Property Mapping")
|
|
verbose_name_plural = _("Radius Provider Property Mappings")
|