def check_prereg_key_and_redirect(request: HttpRequest, confirmation_key: str) -> HttpResponse: confirmation = Confirmation.objects.filter( confirmation_key=confirmation_key).first() if confirmation is None or confirmation.type not in [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION, ]: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST)) prereg_user = confirmation.content_object if prereg_user.status == confirmation_settings.STATUS_REVOKED: return render(request, "zerver/confirmation_link_expired_error.html") try: get_object_from_key(confirmation_key, confirmation.type, activate_object=False) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) # confirm_preregistrationuser.html just extracts the confirmation_key # (and GET parameters) and redirects to /accounts/register, so that the # user can enter their information on a cleaner URL. return render( request, "confirmation/confirm_preregistrationuser.html", context={ "key": confirmation_key, "full_name": request.GET.get("full_name", None) }, )
def check_prereg_key_and_redirect(request: HttpRequest, confirmation_key: str) -> HttpResponse: # If the key isn't valid, show the error message on the original URL confirmation = Confirmation.objects.filter( confirmation_key=confirmation_key).first() if confirmation is None or confirmation.type not in [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION ]: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST)) try: get_object_from_key(confirmation_key, confirmation.type) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) # confirm_preregistrationuser.html just extracts the confirmation_key # (and GET parameters) and redirects to /accounts/register, so that the # user can enter their information on a cleaner URL. return render(request, 'confirmation/confirm_preregistrationuser.html', context={ 'key': confirmation_key, 'full_name': request.GET.get("full_name", None) })
def confirm(request, confirmation_key): # type: (HttpRequest, str) -> HttpResponse try: get_object_from_key(confirmation_key) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) return render(request, 'confirmation/confirm_preregistrationuser.html', context={ 'key': confirmation_key, 'full_name': request.GET.get("full_name", None)})
def confirm(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: get_object_from_key(confirmation_key, Confirmation.USER_REGISTRATION) except ConfirmationKeyException: try: get_object_from_key(confirmation_key, Confirmation.INVITATION) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) return render(request, 'confirmation/confirm_preregistrationuser.html', context={ 'key': confirmation_key, 'full_name': request.GET.get("full_name", None) })
def confirm_email_change(request, confirmation_key): # type: (HttpRequest, str) -> HttpResponse user_profile = request.user if user_profile.realm.email_changes_disabled: raise JsonableError(_("Email address changes are disabled in this organization.")) confirmation_key = confirmation_key.lower() try: obj = get_object_from_key(confirmation_key) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) assert isinstance(obj, EmailChangeStatus) new_email = obj.new_email old_email = obj.old_email do_change_user_email(obj.user_profile, obj.new_email) context = {'realm': obj.realm, 'new_email': new_email} send_email('zerver/emails/notify_change_in_email', to_email=old_email, from_name="Zulip Account Security", from_address=FromAddress.SUPPORT, context=context) ctx = { 'new_email': new_email, 'old_email': old_email, } return render(request, 'confirmation/confirm_email_change.html', context=ctx)
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: email_change_object = get_object_from_key(confirmation_key, Confirmation.EMAIL_CHANGE) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) new_email = email_change_object.new_email old_email = email_change_object.old_email user_profile = email_change_object.user_profile if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin: raise JsonableError( _("Email address changes are disabled in this organization.")) do_change_user_email(user_profile, new_email) context = {'realm': user_profile.realm, 'new_email': new_email} send_email('zerver/emails/notify_change_in_email', to_email=old_email, from_name="Zulip Account Security", from_address=FromAddress.SUPPORT, context=context) ctx = { 'new_email': new_email, 'old_email': old_email, } return render(request, 'confirmation/confirm_email_change.html', context=ctx)
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: email_change_object = get_object_from_key(confirmation_key, Confirmation.EMAIL_CHANGE) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) new_email = email_change_object.new_email old_email = email_change_object.old_email user_profile = email_change_object.user_profile if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin: raise JsonableError(_("Email address changes are disabled in this organization.")) do_change_user_delivery_email(user_profile, new_email) context = {'realm_name': user_profile.realm.name, 'new_email': new_email} send_email('zerver/emails/notify_change_in_email', to_emails=[old_email], from_name="Zulip Account Security", from_address=FromAddress.SUPPORT, language=user_profile.default_language, context=context) ctx = { 'new_email': new_email, 'old_email': old_email, } return render(request, 'confirmation/confirm_email_change.html', context=ctx)
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: email_change_object = get_object_from_key(confirmation_key, Confirmation.EMAIL_CHANGE) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) new_email = email_change_object.new_email old_email = email_change_object.old_email user_profile = email_change_object.user_profile if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin: raise JsonableError(_("Email address changes are disabled in this organization.")) do_change_user_delivery_email(user_profile, new_email) context = {'realm_name': user_profile.realm.name, 'new_email': new_email} language = user_profile.default_language send_email('zerver/emails/notify_change_in_email', to_emails=[old_email], from_name=FromAddress.security_email_from_name(user_profile=user_profile), from_address=FromAddress.SUPPORT, language=language, context=context, realm=user_profile.realm) ctx = { 'new_email_html_tag': SafeString(f'<a href="mailto:{escape(new_email)}">{escape(new_email)}</a>'), 'old_email_html_tag': SafeString(f'<a href="mailto:{escape(old_email)}">{escape(old_email)}</a>'), } return render(request, 'confirmation/confirm_email_change.html', context=ctx)
def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: realm = get_object_from_key(confirmation_key, Confirmation.REALM_REACTIVATION) except ConfirmationKeyException: return render(request, 'zerver/realm_reactivation_link_error.html') do_reactivate_realm(realm) context = {"realm": realm} return render(request, 'zerver/realm_reactivation.html', context)
def confirm(request, confirmation_key): # type: (HttpRequest, str) -> HttpResponse obj = get_object_from_key(confirmation_key) if obj: return render(request, 'confirmation/confirm_preregistrationuser.html', context={ 'key': confirmation_key, 'full_name': request.GET.get("full_name", None)}) else: return render(request, 'confirmation/confirm.html', context = {'confirmed': False})
def check_prereg_key_and_redirect(request: HttpRequest, confirmation_key: str) -> HttpResponse: # If the key isn't valid, show the error message on the original URL confirmation = Confirmation.objects.filter(confirmation_key=confirmation_key).first() if confirmation is None or confirmation.type not in [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION]: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST)) try: get_object_from_key(confirmation_key, confirmation.type) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) # confirm_preregistrationuser.html just extracts the confirmation_key # (and GET parameters) and redirects to /accounts/register, so that the # user can enter their information on a cleaner URL. return render(request, 'confirmation/confirm_preregistrationuser.html', context={ 'key': confirmation_key, 'full_name': request.GET.get("full_name", None)})
def accounts_home_from_multiuse_invite(request, confirmation_key): # type: (HttpRequest, str) -> HttpResponse multiuse_object = None try: multiuse_object = get_object_from_key(confirmation_key) except ConfirmationKeyException as exception: realm = get_realm_from_request(request) if realm is None or realm.invite_required: return render_confirmation_key_error(request, exception) return accounts_home(request, multiuse_object=multiuse_object)
def process_unsubscribe(request: HttpRequest, confirmation_key: str, subscription_type: str, unsubscribe_function: Callable[[UserProfile], None]) -> HttpResponse: try: user_profile = get_object_from_key(confirmation_key, Confirmation.UNSUBSCRIBE) except ConfirmationKeyException: return render(request, 'zerver/unsubscribe_link_error.html') unsubscribe_function(user_profile) context = common_context(user_profile) context.update({"subscription_type": subscription_type}) return render(request, 'zerver/unsubscribe_success.html', context=context)
def process_unsubscribe(request: HttpRequest, confirmation_key: str, subscription_type: str, unsubscribe_function: Callable[[UserProfile], None]) -> HttpResponse: try: user_profile = get_object_from_key(confirmation_key, Confirmation.UNSUBSCRIBE) except ConfirmationKeyException as exception: return render(request, 'zerver/unsubscribe_link_error.html') unsubscribe_function(user_profile) context = common_context(user_profile) context.update({"subscription_type": subscription_type}) return render(request, 'zerver/unsubscribe_success.html', context=context)
def accounts_home_from_multiuse_invite(request: HttpRequest, confirmation_key: str) -> HttpResponse: multiuse_object = None try: multiuse_object = get_object_from_key(confirmation_key, Confirmation.MULTIUSE_INVITE) # Required for oAuth2 request.session["multiuse_object_key"] = confirmation_key except ConfirmationKeyException as exception: realm = get_realm_from_request(request) if realm is None or realm.invite_required: return render_confirmation_key_error(request, exception) return accounts_home(request, multiuse_object=multiuse_object)
def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: realm = get_object_from_key(confirmation_key, [Confirmation.REALM_REACTIVATION]) except ConfirmationKeyException: return render(request, "zerver/realm_reactivation_link_error.html") assert isinstance(realm, Realm) do_reactivate_realm(realm) context = {"realm": realm} return render(request, "zerver/realm_reactivation.html", context)
def check_prereg_key( request: HttpRequest, confirmation_key: str) -> Union[Confirmation, HttpResponse]: """ Checks if the Confirmation key is valid, returning the Confirmation object in case of success and an appropriate error page otherwise. """ try: confirmation: Optional[Confirmation] = Confirmation.objects.get( confirmation_key=confirmation_key) except Confirmation.DoesNotExist: confirmation = None if confirmation is None or confirmation.type not in [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION, ]: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST)) prereg_user = confirmation.content_object assert prereg_user is not None if prereg_user.status == confirmation_settings.STATUS_REVOKED: return render(request, "zerver/confirmation_link_expired_error.html", status=404) try: get_object_from_key(confirmation_key, confirmation.type, activate_object=False) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) return confirmation
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse: try: email_change_object = get_object_from_key(confirmation_key, [Confirmation.EMAIL_CHANGE]) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) new_email = email_change_object.new_email old_email = email_change_object.old_email user_profile = email_change_object.user_profile if user_profile.realm.deactivated: return redirect_to_deactivation_notice() if not user_profile.is_active: # TODO: Make this into a user-facing error, not JSON raise UserDeactivatedError() if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin: raise JsonableError( _("Email address changes are disabled in this organization.")) do_change_user_delivery_email(user_profile, new_email) context = {"realm_name": user_profile.realm.name, "new_email": new_email} language = user_profile.default_language send_email( "zerver/emails/notify_change_in_email", to_emails=[old_email], from_name=FromAddress.security_email_from_name( user_profile=user_profile), from_address=FromAddress.SUPPORT, language=language, context=context, realm=user_profile.realm, ) ctx = { "new_email_html_tag": SafeString( f'<a href="mailto:{escape(new_email)}">{escape(new_email)}</a>'), "old_email_html_tag": SafeString( f'<a href="mailto:{escape(old_email)}">{escape(old_email)}</a>'), } return render(request, "confirmation/confirm_email_change.html", context=ctx)
def process_unsubscribe( request: HttpRequest, confirmation_key: str, subscription_type: str, unsubscribe_function: Callable[[UserProfile], None], ) -> HttpResponse: try: user_profile = get_object_from_key(confirmation_key, [Confirmation.UNSUBSCRIBE]) except ConfirmationKeyException: return render(request, "zerver/unsubscribe_link_error.html") assert isinstance(user_profile, UserProfile) unsubscribe_function(user_profile) context = common_context(user_profile) context.update(subscription_type=subscription_type) return render(request, "zerver/unsubscribe_success.html", context=context)
def accounts_home_from_multiuse_invite(request: HttpRequest, confirmation_key: str) -> HttpResponse: realm = get_realm_from_request(request) multiuse_object: Optional[MultiuseInvite] = None try: confirmation_obj = get_object_from_key(confirmation_key, [Confirmation.MULTIUSE_INVITE]) assert isinstance(confirmation_obj, MultiuseInvite) multiuse_object = confirmation_obj if realm != multiuse_object.realm: return render(request, "confirmation/link_does_not_exist.html", status=404) # Required for OAuth 2 except ConfirmationKeyException as exception: if realm is None or realm.invite_required: return render_confirmation_key_error(request, exception) return accounts_home( request, multiuse_object_key=confirmation_key, multiuse_object=multiuse_object )
def check_prereg_key(request: HttpRequest, confirmation_key: str) -> PreregistrationUser: """ Checks if the Confirmation key is valid, returning the PreregistrationUser object in case of success and raising an appropriate ConfirmationKeyException otherwise. """ confirmation_types = [ Confirmation.USER_REGISTRATION, Confirmation.INVITATION, Confirmation.REALM_CREATION, ] prereg_user = get_object_from_key(confirmation_key, confirmation_types, activate_object=False) if prereg_user.status == confirmation_settings.STATUS_REVOKED: raise ConfirmationKeyException(ConfirmationKeyException.EXPIRED) return prereg_user
def maybe_send_to_registration( request: HttpRequest, email: str, full_name: str = "", mobile_flow_otp: Optional[str] = None, desktop_flow_otp: Optional[str] = None, is_signup: bool = False, password_required: bool = True, multiuse_object_key: str = "", full_name_validated: bool = False, ) -> HttpResponse: """Given a successful authentication for an email address (i.e. we've confirmed the user controls the email address) that does not currently have a Zulip account in the target realm, send them to the registration flow or the "continue to registration" flow, depending on is_signup, whether the email address can join the organization (checked in HomepageForm), and similar details. """ # In the desktop and mobile registration flows, the sign up # happens in the browser so the user can use their # already-logged-in social accounts. Then at the end, with the # user account created, we pass the appropriate data to the app # via e.g. a `zulip://` redirect. We store the OTP keys for the # mobile/desktop flow in the session with 1-hour expiry, because # we want this configuration of having a successful authentication # result in being logged into the app to persist if the user makes # mistakes while trying to authenticate (E.g. clicks the wrong # Google account, hits back, etc.) during a given browser session, # rather than just logging into the web app in the target browser. # # We can't use our usual pre-account-creation state storage # approach of putting something in PreregistrationUser, because # that would apply to future registration attempts on other # devices, e.g. just creating an account on the web on their laptop. assert not (mobile_flow_otp and desktop_flow_otp) if mobile_flow_otp: set_expirable_session_var(request.session, "registration_mobile_flow_otp", mobile_flow_otp, expiry_seconds=3600) elif desktop_flow_otp: set_expirable_session_var(request.session, "registration_desktop_flow_otp", desktop_flow_otp, expiry_seconds=3600) multiuse_obj: Optional[MultiuseInvite] = None from_multiuse_invite = False if multiuse_object_key: from_multiuse_invite = True try: multiuse_obj = get_object_from_key(multiuse_object_key, Confirmation.MULTIUSE_INVITE) except ConfirmationKeyException: return render(request, "zerver/confirmation_link_expired_error.html", status=404) realm = multiuse_obj.realm invited_as = multiuse_obj.invited_as else: try: realm = get_realm(get_subdomain(request)) except Realm.DoesNotExist: realm = None invited_as = PreregistrationUser.INVITE_AS["MEMBER"] form = HomepageForm({"email": email}, realm=realm, from_multiuse_invite=from_multiuse_invite) if form.is_valid(): # If the email address is allowed to sign up for an account in # this organization, construct a PreregistrationUser and # Confirmation objects, and then send the user to account # creation or confirm-continue-registration depending on # is_signup. try: prereg_user = filter_to_valid_prereg_users( PreregistrationUser.objects.filter( email__iexact=email, realm=realm)).latest("invited_at") # password_required and full_name data passed here as argument should take precedence # over the defaults with which the existing PreregistrationUser that we've just fetched # was created. prereg_user.password_required = password_required update_fields = ["password_required"] if full_name: prereg_user.full_name = full_name prereg_user.full_name_validated = full_name_validated update_fields.extend(["full_name", "full_name_validated"]) prereg_user.save(update_fields=update_fields) except PreregistrationUser.DoesNotExist: prereg_user = create_preregistration_user( email, request, password_required=password_required, full_name=full_name, full_name_validated=full_name_validated, ) if multiuse_obj is not None: request.session.modified = True streams_to_subscribe = list(multiuse_obj.streams.all()) prereg_user.streams.set(streams_to_subscribe) prereg_user.invited_as = invited_as prereg_user.save() confirmation_link = create_confirmation_link( prereg_user, Confirmation.USER_REGISTRATION) if is_signup: return redirect(confirmation_link) context = { "email": email, "continue_link": confirmation_link, "full_name": full_name } return render(request, "zerver/confirm_continue_registration.html", context=context) # This email address it not allowed to join this organization, so # just send the user back to the registration page. url = reverse("register") context = login_context(request) extra_context: Mapping[str, Any] = { "form": form, "current_url": lambda: url, "from_multiuse_invite": from_multiuse_invite, "multiuse_object_key": multiuse_object_key, "mobile_flow_otp": mobile_flow_otp, "desktop_flow_otp": desktop_flow_otp, } context.update(extra_context) return render(request, "zerver/accounts_home.html", context=context)