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