mirror of
https://github.com/goauthentik/authentik
synced 2026-04-28 10:28:22 +02:00
Compare commits
4 Commits
admin/vers
...
mokeytype_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7325bfde36 | ||
|
|
6383d0bfc0 | ||
|
|
4265e7b0af | ||
|
|
41df11d5dc |
@@ -77,7 +77,7 @@ class GroupSerializer(ModelSerializer):
|
||||
return None
|
||||
return GroupMemberSerializer(instance.users, many=True).data
|
||||
|
||||
def validate_parent(self, parent: Group | None):
|
||||
def validate_parent(self, parent: Group | None) -> None:
|
||||
"""Validate group parent (if set), ensuring the parent isn't itself"""
|
||||
if not self.instance or not parent:
|
||||
return parent
|
||||
@@ -85,7 +85,7 @@ class GroupSerializer(ModelSerializer):
|
||||
raise ValidationError(_("Cannot set group as parent of itself."))
|
||||
return parent
|
||||
|
||||
def validate_is_superuser(self, superuser: bool):
|
||||
def validate_is_superuser(self, superuser: bool) -> bool:
|
||||
"""Ensure that the user creating this group has permissions to set the superuser flag"""
|
||||
request: Request = self.context.get("request", None)
|
||||
if not request:
|
||||
@@ -210,7 +210,7 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||
OpenApiParameter("include_users", bool, default=True),
|
||||
]
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
def list(self, request: Request, *args, **kwargs) -> Response:
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
@extend_schema(
|
||||
@@ -218,7 +218,7 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||
OpenApiParameter("include_users", bool, default=True),
|
||||
]
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
def retrieve(self, request: Request, *args, **kwargs) -> Response:
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
@permission_required("authentik_core.add_user_to_group")
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.db.models.query import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_filters.filters import BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from model_utils.managers import InheritanceQuerySet
|
||||
from rest_framework import mixins
|
||||
from rest_framework.fields import ReadOnlyField, SerializerMethodField
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
@@ -99,5 +100,5 @@ class ProviderViewSet(
|
||||
"application__name",
|
||||
]
|
||||
|
||||
def get_queryset(self): # pragma: no cover
|
||||
def get_queryset(self) -> InheritanceQuerySet: # pragma: no cover
|
||||
return Provider.objects.select_subclasses()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from collections.abc import Iterable
|
||||
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||
from model_utils.managers import InheritanceQuerySet
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
@@ -88,7 +89,7 @@ class SourceViewSet(
|
||||
search_fields = ["slug", "name"]
|
||||
filterset_fields = ["slug", "name", "managed", "pbm_uuid"]
|
||||
|
||||
def get_queryset(self): # pragma: no cover
|
||||
def get_queryset(self) -> InheritanceQuerySet: # pragma: no cover
|
||||
return Source.objects.select_subclasses()
|
||||
|
||||
@permission_required("authentik_core.change_source")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils.timezone import now
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
|
||||
from guardian.shortcuts import assign_perm, get_anonymous_user
|
||||
@@ -41,7 +42,7 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
|
||||
self.fields["key"] = CharField(required=False)
|
||||
|
||||
def validate_user(self, user: User):
|
||||
def validate_user(self, user: User) -> User:
|
||||
"""Ensure user of token cannot be changed"""
|
||||
if self.instance and self.instance.user_id:
|
||||
if user.pk != self.instance.user_id:
|
||||
@@ -138,13 +139,13 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
|
||||
owner_field = "user"
|
||||
rbac_allow_create_without_perm = True
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self) -> QuerySet:
|
||||
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) -> Token:
|
||||
if not self.request.user.is_superuser:
|
||||
instance = serializer.save(
|
||||
user=self.request.user,
|
||||
|
||||
@@ -21,6 +21,7 @@ from django_filters.filters import (
|
||||
UUIDFilter,
|
||||
)
|
||||
from django_filters.filterset import FilterSet
|
||||
from djangoql.schema import BoolField, StrField
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import (
|
||||
OpenApiParameter,
|
||||
@@ -72,8 +73,10 @@ from authentik.core.models import (
|
||||
Token,
|
||||
TokenIntents,
|
||||
User,
|
||||
UserQuerySet,
|
||||
UserTypes,
|
||||
)
|
||||
from authentik.enterprise.search.fields import ChoiceSearchField, JSONSearchField
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.models import FlowToken
|
||||
@@ -349,7 +352,7 @@ class UsersFilter(FilterSet):
|
||||
queryset=Group.objects.all().order_by("name"),
|
||||
)
|
||||
|
||||
def filter_is_superuser(self, queryset, name, value):
|
||||
def filter_is_superuser(self, queryset: UserQuerySet, name: str, value: bool) -> UserQuerySet:
|
||||
if value:
|
||||
return queryset.filter(ak_groups__is_superuser=True).distinct()
|
||||
return queryset.exclude(ak_groups__is_superuser=True).distinct()
|
||||
@@ -395,7 +398,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
filterset_class = UsersFilter
|
||||
search_fields = ["username", "name", "is_active", "email", "uuid", "attributes"]
|
||||
|
||||
def get_ql_fields(self):
|
||||
def get_ql_fields(self) -> list[StrField | BoolField | ChoiceSearchField | JSONSearchField]:
|
||||
from djangoql.schema import BoolField, StrField
|
||||
|
||||
from authentik.enterprise.search.fields import ChoiceSearchField, JSONSearchField
|
||||
@@ -410,7 +413,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
JSONSearchField(User, "attributes", suggest_nested=False),
|
||||
]
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self) -> UserQuerySet:
|
||||
base_qs = User.objects.all().exclude_anonymous()
|
||||
if self.serializer_class(context={"request": self.request})._should_include_groups:
|
||||
base_qs = base_qs.prefetch_related("ak_groups")
|
||||
@@ -421,10 +424,10 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
OpenApiParameter("include_groups", bool, default=True),
|
||||
]
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
def list(self, request: Request, *args, **kwargs) -> Response:
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
def _create_recovery_link(self, for_email=False) -> tuple[str, Token]:
|
||||
def _create_recovery_link(self, for_email: bool = False) -> tuple[str, Token]:
|
||||
"""Create a recovery link (when the current brand has a recovery flow set),
|
||||
that can either be shown to an admin or sent to the user directly"""
|
||||
brand: Brand = self.request._request.brand
|
||||
|
||||
@@ -42,7 +42,7 @@ class JSONExtension(OpenApiSerializerFieldExtension):
|
||||
|
||||
target_class = "authentik.core.api.utils.JSONDictField"
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
def map_serializer_field(self, auto_schema, direction: str) -> dict[str, str]:
|
||||
return build_basic_type(OpenApiTypes.OBJECT)
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class ModelSerializer(BaseModelSerializer):
|
||||
serializer_field_mapping = BaseModelSerializer.serializer_field_mapping.copy()
|
||||
serializer_field_mapping[models.JSONField] = JSONDictField
|
||||
|
||||
def create(self, validated_data):
|
||||
def create(self, validated_data: dict[str, Any]):
|
||||
instance = super().create(validated_data)
|
||||
|
||||
request = self.context.get("request")
|
||||
@@ -61,7 +61,7 @@ class ModelSerializer(BaseModelSerializer):
|
||||
|
||||
return instance
|
||||
|
||||
def update(self, instance: Model, validated_data):
|
||||
def update(self, instance: Model, validated_data: dict[str, Any]):
|
||||
raise_errors_on_nested_writes("update", self, validated_data)
|
||||
info = model_meta.get_field_info(instance)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
@@ -14,6 +15,8 @@ from django.utils.translation import override
|
||||
from sentry_sdk.api import set_tag
|
||||
from structlog.contextvars import STRUCTLOG_KEY_PREFIX
|
||||
|
||||
from authentik.core.models import User
|
||||
|
||||
SESSION_KEY_IMPERSONATE_USER = "authentik/impersonate/user"
|
||||
SESSION_KEY_IMPERSONATE_ORIGINAL_USER = "authentik/impersonate/original_user"
|
||||
RESPONSE_HEADER_ID = "X-authentik-id"
|
||||
@@ -25,7 +28,7 @@ CTX_HOST = ContextVar[str | None](STRUCTLOG_KEY_PREFIX + "host", default=None)
|
||||
CTX_AUTH_VIA = ContextVar[str | None](STRUCTLOG_KEY_PREFIX + KEY_AUTH_VIA, default=None)
|
||||
|
||||
|
||||
def get_user(request):
|
||||
def get_user(request: WSGIRequest) -> AnonymousUser | User:
|
||||
if not hasattr(request, "_cached_user"):
|
||||
user = None
|
||||
if (authenticated_session := request.session.get("authenticatedsession", None)) is not None:
|
||||
@@ -46,7 +49,7 @@ async def aget_user(request):
|
||||
|
||||
|
||||
class AuthenticationMiddleware(MiddlewareMixin):
|
||||
def process_request(self, request):
|
||||
def process_request(self, request: WSGIRequest):
|
||||
if not hasattr(request, "session"):
|
||||
raise ImproperlyConfigured(
|
||||
"The Django authentication middleware requires session "
|
||||
|
||||
@@ -11,6 +11,7 @@ from django.contrib.auth.hashers import check_password
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
from django.contrib.sessions.base_session import AbstractBaseSession
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
from django.db import models
|
||||
from django.db.models import Q, QuerySet, options
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
@@ -22,6 +23,7 @@ from django_cte import CTE, with_cte
|
||||
from guardian.conf import settings
|
||||
from guardian.mixins import GuardianUserMixin
|
||||
from model_utils.managers import InheritanceManager
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import Serializer
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@@ -137,7 +139,7 @@ class AttributesMixin(models.Model):
|
||||
|
||||
|
||||
class GroupQuerySet(QuerySet):
|
||||
def with_children_recursive(self):
|
||||
def with_children_recursive(self) -> "GroupQuerySet":
|
||||
"""Recursively get all groups that have the current queryset as parents
|
||||
or are indirectly related."""
|
||||
|
||||
@@ -210,7 +212,7 @@ class Group(SerializerModel, AttributesMixin):
|
||||
("disable_group_superuser", _("Disable superuser status")),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f"Group {self.name}"
|
||||
|
||||
@property
|
||||
@@ -241,7 +243,7 @@ class Group(SerializerModel, AttributesMixin):
|
||||
class UserQuerySet(models.QuerySet):
|
||||
"""User queryset"""
|
||||
|
||||
def exclude_anonymous(self):
|
||||
def exclude_anonymous(self) -> "UserQuerySet":
|
||||
"""Exclude anonymous user"""
|
||||
return self.exclude(**{User.USERNAME_FIELD: settings.ANONYMOUS_USER_NAME})
|
||||
|
||||
@@ -249,7 +251,7 @@ class UserQuerySet(models.QuerySet):
|
||||
class UserManager(DjangoUserManager):
|
||||
"""User manager that doesn't assign is_superuser and is_staff"""
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self) -> UserQuerySet:
|
||||
"""Create special user queryset"""
|
||||
return UserQuerySet(self.model, using=self._db)
|
||||
|
||||
@@ -295,7 +297,7 @@ class User(SerializerModel, GuardianUserMixin, AttributesMixin, AbstractUser):
|
||||
models.Index(fields=["type"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.username
|
||||
|
||||
@staticmethod
|
||||
@@ -360,7 +362,13 @@ class User(SerializerModel, GuardianUserMixin, AttributesMixin, AbstractUser):
|
||||
"""superuser == staff user"""
|
||||
return self.is_superuser # type: ignore
|
||||
|
||||
def set_password(self, raw_password, signal=True, sender=None, request=None):
|
||||
def set_password(
|
||||
self,
|
||||
raw_password: str,
|
||||
signal: bool = True,
|
||||
sender: None = None,
|
||||
request: WSGIRequest | Request | None = None,
|
||||
) -> None:
|
||||
if self.pk and signal:
|
||||
from authentik.core.signals import password_changed
|
||||
|
||||
@@ -479,7 +487,7 @@ class Provider(SerializerModel):
|
||||
"""Get serializer for this model"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.name)
|
||||
|
||||
|
||||
@@ -611,7 +619,7 @@ class Application(SerializerModel, PolicyBindingModel):
|
||||
)
|
||||
return getattr(providers.first(), provider_type._meta.model_name)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.name)
|
||||
|
||||
class Meta:
|
||||
@@ -631,7 +639,7 @@ class ApplicationEntitlement(AttributesMixin, SerializerModel, PolicyBindingMode
|
||||
verbose_name_plural = _("Application Entitlements")
|
||||
unique_together = (("app", "name"),)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f"Application Entitlement {self.name} for app {self.app_id}"
|
||||
|
||||
@property
|
||||
@@ -640,7 +648,7 @@ class ApplicationEntitlement(AttributesMixin, SerializerModel, PolicyBindingMode
|
||||
|
||||
return ApplicationEntitlementSerializer
|
||||
|
||||
def supported_policy_binding_targets(self):
|
||||
def supported_policy_binding_targets(self) -> list[str]:
|
||||
return ["group", "user"]
|
||||
|
||||
|
||||
@@ -812,7 +820,7 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
|
||||
return {}
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.name)
|
||||
|
||||
class Meta:
|
||||
@@ -895,7 +903,7 @@ class ExpiringModel(models.Model):
|
||||
models.Index(fields=["expiring", "expires"]),
|
||||
]
|
||||
|
||||
def expire_action(self, *args, **kwargs):
|
||||
def expire_action(self, *args, **kwargs) -> tuple[int, dict[str, int]]:
|
||||
"""Handler which is called when this object is expired. By
|
||||
default the object is deleted. This is less efficient compared
|
||||
to bulk deleting objects, but classes like Token() need to change
|
||||
@@ -958,7 +966,7 @@ class Token(SerializerModel, ManagedModel, ExpiringModel):
|
||||
("set_token_key", _("Set a token's key")),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
description = f"{self.identifier}"
|
||||
if self.expiring:
|
||||
description += f" (expires={self.expires})"
|
||||
@@ -1023,7 +1031,7 @@ class PropertyMapping(SerializerModel, ManagedModel):
|
||||
except Exception as exc:
|
||||
raise PropertyMappingExpressionException(exc, self) from exc
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f"Property Mapping {self.name}"
|
||||
|
||||
class Meta:
|
||||
@@ -1051,7 +1059,7 @@ class Session(ExpiringModel, AbstractBaseSession):
|
||||
]
|
||||
default_permissions = []
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.session_key
|
||||
|
||||
class Keys(StrEnum):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""authentik sessions engine"""
|
||||
|
||||
import pickle # nosec
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY
|
||||
from django.contrib.sessions.backends.db import SessionStore as SessionBase
|
||||
@@ -9,13 +10,19 @@ from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.models import Session
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
def __init__(self, session_key=None, last_ip=None, last_user_agent=""):
|
||||
def __init__(
|
||||
self,
|
||||
session_key: str | None = None,
|
||||
last_ip: str | None = None,
|
||||
last_user_agent: str = "",
|
||||
):
|
||||
super().__init__(session_key)
|
||||
self._create_kwargs = {
|
||||
"last_ip": last_ip or ClientIPMiddleware.default_ip,
|
||||
@@ -23,16 +30,16 @@ class SessionStore(SessionBase):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_model_class(cls):
|
||||
def get_model_class(cls) -> type[Session]:
|
||||
from authentik.core.models import Session
|
||||
|
||||
return Session
|
||||
|
||||
@cached_property
|
||||
def model_fields(self):
|
||||
def model_fields(self) -> list[str]:
|
||||
return [k.value for k in self.model.Keys]
|
||||
|
||||
def _get_session_from_db(self):
|
||||
def _get_session_from_db(self) -> Session:
|
||||
try:
|
||||
return (
|
||||
self.model.objects.select_related(
|
||||
@@ -74,10 +81,10 @@ class SessionStore(SessionBase):
|
||||
LOGGER.warning(str(exc))
|
||||
self._session_key = None
|
||||
|
||||
def encode(self, session_dict):
|
||||
def encode(self, session_dict: dict[str, Any]) -> bytes:
|
||||
return pickle.dumps(session_dict, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
def decode(self, session_data):
|
||||
def decode(self, session_data: bytes) -> dict[str, Any]:
|
||||
try:
|
||||
return pickle.loads(session_data) # nosec
|
||||
except pickle.PickleError:
|
||||
@@ -86,7 +93,7 @@ class SessionStore(SessionBase):
|
||||
pass
|
||||
return {}
|
||||
|
||||
def load(self):
|
||||
def load(self) -> dict[str, Any]:
|
||||
s = self._get_session_from_db()
|
||||
if s:
|
||||
return {
|
||||
@@ -108,7 +115,7 @@ class SessionStore(SessionBase):
|
||||
else:
|
||||
return {}
|
||||
|
||||
def create_model_instance(self, data):
|
||||
def create_model_instance(self, data: dict[str, Any]) -> Session:
|
||||
args = {
|
||||
"session_key": self._get_or_create_session_key(),
|
||||
"expires": self.get_expiry_date(),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from django.http.response import (
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
HttpResponseNotAllowed,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseServerError,
|
||||
)
|
||||
@@ -61,6 +62,6 @@ class ServerErrorView(TemplateView):
|
||||
response_class = ServerErrorTemplateResponse
|
||||
template_name = "if/error.html"
|
||||
|
||||
def dispatch(self, *args, **kwargs): # pragma: no cover
|
||||
def dispatch(self, *args, **kwargs) -> HttpResponseNotAllowed: # pragma: no cover
|
||||
"""Little wrapper so django accepts this function"""
|
||||
return super().dispatch(*args, **kwargs)
|
||||
|
||||
@@ -4,7 +4,7 @@ from hashlib import sha256
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.http import HttpRequest, HttpResponseBadRequest
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from requests.exceptions import RequestException
|
||||
@@ -68,32 +68,44 @@ class AuthenticatorSMSStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
help_text=_("Optionally modify the payload being sent to custom providers."),
|
||||
)
|
||||
|
||||
def send(self, token: str, device: "SMSDevice"):
|
||||
def send(self, request: HttpRequest, token: str, device: "SMSDevice"):
|
||||
"""Send message via selected provider"""
|
||||
if self.provider == SMSProviders.TWILIO:
|
||||
return self.send_twilio(token, device)
|
||||
return self.send_twilio(request, token, device)
|
||||
if self.provider == SMSProviders.GENERIC:
|
||||
return self.send_generic(token, device)
|
||||
return self.send_generic(request, token, device)
|
||||
raise ValueError(f"invalid provider {self.provider}")
|
||||
|
||||
def get_message(self, token: str) -> str:
|
||||
"""Get SMS message"""
|
||||
return _("Use this code to authenticate in authentik: {token}".format_map({"token": token}))
|
||||
|
||||
def send_twilio(self, token: str, device: "SMSDevice"):
|
||||
def send_twilio(self, request: HttpRequest, token: str, device: "SMSDevice"):
|
||||
"""send sms via twilio provider"""
|
||||
client = Client(self.account_sid, self.auth)
|
||||
message_body = str(self.get_message(token))
|
||||
if self.mapping:
|
||||
payload = sanitize_item(
|
||||
self.mapping.evaluate(
|
||||
user=device.user,
|
||||
request=request,
|
||||
device=device,
|
||||
token=token,
|
||||
stage=self,
|
||||
)
|
||||
)
|
||||
message_body = payload.get("message", message_body)
|
||||
|
||||
try:
|
||||
message = client.messages.create(
|
||||
to=device.phone_number, from_=self.from_number, body=str(self.get_message(token))
|
||||
to=device.phone_number, from_=self.from_number, body=message_body
|
||||
)
|
||||
LOGGER.debug("Sent SMS", to=device, message=message.sid)
|
||||
except TwilioRestException as exc:
|
||||
LOGGER.warning("Error sending token by Twilio SMS", exc=exc, msg=exc.msg)
|
||||
raise ValidationError(exc.msg) from None
|
||||
|
||||
def send_generic(self, token: str, device: "SMSDevice"):
|
||||
def send_generic(self, request: HttpRequest, token: str, device: "SMSDevice"):
|
||||
"""Send SMS via outside API"""
|
||||
payload = {
|
||||
"From": self.from_number,
|
||||
@@ -106,7 +118,7 @@ class AuthenticatorSMSStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
payload = sanitize_item(
|
||||
self.mapping.evaluate(
|
||||
user=device.user,
|
||||
request=None,
|
||||
request=request,
|
||||
device=device,
|
||||
token=token,
|
||||
stage=self,
|
||||
|
||||
@@ -71,7 +71,7 @@ class AuthenticatorSMSStageView(ChallengeStageView):
|
||||
raise ValidationError(_("Invalid phone number"))
|
||||
# No code yet, but we have a phone number, so send a verification message
|
||||
device: SMSDevice = self.request.session[SESSION_KEY_SMS_DEVICE]
|
||||
stage.send(device.token, device)
|
||||
stage.send(self.request, device.token, device)
|
||||
|
||||
def _has_phone_number(self) -> str | None:
|
||||
context = self.executor.plan.context
|
||||
|
||||
@@ -124,7 +124,7 @@ def select_challenge(request: HttpRequest, device: Device):
|
||||
def select_challenge_sms(request: HttpRequest, device: SMSDevice):
|
||||
"""Send SMS"""
|
||||
device.generate_token()
|
||||
device.stage.send(device.token, device)
|
||||
device.stage.send(request, device.token, device)
|
||||
|
||||
|
||||
def select_challenge_email(request: HttpRequest, device: EmailDevice):
|
||||
|
||||
20
monkeytype_config.py
Normal file
20
monkeytype_config.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# Standard Library
|
||||
import os
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
|
||||
# 3rd-party
|
||||
from monkeytype.config import DefaultConfig
|
||||
|
||||
|
||||
class MonkeyConfig(DefaultConfig):
|
||||
@contextmanager
|
||||
def cli_context(self, command: str) -> Iterator[None]:
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
|
||||
import django
|
||||
|
||||
django.setup()
|
||||
yield
|
||||
|
||||
|
||||
CONFIG = MonkeyConfig()
|
||||
@@ -161,38 +161,6 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
|
||||
${msg("This is the password to be used with basic auth")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Mapping")} name="mapping">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (
|
||||
query?: string,
|
||||
): Promise<NotificationWebhookMapping[]> => {
|
||||
const args: PropertymappingsNotificationListRequest = {
|
||||
ordering: "saml_name",
|
||||
};
|
||||
if (query !== undefined) {
|
||||
args.search = query;
|
||||
}
|
||||
const items = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsNotificationList(args);
|
||||
return items.results;
|
||||
}}
|
||||
.renderElement=${(item: NotificationWebhookMapping): string => {
|
||||
return item.name;
|
||||
}}
|
||||
.value=${(item: NotificationWebhookMapping | undefined): string | undefined => {
|
||||
return item?.pk;
|
||||
}}
|
||||
.selected=${(item: NotificationWebhookMapping): boolean => {
|
||||
return this.instance?.mapping === item.pk;
|
||||
}}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Modify the payload sent to the custom provider.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -269,6 +237,38 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
|
||||
${this.provider === ProviderEnum.Generic
|
||||
? this.renderProviderGeneric()
|
||||
: this.renderProviderTwillio()}
|
||||
<ak-form-element-horizontal label=${msg("Mapping")} name="mapping">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (
|
||||
query?: string,
|
||||
): Promise<NotificationWebhookMapping[]> => {
|
||||
const args: PropertymappingsNotificationListRequest = {
|
||||
ordering: "name",
|
||||
};
|
||||
if (query) {
|
||||
args.search = query;
|
||||
}
|
||||
const items = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsNotificationList(args);
|
||||
return items.results;
|
||||
}}
|
||||
.renderElement=${(item: NotificationWebhookMapping): string => {
|
||||
return item.name;
|
||||
}}
|
||||
.value=${(item?: NotificationWebhookMapping) => {
|
||||
return item?.pk;
|
||||
}}
|
||||
.selected=${(item: NotificationWebhookMapping): boolean => {
|
||||
return this.instance?.mapping === item.pk;
|
||||
}}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Modify the payload sent to the provider.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="verifyOnly">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="de" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
@@ -596,9 +596,9 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>Die URL "
|
||||
<x id="0" equiv-text="${this.url}"/>" wurde nicht gefunden.</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>Die URL "
|
||||
<x id="0" equiv-text="${this.url}"/>" wurde nicht gefunden.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
@@ -1709,8 +1709,8 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>Geben Sie entweder eine vollständige URL oder einen relativen Pfad ein oder geben Sie 'fa://fa-test' ein, um das Font Awesome-Icon "fa-test" zu verwenden</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>Geben Sie entweder eine vollständige URL oder einen relativen Pfad ein oder geben Sie 'fa://fa-test' ein, um das Font Awesome-Icon "fa-test" zu verwenden</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
@@ -3756,10 +3756,10 @@ Hier können nur Policies verwendet werden, da der Zugriff geprüft wird, bevor
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>Bist du sicher, dass du
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>"aktualisieren möchtest?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>"aktualisieren möchtest?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
@@ -4632,11 +4632,6 @@ Beim Erstellen eines festen Auswahlfelds aktiviere „Als Ausdruck interpretiere
|
||||
<source>Mapping</source>
|
||||
<target>Zuordnung</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Passe die an den Custom Provider gesendete Payload an.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -4824,8 +4819,8 @@ Beim Erstellen eines festen Auswahlfelds aktiviere „Als Ausdruck interpretiere
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>Ein "Roaming"-Authentifikator, wie ein YubiKey</target>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>Ein "Roaming"-Authentifikator, wie ein YubiKey</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sfffba7b23d8fb40c">
|
||||
@@ -5183,7 +5178,7 @@ Beim Erstellen eines festen Auswahlfelds aktiviere „Als Ausdruck interpretiere
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>Wenn auf eine Dauer größer als 0 gesetzt, hat der Benutzer die Option „Angemeldet bleiben“, wodurch seine Sitzung um die hier angegebene Zeit verlängert wird.</target>
|
||||
|
||||
</trans-unit>
|
||||
@@ -7442,7 +7437,7 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<target>Benutzer erfolgreich erstellt und zu Gruppe <x id="0" equiv-text="${this.group.name}"/> hinzugefügt.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s824e0943a7104668">
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>Dieser Benutzer wird der Gruppe &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot; hinzugefügt.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||
@@ -8719,7 +8714,7 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<target>Gruppe synchronisieren</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, vom typ <x id="2" equiv-text="${p.type}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s25bacc19d98b444e">
|
||||
@@ -8967,8 +8962,8 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<target>Gültige Redirect-URIs nach einem erfolgreichen Autorisierungsflow. Gib hier auch alle Origins für Implicit-Flows an.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4c49d27de60a532b">
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>Um jede Redirect-URI zu erlauben, setze den Modus auf Regex und den Wert auf ".*". Beachte die möglichen Sicherheitsimplikationen.</target>
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>Um jede Redirect-URI zu erlauben, setze den Modus auf Regex und den Wert auf ".*". Beachte die möglichen Sicherheitsimplikationen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa52bf79fe1ccb13e">
|
||||
<source>Federated OIDC Sources</source>
|
||||
@@ -9717,7 +9712,7 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<target>Wie die Authentifizierung während eines Authorization-Code-Token-Anforderungsflows durchgeführt wird</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s844baf19a6c4a9b4">
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<target>„Angemeldet bleiben auf diesem Gerät“ aktivieren</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa72bca733f40692">
|
||||
@@ -9916,7 +9911,10 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
<target><x id="0" equiv-text="${item.name || item.username}"/> - Passwort ändern.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
||||
|
||||
@@ -3708,10 +3708,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>Mapping</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Modify the payload sent to the custom provider.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
<target>Stage used to configure an SMS-based TOTP authenticator.</target>
|
||||
@@ -7777,6 +7773,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4569,11 +4569,6 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
|
||||
<source>Mapping</source>
|
||||
<target>Asignación</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Modificar la carga útil enviada al proveedor personalizado.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9328,6 +9323,9 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4633,11 +4633,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>Mappage</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Modifier le contenu envoyé aux fournisseurs personnalisés.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9893,6 +9888,9 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4634,11 +4634,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>Mappatura</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Modifica il payload inviato al provider personalizzato.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9920,6 +9915,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
<target>Aggiorna <x id="0" equiv-text="${item.name || item.username}"/> password</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4579,11 +4579,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>매핑</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>사용자 지정 공급자로 전송되는 페이로드를 수정합니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9234,6 +9229,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4590,11 +4590,6 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
|
||||
<source>Mapping</source>
|
||||
<target>Toewijzing</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Wijzig het payload-bericht dat naar de aangepaste provider wordt verzonden.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9138,6 +9133,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4635,11 +4635,6 @@ Można tu używać tylko zasad, ponieważ dostęp jest sprawdzany przed uwierzyt
|
||||
<source>Mapping</source>
|
||||
<target>Mapowanie</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Zmodyfikuj payload wysłany do niestandardowego dostawcy.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9560,6 +9555,9 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4603,11 +4603,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>Ḿàƥƥĩńĝ</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Ḿōďĩƒŷ ţĥē ƥàŷĺōàď śēńţ ţō ţĥē ćũśţōḿ ƥŕōvĩďēŕ.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9569,4 +9564,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
||||
@@ -4634,11 +4634,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>Сопоставление</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Изменить данные, отправляемые пользовательскому провайдеру.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9652,6 +9647,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4603,11 +4603,6 @@ Belirlenen seçeneklerden biri veya her ikisi de eşiğe eşit veya eşiğin üz
|
||||
<source>Mapping</source>
|
||||
<target>Eşleme</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>Özel sağlayıcıya gönderilen yükü değiştirin.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9624,6 +9619,9 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -3309,9 +3309,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="sa92398dba8b12d85">
|
||||
<source>Mapping</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
</trans-unit>
|
||||
@@ -6395,6 +6392,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -4634,11 +4634,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>映射</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>修改发送到自定义提供程序的载荷。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9917,6 +9912,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -3510,9 +3510,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="sa92398dba8b12d85">
|
||||
<source>Mapping</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
<target>用于配置基于短信的 TOTP 身份验证器的阶段。</target>
|
||||
@@ -7479,6 +7476,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4571,11 +4571,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Mapping</source>
|
||||
<target>對應</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s38162f615710c7b4">
|
||||
<source>Modify the payload sent to the custom provider.</source>
|
||||
<target>修改發送至客製化供應商的酬載。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e830ae7688d1219">
|
||||
<source>Stage used to configure an SMS-based TOTP authenticator.</source>
|
||||
@@ -9213,6 +9208,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s547b687213f48489">
|
||||
<source>Update <x id="0" equiv-text="${item.name || item.username}"/>'s password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3c619d54c995d4ab">
|
||||
<source>Modify the payload sent to the provider.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6,7 +6,7 @@ This stage configures an SMS-based authenticator using either Twilio, or a gener
|
||||
|
||||
## Providers
|
||||
|
||||
#### Twilio
|
||||
### Twilio
|
||||
|
||||
Navigate to https://console.twilio.com/, and log in to your existing account, or create a new one.
|
||||
|
||||
@@ -22,7 +22,23 @@ The other two steps can be skipped using the _Skip setup_ button.
|
||||
|
||||
Navigate back to the root of your Twilio console, and copy the Auth token. This is the value for the _Twilio Auth Token_ field in authentik. Copy the value of **Account SID**. This is the value for the _Twilio Account SID_ field in authentik.
|
||||
|
||||
#### Generic
|
||||
#### Custom SMS message :ak-version[2025.8]
|
||||
|
||||
Using a property mapping, it is possible to customize the SMS message sent via Twilio. The mapping should return a dictionary with the `message` key, which will be sent to the user. For example:
|
||||
|
||||
```python
|
||||
return {
|
||||
"message": f"This is a custom message for {request.http_request.brand.branding_title} SMS authentication. The code is {token}".
|
||||
}
|
||||
```
|
||||
|
||||
Variables related to different objects can be used within the message:
|
||||
|
||||
- The end user device, for example: `device.phone_number`
|
||||
- The stage sending the message, for example: `stage.from_number`
|
||||
- The request this verification code is being sent from, for example: `request.http_request.brand`
|
||||
|
||||
### Generic
|
||||
|
||||
For the generic provider, a POST request will be sent to the URL you have specified in the _External API URL_ field. The request payload looks like this
|
||||
|
||||
@@ -36,7 +52,9 @@ For the generic provider, a POST request will be sent to the URL you have specif
|
||||
|
||||
Authentication can either be done as HTTP Basic, or via a Bearer Token. Any response with status 400 or above is counted as failed, and will prevent the user from proceeding.
|
||||
|
||||
Starting with authentik 2022.10, a custom webhook mapping can be specified to freely customize the payload of the request. For example:
|
||||
#### Custom SMS message
|
||||
|
||||
A custom webhook mapping can be used to customize the SMS message sent to users. For example:
|
||||
|
||||
```python
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user