module
pretalx.person.domain.user
Functions 6
change_password(user, new_password)sourceSet new_password, clear any pending reset token, send the confirmation mail and log the change.
@transaction.atomic
def change_password(user, new_password):
"""Set ``new_password``, clear any pending reset token, send the
confirmation mail and log the change."""
user.set_password(new_password)
user.pw_reset_token = None
user.pw_reset_time = None
user.save(update_fields=["password", "pw_reset_token", "pw_reset_time"])
send_system_mail(
subject=PASSWORD_CHANGED_SUBJECT,
text=PASSWORD_CHANGED_TEXT,
to=user.email,
locale=user.locale,
context_kwargs={"user": user},
)
user.log_action("pretalx.user.password.update")
create_user(*, email, name='', password=None, **kwargs)sourceSingle entry point for creating User objects.
Extra kwargs are forwarded to the User constructor.
def create_user(*, email, name="", password=None, **kwargs):
"""Single entry point for creating User objects.
Extra kwargs are forwarded to the User constructor.
"""
name = (name or "").strip()
kwargs.setdefault("locale", get_language())
kwargs.setdefault("timezone", timezone.get_current_timezone_name())
user = User(email=email, name=name, **kwargs)
if password is None:
user.set_unusable_password()
else:
user.set_password(password)
user.clean()
user.save()
return user
deactivate_user(user)sourceAnonymise user in place: scramble email, blank personal data, drop pictures and team memberships, delete answers flagged as personal data.
@transaction.atomic
def deactivate_user(user):
"""Anonymise ``user`` in place: scramble email, blank personal data,
drop pictures and team memberships, delete answers flagged as
personal data."""
user.email = f"deleted_user_{get_random_string(12)}@localhost"
while User.objects.filter(email__iexact=user.email).exists():
user.email = f"deleted_user_{get_random_string(12)}@localhost"
user.name = "Deleted User"
user.is_active = False
user.is_superuser = False
user.is_administrator = False
user.locale = "en"
user.timezone = "UTC"
user.pw_reset_token = None
user.pw_reset_time = None
user.email_verification_state = EmailVerificationState.UNVERIFIED
user.pending_email = None
user.pending_email_sent = None
user.set_unusable_password()
user.delete_files()
user.profile_picture = None
user.save()
user.profiles.update(biography="")
for answer in (
Answer.objects.filter(
models.Q(speaker__user=user) | models.Q(submission__speakers__user=user),
question__contains_personal_data=True,
)
.select_related("question", "speaker__event", "submission__event")
.distinct()
):
answer.delete() # iterate to delete answer files too
for team in user.teams.all():
team.members.remove(user)
delete_user_signal.send(None, user=user, db_delete=True)
get_password_reset_url(user, *, event=None, orga=False)sourceURL the password-reset mail points at, namespaced by the orga flag and (optionally) the event slug.
def get_password_reset_url(user, *, event=None, orga=False):
"""URL the password-reset mail points at, namespaced by the orga
flag and (optionally) the event slug."""
if event:
path = "orga:event.auth.recover" if orga else "cfp:event.recover"
kwargs = {"token": user.pw_reset_token, "event": event.slug}
else:
path = "orga:auth.recover"
kwargs = {"token": user.pw_reset_token}
# Returning an :class:`urlman.UrlString` (a ``str`` subclass)
# lets :func:`pretalx.mail.domain.context.get_mail_context` drop the
# result into ``safe_extra_context`` without a separate
# ``mark_safe`` wrap at every call site.
return UrlString(build_absolute_uri(path, kwargs=kwargs))
reset_password(user, *, event=None, orga=False, log_actor=None, mail_text=None)sourceMint a fresh pw_reset_token and queue the recovery mail.
log_actor (orga who triggered the reset on behalf of the user) is logged on the activity entry; orga=True flips the recovery URL onto the orga namespace.
@transaction.atomic
def reset_password(user, *, event=None, orga=False, log_actor=None, mail_text=None):
"""Mint a fresh ``pw_reset_token`` and queue the recovery mail.
``log_actor`` (orga who triggered the reset on behalf of the user)
is logged on the activity entry; ``orga=True`` flips the recovery
URL onto the orga namespace."""
user.pw_reset_token = get_random_string(32)
user.pw_reset_time = now()
user.save(update_fields=["pw_reset_token", "pw_reset_time"])
send_system_mail(
subject=PASSWORD_RESET_SUBJECT,
text=mail_text or PASSWORD_RESET_TEXT,
to=user.email,
event=event,
locale=user.locale,
safe_extra_context={
"url": get_password_reset_url(user, event=event, orga=orga)
},
context_kwargs={"user": user},
)
user.log_action(
action="pretalx.user.password.reset", person=log_actor, orga=bool(log_actor)
)
shred_user(user)sourceDelete user outright.
Refuses if the user still has submissions, answers or team memberships — call deactivate_user in that case.
@transaction.atomic
def shred_user(user):
"""Delete ``user`` outright. Refuses if the user still has
submissions, answers or team memberships — call ``deactivate_user``
in that case."""
with scopes_disabled():
if (
Submission.all_objects.filter(speakers__user=user).exists()
or user.teams.exists()
# We use two separate queries here for performance reasons:
# Q(…) | Q(…) would run an expensive index-less join first
# before cutting it with a LIMIT 1
or Answer.objects.filter(speaker__user=user).exists()
or Answer.objects.filter(submission__speakers__user=user).exists()
):
raise UserDeletionError(
f"Cannot delete user <{user.email}> because they have submissions, answers, or teams. Please deactivate this user instead."
)
user.logged_actions().delete()
actions_by(user).update(person=None)
user.delete_files()
delete_user_signal.send(None, user=user, db_delete=True)
user.delete()