authentik.providers.saml.tasks
SAML Provider tasks
1"""SAML Provider tasks""" 2 3import requests 4from django.contrib.auth import get_user_model 5from dramatiq.actor import actor 6from structlog.stdlib import get_logger 7 8from authentik.events.models import Event, EventAction 9from authentik.providers.saml.models import SAMLProvider 10from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor 11from authentik.providers.saml.processors.logout_request_parser import LogoutRequest 12from authentik.providers.saml.processors.logout_response_processor import LogoutResponseProcessor 13 14LOGGER = get_logger() 15User = get_user_model() 16 17 18@actor(description="Send SAML LogoutRequest to a Service Provider") 19def send_saml_logout_request( 20 provider_pk: int, 21 sls_url: str, 22 name_id: str, 23 name_id_format: str, 24 session_index: str, 25): 26 """Send SAML LogoutRequest to a Service Provider using session data""" 27 provider = SAMLProvider.objects.filter(pk=provider_pk).first() 28 if not provider: 29 LOGGER.error( 30 "Provider not found for SAML logout request", 31 provider_pk=provider_pk, 32 session_index=session_index, 33 ) 34 return False 35 36 LOGGER.debug( 37 "Sending SAML logout request", 38 provider=provider.name, 39 session_index=session_index, 40 ) 41 42 # Note: We don't need the user object for the logout request itself 43 processor = LogoutRequestProcessor( 44 provider=provider, 45 user=None, 46 destination=sls_url, 47 name_id=name_id, 48 name_id_format=name_id_format, 49 session_index=session_index, 50 ) 51 52 return send_post_logout_request(provider, processor) 53 54 55def send_post_logout_request(provider: SAMLProvider, processor: LogoutRequestProcessor) -> bool: 56 """Send LogoutRequest using POST binding""" 57 encoded_request = processor.encode_post() 58 59 form_data = { 60 "SAMLRequest": encoded_request, 61 } 62 63 if processor.relay_state: 64 form_data["RelayState"] = processor.relay_state 65 66 response = requests.post( 67 provider.sls_url, 68 data=form_data, 69 timeout=10, 70 headers={ 71 "Content-Type": "application/x-www-form-urlencoded", 72 }, 73 allow_redirects=True, 74 ) 75 response.raise_for_status() 76 77 LOGGER.debug( 78 "Sent POST logout request", 79 provider=provider, 80 status_code=response.status_code, 81 ) 82 83 return True 84 85 86@actor(description="Send SAML LogoutResponse to a Service Provider (backchannel)") 87def send_saml_logout_response( 88 provider_pk: int, 89 sls_url: str, 90 logout_request_id: str | None = None, 91 relay_state: str | None = None, 92): 93 """Send SAML LogoutResponse to a Service Provider using backchannel (server-to-server)""" 94 provider = SAMLProvider.objects.filter(pk=provider_pk).first() 95 if not provider: 96 LOGGER.error( 97 "Provider not found for SAML logout response", 98 provider_pk=provider_pk, 99 ) 100 return False 101 102 LOGGER.debug( 103 "Sending backchannel SAML logout response", 104 provider=provider.name, 105 sls_url=sls_url, 106 ) 107 108 # Create a minimal LogoutRequest object for the response processor 109 # We only need the ID and relay_state for building the response 110 logout_request = None 111 if logout_request_id: 112 logout_request = LogoutRequest() 113 logout_request.id = logout_request_id 114 logout_request.relay_state = relay_state 115 116 # Build the logout response 117 processor = LogoutResponseProcessor( 118 provider=provider, 119 logout_request=logout_request, 120 destination=sls_url, 121 relay_state=relay_state, 122 ) 123 124 encoded_response = processor.encode_post() 125 126 form_data = { 127 "SAMLResponse": encoded_response, 128 } 129 130 if relay_state: 131 form_data["RelayState"] = relay_state 132 133 # Send the logout response to the SP 134 try: 135 response = requests.post( 136 sls_url, 137 data=form_data, 138 timeout=10, 139 headers={ 140 "Content-Type": "application/x-www-form-urlencoded", 141 }, 142 allow_redirects=True, 143 ) 144 response.raise_for_status() 145 146 LOGGER.info( 147 "Successfully sent backchannel logout response to SP", 148 provider=provider.name, 149 sls_url=sls_url, 150 status_code=response.status_code, 151 ) 152 return True 153 154 except requests.exceptions.RequestException as exc: 155 LOGGER.warning( 156 "Failed to send backchannel logout response to SP", 157 provider=provider.name, 158 sls_url=sls_url, 159 error=str(exc), 160 ) 161 Event.new( 162 EventAction.CONFIGURATION_ERROR, 163 provider=provider, 164 message=f"Backchannel logout response failed: {str(exc)}", 165 ).save() 166 return False
360class User(SerializerModel, AttributesMixin, AbstractUser): 361 """authentik User model, based on django's contrib auth user model.""" 362 363 # Overwriting PermissionsMixin: permissions are handled by roles. 364 # (This knowingly violates the Liskov substitution principle. It is better to fail loudly.) 365 user_permissions = None 366 367 uuid = models.UUIDField(default=uuid4, editable=False, unique=True) 368 name = models.TextField(help_text=_("User's display name.")) 369 path = models.TextField(default="users") 370 type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL) 371 372 sources = models.ManyToManyField("Source", through="UserSourceConnection") 373 groups = models.ManyToManyField("Group", related_name="users") 374 roles = models.ManyToManyField("authentik_rbac.Role", related_name="users", blank=True) 375 password_change_date = models.DateTimeField(auto_now_add=True) 376 377 last_updated = models.DateTimeField(auto_now=True) 378 379 objects = UserManager() 380 381 class Meta: 382 verbose_name = _("User") 383 verbose_name_plural = _("Users") 384 permissions = [ 385 ("reset_user_password", _("Reset Password")), 386 ("impersonate", _("Can impersonate other users")), 387 ("preview_user", _("Can preview user data sent to providers")), 388 ("view_user_applications", _("View applications the user has access to")), 389 ] 390 indexes = [ 391 models.Index(fields=["last_login"]), 392 models.Index(fields=["password_change_date"]), 393 models.Index(fields=["uuid"]), 394 models.Index(fields=["path"]), 395 models.Index(fields=["type"]), 396 models.Index(fields=["date_joined"]), 397 models.Index(fields=["last_updated"]), 398 ] 399 400 def __str__(self): 401 return self.username 402 403 @staticmethod 404 def default_path() -> str: 405 """Get the default user path""" 406 return User._meta.get_field("path").default 407 408 def all_groups(self) -> QuerySet[Group]: 409 """Recursively get all groups this user is a member of.""" 410 return self.groups.all().with_ancestors() 411 412 def all_roles(self) -> QuerySet[Role]: 413 """Get all roles of this user and all of its groups (recursively).""" 414 return Role.objects.filter(Q(users=self) | Q(groups__in=self.all_groups())).distinct() 415 416 def get_managed_role(self, create=False): 417 if create: 418 name = managed_role_name(self) 419 role, created = Role.objects.get_or_create(name=name, managed=name) 420 if created: 421 role.users.add(self) 422 return role 423 else: 424 return Role.objects.filter(name=managed_role_name(self)).first() 425 426 def get_all_model_perms_on_managed_role(self) -> QuerySet[RoleModelPermission]: 427 role = self.get_managed_role() 428 if not role: 429 return RoleModelPermission.objects.none() 430 return RoleModelPermission.objects.filter(role=role) 431 432 def get_all_obj_perms_on_managed_role(self) -> QuerySet[RoleObjectPermission]: 433 role = self.get_managed_role() 434 if not role: 435 return RoleObjectPermission.objects.none() 436 return RoleObjectPermission.objects.filter(role=role) 437 438 def assign_perms_to_managed_role( 439 self, 440 perms: str | list[str] | Permission | list[Permission], 441 obj: models.Model | None = None, 442 ): 443 if not perms: 444 return 445 role = self.get_managed_role(create=True) 446 role.assign_perms(perms, obj) 447 448 def remove_perms_from_managed_role( 449 self, 450 perms: str | list[str] | Permission | list[Permission], 451 obj: models.Model | None = None, 452 ): 453 role = self.get_managed_role() 454 if not role: 455 return None 456 role.remove_perms(perms, obj) 457 458 def remove_all_perms_from_managed_role(self): 459 role = self.get_managed_role() 460 if not role: 461 return None 462 RoleModelPermission.objects.filter(role=role).delete() 463 RoleObjectPermission.objects.filter(role=role).delete() 464 465 def group_attributes(self, request: HttpRequest | None = None) -> dict[str, Any]: 466 """Get a dictionary containing the attributes from all groups the user belongs to, 467 including the users attributes""" 468 final_attributes = {} 469 if request and hasattr(request, "brand"): 470 always_merger.merge(final_attributes, request.brand.attributes) 471 for group in self.all_groups().order_by("name"): 472 always_merger.merge(final_attributes, group.attributes) 473 always_merger.merge(final_attributes, self.attributes) 474 return final_attributes 475 476 def app_entitlements(self, app: Application | None) -> QuerySet[ApplicationEntitlement]: 477 """Get all entitlements this user has for `app`.""" 478 if not app: 479 return [] 480 all_groups = self.all_groups() 481 qs = app.applicationentitlement_set.filter( 482 Q( 483 Q(bindings__user=self) | Q(bindings__group__in=all_groups), 484 bindings__negate=False, 485 ) 486 | Q( 487 Q(~Q(bindings__user=self), bindings__user__isnull=False) 488 | Q(~Q(bindings__group__in=all_groups), bindings__group__isnull=False), 489 bindings__negate=True, 490 ), 491 bindings__enabled=True, 492 ).order_by("name") 493 return qs 494 495 def app_entitlements_attributes(self, app: Application | None) -> dict: 496 """Get a dictionary containing all merged attributes from app entitlements for `app`.""" 497 final_attributes = {} 498 for attrs in self.app_entitlements(app).values_list("attributes", flat=True): 499 always_merger.merge(final_attributes, attrs) 500 return final_attributes 501 502 @property 503 def serializer(self) -> Serializer: 504 from authentik.core.api.users import UserSerializer 505 506 return UserSerializer 507 508 @cached_property 509 def is_superuser(self) -> bool: 510 """Get supseruser status based on membership in a group with superuser status""" 511 return self.all_groups().filter(is_superuser=True).exists() 512 513 @property 514 def is_staff(self) -> bool: 515 """superuser == staff user""" 516 return self.is_superuser # type: ignore 517 518 # TODO: remove this after 2026. 519 @property 520 def ak_groups(self): 521 """This is a proxy for a renamed, deprecated field.""" 522 from authentik.events.models import Event 523 524 deprecation = "authentik.core.models.User.ak_groups" 525 replacement = "authentik.core.models.User.groups" 526 message_logger = ( 527 f"{deprecation} is deprecated and will be removed in a future version of " 528 f"authentik. Please use {replacement} instead." 529 ) 530 message_event = ( 531 f"{message_logger} This event will not be repeated until it expires (by " 532 "default: in 30 days). See authentik logs for every will invocation of this " 533 "deprecation." 534 ) 535 stacktrace = traceback.format_stack() 536 # The last line is this function, the next-to-last line is its caller 537 cause = stacktrace[-2] if len(stacktrace) > 1 else "Unknown, see stacktrace in logs" 538 if search := re.search(r'"(.*?)"', cause): 539 cause = f"Property mapping or Expression policy named {search.group(1)}" 540 541 LOGGER.warning( 542 "deprecation used", 543 message=message_logger, 544 deprecation=deprecation, 545 replacement=replacement, 546 cause=cause, 547 stacktrace=stacktrace, 548 ) 549 Event.log_deprecation( 550 deprecation, message=message_event, cause=cause, replacement=replacement 551 ) 552 return self.groups 553 554 def set_password(self, raw_password, signal=True, sender=None, request=None): 555 if self.pk and signal: 556 from authentik.core.signals import password_changed 557 558 if not sender: 559 sender = self 560 password_changed.send(sender=sender, user=self, password=raw_password, request=request) 561 self.password_change_date = now() 562 return super().set_password(raw_password) 563 564 def check_password(self, raw_password: str) -> bool: 565 """ 566 Return a boolean of whether the raw_password was correct. Handles 567 hashing formats behind the scenes. 568 569 Slightly changed version which doesn't send a signal for such internal hash upgrades 570 """ 571 572 def setter(raw_password): 573 self.set_password(raw_password, signal=False) 574 # Password hash upgrades shouldn't be considered password changes. 575 self._password = None 576 self.save(update_fields=["password"]) 577 578 return check_password(raw_password, self.password, setter) 579 580 @property 581 def uid(self) -> str: 582 """Generate a globally unique UID, based on the user ID and the hashed secret key""" 583 return sha256(f"{self.id}-{get_unique_identifier()}".encode("ascii")).hexdigest() 584 585 def locale(self, request: HttpRequest | None = None) -> str: 586 """Get the locale the user has configured""" 587 if request and hasattr(request, "LANGUAGE_CODE"): 588 return request.LANGUAGE_CODE 589 try: 590 return self.attributes.get("settings", {}).get("locale", "") 591 592 except Exception as exc: # noqa 593 LOGGER.warning("Failed to get default locale", exc=exc) 594 if request: 595 return request.brand.locale 596 return "" 597 598 @property 599 def avatar(self) -> str: 600 """Get avatar, depending on authentik.avatar setting""" 601 return get_avatar(self)
authentik User model, based on django's contrib auth user model.
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.
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 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 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.
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.
403 @staticmethod 404 def default_path() -> str: 405 """Get the default user path""" 406 return User._meta.get_field("path").default
Get the default user path
408 def all_groups(self) -> QuerySet[Group]: 409 """Recursively get all groups this user is a member of.""" 410 return self.groups.all().with_ancestors()
Recursively get all groups this user is a member of.
412 def all_roles(self) -> QuerySet[Role]: 413 """Get all roles of this user and all of its groups (recursively).""" 414 return Role.objects.filter(Q(users=self) | Q(groups__in=self.all_groups())).distinct()
Get all roles of this user and all of its groups (recursively).
416 def get_managed_role(self, create=False): 417 if create: 418 name = managed_role_name(self) 419 role, created = Role.objects.get_or_create(name=name, managed=name) 420 if created: 421 role.users.add(self) 422 return role 423 else: 424 return Role.objects.filter(name=managed_role_name(self)).first()
465 def group_attributes(self, request: HttpRequest | None = None) -> dict[str, Any]: 466 """Get a dictionary containing the attributes from all groups the user belongs to, 467 including the users attributes""" 468 final_attributes = {} 469 if request and hasattr(request, "brand"): 470 always_merger.merge(final_attributes, request.brand.attributes) 471 for group in self.all_groups().order_by("name"): 472 always_merger.merge(final_attributes, group.attributes) 473 always_merger.merge(final_attributes, self.attributes) 474 return final_attributes
Get a dictionary containing the attributes from all groups the user belongs to, including the users attributes
476 def app_entitlements(self, app: Application | None) -> QuerySet[ApplicationEntitlement]: 477 """Get all entitlements this user has for `app`.""" 478 if not app: 479 return [] 480 all_groups = self.all_groups() 481 qs = app.applicationentitlement_set.filter( 482 Q( 483 Q(bindings__user=self) | Q(bindings__group__in=all_groups), 484 bindings__negate=False, 485 ) 486 | Q( 487 Q(~Q(bindings__user=self), bindings__user__isnull=False) 488 | Q(~Q(bindings__group__in=all_groups), bindings__group__isnull=False), 489 bindings__negate=True, 490 ), 491 bindings__enabled=True, 492 ).order_by("name") 493 return qs
Get all entitlements this user has for app.
495 def app_entitlements_attributes(self, app: Application | None) -> dict: 496 """Get a dictionary containing all merged attributes from app entitlements for `app`.""" 497 final_attributes = {} 498 for attrs in self.app_entitlements(app).values_list("attributes", flat=True): 499 always_merger.merge(final_attributes, attrs) 500 return final_attributes
Get a dictionary containing all merged attributes from app entitlements for app.
502 @property 503 def serializer(self) -> Serializer: 504 from authentik.core.api.users import UserSerializer 505 506 return UserSerializer
Get serializer for this model
Get supseruser status based on membership in a group with superuser status
513 @property 514 def is_staff(self) -> bool: 515 """superuser == staff user""" 516 return self.is_superuser # type: ignore
superuser == staff user
519 @property 520 def ak_groups(self): 521 """This is a proxy for a renamed, deprecated field.""" 522 from authentik.events.models import Event 523 524 deprecation = "authentik.core.models.User.ak_groups" 525 replacement = "authentik.core.models.User.groups" 526 message_logger = ( 527 f"{deprecation} is deprecated and will be removed in a future version of " 528 f"authentik. Please use {replacement} instead." 529 ) 530 message_event = ( 531 f"{message_logger} This event will not be repeated until it expires (by " 532 "default: in 30 days). See authentik logs for every will invocation of this " 533 "deprecation." 534 ) 535 stacktrace = traceback.format_stack() 536 # The last line is this function, the next-to-last line is its caller 537 cause = stacktrace[-2] if len(stacktrace) > 1 else "Unknown, see stacktrace in logs" 538 if search := re.search(r'"(.*?)"', cause): 539 cause = f"Property mapping or Expression policy named {search.group(1)}" 540 541 LOGGER.warning( 542 "deprecation used", 543 message=message_logger, 544 deprecation=deprecation, 545 replacement=replacement, 546 cause=cause, 547 stacktrace=stacktrace, 548 ) 549 Event.log_deprecation( 550 deprecation, message=message_event, cause=cause, replacement=replacement 551 ) 552 return self.groups
This is a proxy for a renamed, deprecated field.
554 def set_password(self, raw_password, signal=True, sender=None, request=None): 555 if self.pk and signal: 556 from authentik.core.signals import password_changed 557 558 if not sender: 559 sender = self 560 password_changed.send(sender=sender, user=self, password=raw_password, request=request) 561 self.password_change_date = now() 562 return super().set_password(raw_password)
564 def check_password(self, raw_password: str) -> bool: 565 """ 566 Return a boolean of whether the raw_password was correct. Handles 567 hashing formats behind the scenes. 568 569 Slightly changed version which doesn't send a signal for such internal hash upgrades 570 """ 571 572 def setter(raw_password): 573 self.set_password(raw_password, signal=False) 574 # Password hash upgrades shouldn't be considered password changes. 575 self._password = None 576 self.save(update_fields=["password"]) 577 578 return check_password(raw_password, self.password, setter)
Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes.
Slightly changed version which doesn't send a signal for such internal hash upgrades
580 @property 581 def uid(self) -> str: 582 """Generate a globally unique UID, based on the user ID and the hashed secret key""" 583 return sha256(f"{self.id}-{get_unique_identifier()}".encode("ascii")).hexdigest()
Generate a globally unique UID, based on the user ID and the hashed secret key
585 def locale(self, request: HttpRequest | None = None) -> str: 586 """Get the locale the user has configured""" 587 if request and hasattr(request, "LANGUAGE_CODE"): 588 return request.LANGUAGE_CODE 589 try: 590 return self.attributes.get("settings", {}).get("locale", "") 591 592 except Exception as exc: # noqa 593 LOGGER.warning("Failed to get default locale", exc=exc) 594 if request: 595 return request.brand.locale 596 return ""
Get the locale the user has configured
598 @property 599 def avatar(self) -> str: 600 """Get avatar, depending on authentik.avatar setting""" 601 return get_avatar(self)
Get avatar, depending on authentik.avatar setting
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.
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.
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.
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 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 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 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.
The requested object does not exist
The query returned multiple objects when only one was expected.
Send SAML LogoutRequest to a Service Provider using session data
56def send_post_logout_request(provider: SAMLProvider, processor: LogoutRequestProcessor) -> bool: 57 """Send LogoutRequest using POST binding""" 58 encoded_request = processor.encode_post() 59 60 form_data = { 61 "SAMLRequest": encoded_request, 62 } 63 64 if processor.relay_state: 65 form_data["RelayState"] = processor.relay_state 66 67 response = requests.post( 68 provider.sls_url, 69 data=form_data, 70 timeout=10, 71 headers={ 72 "Content-Type": "application/x-www-form-urlencoded", 73 }, 74 allow_redirects=True, 75 ) 76 response.raise_for_status() 77 78 LOGGER.debug( 79 "Sent POST logout request", 80 provider=provider, 81 status_code=response.status_code, 82 ) 83 84 return True
Send LogoutRequest using POST binding
Send SAML LogoutResponse to a Service Provider using backchannel (server-to-server)