authentik.flows.models
Flow models
1"""Flow models""" 2 3from base64 import b64decode, b64encode 4from pickle import dumps, loads # nosec 5from typing import TYPE_CHECKING 6from uuid import uuid4 7 8from django.core.validators import validate_slug 9from django.db import models 10from django.http import HttpRequest 11from django.utils.translation import gettext_lazy as _ 12from model_utils.managers import InheritanceManager 13from rest_framework.serializers import BaseSerializer 14from structlog.stdlib import get_logger 15 16from authentik.admin.files.fields import FileField 17from authentik.admin.files.manager import get_file_manager 18from authentik.admin.files.usage import FileUsage 19from authentik.core.models import Token 20from authentik.core.types import UserSettingSerializer 21from authentik.flows.challenge import FlowLayout 22from authentik.lib.config import CONFIG 23from authentik.lib.models import InheritanceForeignKey, InternallyManagedMixin, SerializerModel 24from authentik.lib.utils.reflection import class_to_path 25from authentik.policies.models import PolicyBindingModel 26 27if TYPE_CHECKING: 28 from authentik.flows.planner import FlowPlan 29 from authentik.flows.stage import StageView 30 31LOGGER = get_logger() 32 33 34class FlowAuthenticationRequirement(models.TextChoices): 35 """Required level of authentication and authorization to access a flow""" 36 37 NONE = "none" 38 REQUIRE_AUTHENTICATED = "require_authenticated" 39 REQUIRE_UNAUTHENTICATED = "require_unauthenticated" 40 REQUIRE_SUPERUSER = "require_superuser" 41 REQUIRE_REDIRECT = "require_redirect" 42 REQUIRE_OUTPOST = "require_outpost" 43 REQUIRE_TOKEN = "require_token" 44 45 46class NotConfiguredAction(models.TextChoices): 47 """Decides how the FlowExecutor should proceed when a stage isn't configured""" 48 49 SKIP = "skip" 50 DENY = "deny" 51 CONFIGURE = "configure" 52 53 54class InvalidResponseAction(models.TextChoices): 55 """Configure how the flow executor should handle invalid responses to challenges""" 56 57 RETRY = "retry" 58 RESTART = "restart" 59 RESTART_WITH_CONTEXT = "restart_with_context" 60 61 62class FlowDeniedAction(models.TextChoices): 63 """Configure what response is given to denied flow executions""" 64 65 MESSAGE_CONTINUE = "message_continue" 66 MESSAGE = "message" 67 CONTINUE = "continue" 68 69 70class FlowDesignation(models.TextChoices): 71 """Designation of what a Flow should be used for. At a later point, this 72 should be replaced by a database entry.""" 73 74 AUTHENTICATION = "authentication" 75 AUTHORIZATION = "authorization" 76 INVALIDATION = "invalidation" 77 ENROLLMENT = "enrollment" 78 UNRENOLLMENT = "unenrollment" 79 RECOVERY = "recovery" 80 STAGE_CONFIGURATION = "stage_configuration" 81 82 83class Stage(SerializerModel): 84 """Stage is an instance of a component used in a flow. This can verify the user, 85 enroll the user or offer a way of recovery""" 86 87 stage_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 88 89 name = models.TextField(unique=True) 90 91 objects = InheritanceManager() 92 93 @property 94 def view(self) -> type[StageView]: 95 """Return StageView class that implements logic for this stage""" 96 # This is a bit of a workaround, since we can't set class methods with setattr 97 if hasattr(self, "__in_memory_type"): 98 return getattr(self, "__in_memory_type") 99 raise NotImplementedError 100 101 @property 102 def component(self) -> str: 103 """Return component used to edit this object""" 104 raise NotImplementedError 105 106 def ui_user_settings(self) -> UserSettingSerializer | None: 107 """Entrypoint to integrate with User settings. Can either return None if no 108 user settings are available, or a challenge.""" 109 return None 110 111 @property 112 def is_in_memory(self): 113 return hasattr(self, "__in_memory_type") 114 115 def __str__(self): 116 if self.is_in_memory: 117 return f"In-memory Stage {getattr(self, '__in_memory_type')}" 118 return f"Stage {self.name}" 119 120 121def in_memory_stage(view: type[StageView], **kwargs) -> Stage: 122 """Creates an in-memory stage instance, based on a `view` as view. 123 Any key-word arguments are set as attributes on the stage object, 124 accessible via `self.executor.current_stage`.""" 125 stage = Stage() 126 # Because we can't pickle a locally generated function, 127 # we set the view as a separate property and reference a generic function 128 # that returns that member 129 setattr(stage, "__in_memory_type", view) 130 stage.name = _("Dynamic In-memory stage: {doc}".format_map({"doc": view.__doc__})) 131 stage._meta.verbose_name = class_to_path(view) 132 for key, value in kwargs.items(): 133 setattr(stage, key, value) 134 return stage 135 136 137class Flow(SerializerModel, PolicyBindingModel): 138 """Flow describes how a series of Stages should be executed to authenticate/enroll/recover 139 a user. Additionally, policies can be applied, to specify which users 140 have access to this flow.""" 141 142 flow_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 143 144 name = models.TextField() 145 slug = models.TextField( 146 validators=[validate_slug], 147 unique=True, 148 help_text=_("Visible in the URL."), 149 ) 150 151 title = models.TextField(help_text=_("Shown as the Title in Flow pages.")) 152 layout = models.TextField(default=FlowLayout.STACKED, choices=FlowLayout.choices) 153 154 designation = models.CharField( 155 max_length=100, 156 choices=FlowDesignation.choices, 157 help_text=_( 158 "Decides what this Flow is used for. For example, the Authentication flow " 159 "is redirect to when an un-authenticated user visits authentik." 160 ), 161 ) 162 163 background = FileField( 164 blank=True, 165 default="", 166 help_text=_("Background shown during execution"), 167 ) 168 169 compatibility_mode = models.BooleanField( 170 default=False, 171 help_text=_( 172 "Enable compatibility mode, increases compatibility with " 173 "password managers on mobile devices." 174 ), 175 ) 176 177 denied_action = models.TextField( 178 choices=FlowDeniedAction.choices, 179 default=FlowDeniedAction.MESSAGE_CONTINUE, 180 help_text=_("Configure what should happen when a flow denies access to a user."), 181 ) 182 183 authentication = models.TextField( 184 choices=FlowAuthenticationRequirement.choices, 185 default=FlowAuthenticationRequirement.NONE, 186 help_text=_("Required level of authentication and authorization to access a flow."), 187 ) 188 189 def background_url( 190 self, 191 request: HttpRequest | None = None, 192 use_cache: bool = True, 193 ) -> str: 194 """Get the URL to the background image""" 195 if not self.background: 196 if request: 197 return request.brand.branding_default_flow_background_url( 198 request, 199 use_cache=use_cache, 200 ) 201 return ( 202 CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg" 203 ) 204 205 return get_file_manager(FileUsage.MEDIA).file_url( 206 self.background, 207 request, 208 use_cache=use_cache, 209 ) 210 211 def background_themed_urls( 212 self, 213 request: HttpRequest | None = None, 214 use_cache: bool = True, 215 ) -> dict[str, str] | None: 216 """Get themed URLs for background if it contains %(theme)s""" 217 if not self.background: 218 if request: 219 return request.brand.branding_default_flow_background_themed_urls( 220 request, 221 use_cache=use_cache, 222 ) 223 return None 224 225 return get_file_manager(FileUsage.MEDIA).themed_urls( 226 self.background, 227 request, 228 use_cache=use_cache, 229 ) 230 231 stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True) 232 233 @property 234 def serializer(self) -> type[BaseSerializer]: 235 from authentik.flows.api.flows import FlowSerializer 236 237 return FlowSerializer 238 239 def __str__(self) -> str: 240 return f"Flow {self.name} ({self.slug})" 241 242 class Meta: 243 verbose_name = _("Flow") 244 verbose_name_plural = _("Flows") 245 246 permissions = [ 247 ("export_flow", _("Can export a Flow")), 248 ("inspect_flow", _("Can inspect a Flow's execution")), 249 ("view_flow_cache", _("View Flow's cache metrics")), 250 ("clear_flow_cache", _("Clear Flow's cache metrics")), 251 ] 252 253 254class FlowStageBinding(SerializerModel, PolicyBindingModel): 255 """Relationship between Flow and Stage. Order is required and unique for 256 each flow-stage Binding. Additionally, policies can be specified, which determine if 257 this Binding applies to the current user""" 258 259 fsb_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 260 261 target = models.ForeignKey("Flow", on_delete=models.CASCADE) 262 stage = InheritanceForeignKey(Stage, on_delete=models.CASCADE) 263 264 evaluate_on_plan = models.BooleanField( 265 default=False, 266 help_text=_("Evaluate policies during the Flow planning process."), 267 ) 268 re_evaluate_policies = models.BooleanField( 269 default=True, 270 help_text=_("Evaluate policies when the Stage is presented to the user."), 271 ) 272 273 invalid_response_action = models.TextField( 274 choices=InvalidResponseAction.choices, 275 default=InvalidResponseAction.RETRY, 276 help_text=_( 277 "Configure how the flow executor should handle an invalid response to a " 278 "challenge. RETRY returns the error message and a similar challenge to the " 279 "executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT " 280 "restarts the flow while keeping the current context." 281 ), 282 ) 283 284 order = models.IntegerField() 285 286 objects = InheritanceManager() 287 288 @property 289 def serializer(self) -> type[BaseSerializer]: 290 from authentik.flows.api.bindings import FlowStageBindingSerializer 291 292 return FlowStageBindingSerializer 293 294 def __str__(self) -> str: 295 if not self.target_id and self.stage_id: 296 return f"In-memory Flow-stage binding {self.stage}" 297 return f"Flow-stage binding #{self.order} to {self.target_id}" 298 299 class Meta: 300 ordering = ["target", "order"] 301 302 verbose_name = _("Flow Stage Binding") 303 verbose_name_plural = _("Flow Stage Bindings") 304 unique_together = (("target", "stage", "order"),) 305 306 307class ConfigurableStage(models.Model): 308 """Abstract base class for a Stage that can be configured by the enduser. 309 The stage should create a default flow with the configure_stage designation during 310 migration.""" 311 312 configure_flow = models.ForeignKey( 313 Flow, 314 on_delete=models.SET_NULL, 315 null=True, 316 blank=True, 317 help_text=_( 318 "Flow used by an authenticated user to configure this Stage. " 319 "If empty, user will not be able to configure this stage." 320 ), 321 ) 322 323 class Meta: 324 abstract = True 325 326 327class FriendlyNamedStage(models.Model): 328 """Abstract base class for a Stage that can have a user friendly name configured.""" 329 330 friendly_name = models.TextField(blank=True) 331 332 class Meta: 333 abstract = True 334 335 336class FlowToken(InternallyManagedMixin, Token): 337 """Subclass of a standard Token, stores the currently active flow plan upon creation. 338 Can be used to later resume a flow.""" 339 340 flow = models.ForeignKey(Flow, on_delete=models.CASCADE) 341 _plan = models.TextField() 342 revoke_on_execution = models.BooleanField(default=True) 343 344 @staticmethod 345 def pickle(plan: FlowPlan) -> str: 346 """Pickle into string""" 347 data = dumps(plan) 348 return b64encode(data).decode() 349 350 @property 351 def plan(self) -> FlowPlan: 352 """Load Flow plan from pickled version""" 353 return loads(b64decode(self._plan.encode())) # nosec 354 355 def __str__(self) -> str: 356 return f"Flow Token {super().__str__()}" 357 358 class Meta: 359 verbose_name = _("Flow Token") 360 verbose_name_plural = _("Flow Tokens")
35class FlowAuthenticationRequirement(models.TextChoices): 36 """Required level of authentication and authorization to access a flow""" 37 38 NONE = "none" 39 REQUIRE_AUTHENTICATED = "require_authenticated" 40 REQUIRE_UNAUTHENTICATED = "require_unauthenticated" 41 REQUIRE_SUPERUSER = "require_superuser" 42 REQUIRE_REDIRECT = "require_redirect" 43 REQUIRE_OUTPOST = "require_outpost" 44 REQUIRE_TOKEN = "require_token"
Required level of authentication and authorization to access a flow
47class NotConfiguredAction(models.TextChoices): 48 """Decides how the FlowExecutor should proceed when a stage isn't configured""" 49 50 SKIP = "skip" 51 DENY = "deny" 52 CONFIGURE = "configure"
Decides how the FlowExecutor should proceed when a stage isn't configured
55class InvalidResponseAction(models.TextChoices): 56 """Configure how the flow executor should handle invalid responses to challenges""" 57 58 RETRY = "retry" 59 RESTART = "restart" 60 RESTART_WITH_CONTEXT = "restart_with_context"
Configure how the flow executor should handle invalid responses to challenges
63class FlowDeniedAction(models.TextChoices): 64 """Configure what response is given to denied flow executions""" 65 66 MESSAGE_CONTINUE = "message_continue" 67 MESSAGE = "message" 68 CONTINUE = "continue"
Configure what response is given to denied flow executions
71class FlowDesignation(models.TextChoices): 72 """Designation of what a Flow should be used for. At a later point, this 73 should be replaced by a database entry.""" 74 75 AUTHENTICATION = "authentication" 76 AUTHORIZATION = "authorization" 77 INVALIDATION = "invalidation" 78 ENROLLMENT = "enrollment" 79 UNRENOLLMENT = "unenrollment" 80 RECOVERY = "recovery" 81 STAGE_CONFIGURATION = "stage_configuration"
Designation of what a Flow should be used for. At a later point, this should be replaced by a database entry.
84class Stage(SerializerModel): 85 """Stage is an instance of a component used in a flow. This can verify the user, 86 enroll the user or offer a way of recovery""" 87 88 stage_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 89 90 name = models.TextField(unique=True) 91 92 objects = InheritanceManager() 93 94 @property 95 def view(self) -> type[StageView]: 96 """Return StageView class that implements logic for this stage""" 97 # This is a bit of a workaround, since we can't set class methods with setattr 98 if hasattr(self, "__in_memory_type"): 99 return getattr(self, "__in_memory_type") 100 raise NotImplementedError 101 102 @property 103 def component(self) -> str: 104 """Return component used to edit this object""" 105 raise NotImplementedError 106 107 def ui_user_settings(self) -> UserSettingSerializer | None: 108 """Entrypoint to integrate with User settings. Can either return None if no 109 user settings are available, or a challenge.""" 110 return None 111 112 @property 113 def is_in_memory(self): 114 return hasattr(self, "__in_memory_type") 115 116 def __str__(self): 117 if self.is_in_memory: 118 return f"In-memory Stage {getattr(self, '__in_memory_type')}" 119 return f"Stage {self.name}"
Stage is an instance of a component used in a flow. This can verify the user, enroll the user or offer a way of recovery
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
94 @property 95 def view(self) -> type[StageView]: 96 """Return StageView class that implements logic for this stage""" 97 # This is a bit of a workaround, since we can't set class methods with setattr 98 if hasattr(self, "__in_memory_type"): 99 return getattr(self, "__in_memory_type") 100 raise NotImplementedError
Return StageView class that implements logic for this stage
102 @property 103 def component(self) -> str: 104 """Return component used to edit this object""" 105 raise NotImplementedError
Return component used to edit this object
107 def ui_user_settings(self) -> UserSettingSerializer | None: 108 """Entrypoint to integrate with User settings. Can either return None if no 109 user settings are available, or a challenge.""" 110 return None
Entrypoint to integrate with User settings. Can either return None if no user settings are available, or a challenge.
Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.
In the example::
class Pizza(Model):
toppings = ManyToManyField(Topping, related_name='pizzas')
Pizza.toppings and Topping.pizzas are ManyToManyDescriptor
instances.
Most of the implementation is delegated to a dynamically defined manager
class built by create_forward_many_to_many_manager() defined below.
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.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant is a ReverseOneToOneDescriptor instance.
Inherited Members
The requested object does not exist
The query returned multiple objects when only one was expected.
122def in_memory_stage(view: type[StageView], **kwargs) -> Stage: 123 """Creates an in-memory stage instance, based on a `view` as view. 124 Any key-word arguments are set as attributes on the stage object, 125 accessible via `self.executor.current_stage`.""" 126 stage = Stage() 127 # Because we can't pickle a locally generated function, 128 # we set the view as a separate property and reference a generic function 129 # that returns that member 130 setattr(stage, "__in_memory_type", view) 131 stage.name = _("Dynamic In-memory stage: {doc}".format_map({"doc": view.__doc__})) 132 stage._meta.verbose_name = class_to_path(view) 133 for key, value in kwargs.items(): 134 setattr(stage, key, value) 135 return stage
Creates an in-memory stage instance, based on a view as view.
Any key-word arguments are set as attributes on the stage object,
accessible via self.executor.current_stage.
138class Flow(SerializerModel, PolicyBindingModel): 139 """Flow describes how a series of Stages should be executed to authenticate/enroll/recover 140 a user. Additionally, policies can be applied, to specify which users 141 have access to this flow.""" 142 143 flow_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 144 145 name = models.TextField() 146 slug = models.TextField( 147 validators=[validate_slug], 148 unique=True, 149 help_text=_("Visible in the URL."), 150 ) 151 152 title = models.TextField(help_text=_("Shown as the Title in Flow pages.")) 153 layout = models.TextField(default=FlowLayout.STACKED, choices=FlowLayout.choices) 154 155 designation = models.CharField( 156 max_length=100, 157 choices=FlowDesignation.choices, 158 help_text=_( 159 "Decides what this Flow is used for. For example, the Authentication flow " 160 "is redirect to when an un-authenticated user visits authentik." 161 ), 162 ) 163 164 background = FileField( 165 blank=True, 166 default="", 167 help_text=_("Background shown during execution"), 168 ) 169 170 compatibility_mode = models.BooleanField( 171 default=False, 172 help_text=_( 173 "Enable compatibility mode, increases compatibility with " 174 "password managers on mobile devices." 175 ), 176 ) 177 178 denied_action = models.TextField( 179 choices=FlowDeniedAction.choices, 180 default=FlowDeniedAction.MESSAGE_CONTINUE, 181 help_text=_("Configure what should happen when a flow denies access to a user."), 182 ) 183 184 authentication = models.TextField( 185 choices=FlowAuthenticationRequirement.choices, 186 default=FlowAuthenticationRequirement.NONE, 187 help_text=_("Required level of authentication and authorization to access a flow."), 188 ) 189 190 def background_url( 191 self, 192 request: HttpRequest | None = None, 193 use_cache: bool = True, 194 ) -> str: 195 """Get the URL to the background image""" 196 if not self.background: 197 if request: 198 return request.brand.branding_default_flow_background_url( 199 request, 200 use_cache=use_cache, 201 ) 202 return ( 203 CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg" 204 ) 205 206 return get_file_manager(FileUsage.MEDIA).file_url( 207 self.background, 208 request, 209 use_cache=use_cache, 210 ) 211 212 def background_themed_urls( 213 self, 214 request: HttpRequest | None = None, 215 use_cache: bool = True, 216 ) -> dict[str, str] | None: 217 """Get themed URLs for background if it contains %(theme)s""" 218 if not self.background: 219 if request: 220 return request.brand.branding_default_flow_background_themed_urls( 221 request, 222 use_cache=use_cache, 223 ) 224 return None 225 226 return get_file_manager(FileUsage.MEDIA).themed_urls( 227 self.background, 228 request, 229 use_cache=use_cache, 230 ) 231 232 stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True) 233 234 @property 235 def serializer(self) -> type[BaseSerializer]: 236 from authentik.flows.api.flows import FlowSerializer 237 238 return FlowSerializer 239 240 def __str__(self) -> str: 241 return f"Flow {self.name} ({self.slug})" 242 243 class Meta: 244 verbose_name = _("Flow") 245 verbose_name_plural = _("Flows") 246 247 permissions = [ 248 ("export_flow", _("Can export a Flow")), 249 ("inspect_flow", _("Can inspect a Flow's execution")), 250 ("view_flow_cache", _("View Flow's cache metrics")), 251 ("clear_flow_cache", _("Clear Flow's cache metrics")), 252 ]
Flow describes how a series of Stages should be executed to authenticate/enroll/recover a user. Additionally, policies can be applied, to specify which users have access to this flow.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
190 def background_url( 191 self, 192 request: HttpRequest | None = None, 193 use_cache: bool = True, 194 ) -> str: 195 """Get the URL to the background image""" 196 if not self.background: 197 if request: 198 return request.brand.branding_default_flow_background_url( 199 request, 200 use_cache=use_cache, 201 ) 202 return ( 203 CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg" 204 ) 205 206 return get_file_manager(FileUsage.MEDIA).file_url( 207 self.background, 208 request, 209 use_cache=use_cache, 210 )
Get the URL to the background image
212 def background_themed_urls( 213 self, 214 request: HttpRequest | None = None, 215 use_cache: bool = True, 216 ) -> dict[str, str] | None: 217 """Get themed URLs for background if it contains %(theme)s""" 218 if not self.background: 219 if request: 220 return request.brand.branding_default_flow_background_themed_urls( 221 request, 222 use_cache=use_cache, 223 ) 224 return None 225 226 return get_file_manager(FileUsage.MEDIA).themed_urls( 227 self.background, 228 request, 229 use_cache=use_cache, 230 )
Get themed URLs for background if it contains %(theme)s
Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.
In the example::
class Pizza(Model):
toppings = ManyToManyField(Topping, related_name='pizzas')
Pizza.toppings and Topping.pizzas are ManyToManyDescriptor
instances.
Most of the implementation is delegated to a dynamically defined manager
class built by create_forward_many_to_many_manager() defined below.
234 @property 235 def serializer(self) -> type[BaseSerializer]: 236 from authentik.flows.api.flows import FlowSerializer 237 238 return FlowSerializer
Get serializer for this model
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Inherited Members
The requested object does not exist
The query returned multiple objects when only one was expected.
255class FlowStageBinding(SerializerModel, PolicyBindingModel): 256 """Relationship between Flow and Stage. Order is required and unique for 257 each flow-stage Binding. Additionally, policies can be specified, which determine if 258 this Binding applies to the current user""" 259 260 fsb_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) 261 262 target = models.ForeignKey("Flow", on_delete=models.CASCADE) 263 stage = InheritanceForeignKey(Stage, on_delete=models.CASCADE) 264 265 evaluate_on_plan = models.BooleanField( 266 default=False, 267 help_text=_("Evaluate policies during the Flow planning process."), 268 ) 269 re_evaluate_policies = models.BooleanField( 270 default=True, 271 help_text=_("Evaluate policies when the Stage is presented to the user."), 272 ) 273 274 invalid_response_action = models.TextField( 275 choices=InvalidResponseAction.choices, 276 default=InvalidResponseAction.RETRY, 277 help_text=_( 278 "Configure how the flow executor should handle an invalid response to a " 279 "challenge. RETRY returns the error message and a similar challenge to the " 280 "executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT " 281 "restarts the flow while keeping the current context." 282 ), 283 ) 284 285 order = models.IntegerField() 286 287 objects = InheritanceManager() 288 289 @property 290 def serializer(self) -> type[BaseSerializer]: 291 from authentik.flows.api.bindings import FlowStageBindingSerializer 292 293 return FlowStageBindingSerializer 294 295 def __str__(self) -> str: 296 if not self.target_id and self.stage_id: 297 return f"In-memory Flow-stage binding {self.stage}" 298 return f"Flow-stage binding #{self.order} to {self.target_id}" 299 300 class Meta: 301 ordering = ["target", "order"] 302 303 verbose_name = _("Flow Stage Binding") 304 verbose_name_plural = _("Flow Stage Bindings") 305 unique_together = (("target", "stage", "order"),)
Relationship between Flow and Stage. Order is required and unique for each flow-stage Binding. Additionally, policies can be specified, which determine if this Binding applies to the current user
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
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.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
289 @property 290 def serializer(self) -> type[BaseSerializer]: 291 from authentik.flows.api.bindings import FlowStageBindingSerializer 292 293 return FlowStageBindingSerializer
Get serializer for this model
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
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.
Inherited Members
The requested object does not exist
The query returned multiple objects when only one was expected.
308class ConfigurableStage(models.Model): 309 """Abstract base class for a Stage that can be configured by the enduser. 310 The stage should create a default flow with the configure_stage designation during 311 migration.""" 312 313 configure_flow = models.ForeignKey( 314 Flow, 315 on_delete=models.SET_NULL, 316 null=True, 317 blank=True, 318 help_text=_( 319 "Flow used by an authenticated user to configure this Stage. " 320 "If empty, user will not be able to configure this stage." 321 ), 322 ) 323 324 class Meta: 325 abstract = True
Abstract base class for a Stage that can be configured by the enduser. The stage should create a default flow with the configure_stage designation during migration.
328class FriendlyNamedStage(models.Model): 329 """Abstract base class for a Stage that can have a user friendly name configured.""" 330 331 friendly_name = models.TextField(blank=True) 332 333 class Meta: 334 abstract = True
Abstract base class for a Stage that can have a user friendly name configured.
337class FlowToken(InternallyManagedMixin, Token): 338 """Subclass of a standard Token, stores the currently active flow plan upon creation. 339 Can be used to later resume a flow.""" 340 341 flow = models.ForeignKey(Flow, on_delete=models.CASCADE) 342 _plan = models.TextField() 343 revoke_on_execution = models.BooleanField(default=True) 344 345 @staticmethod 346 def pickle(plan: FlowPlan) -> str: 347 """Pickle into string""" 348 data = dumps(plan) 349 return b64encode(data).decode() 350 351 @property 352 def plan(self) -> FlowPlan: 353 """Load Flow plan from pickled version""" 354 return loads(b64decode(self._plan.encode())) # nosec 355 356 def __str__(self) -> str: 357 return f"Flow Token {super().__str__()}" 358 359 class Meta: 360 verbose_name = _("Flow Token") 361 verbose_name_plural = _("Flow Tokens")
Subclass of a standard Token, stores the currently active flow plan upon creation. Can be used to later resume a 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.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
345 @staticmethod 346 def pickle(plan: FlowPlan) -> str: 347 """Pickle into string""" 348 data = dumps(plan) 349 return b64encode(data).decode()
Pickle into string
351 @property 352 def plan(self) -> FlowPlan: 353 """Load Flow plan from pickled version""" 354 return loads(b64decode(self._plan.encode())) # nosec
Load Flow plan from pickled version
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.
Inherited Members
The requested object does not exist
The query returned multiple objects when only one was expected.