def test_set_and_get_with_delete(self) -> None: set_expirable_session_var( self.session, "test_set_and_get_with_delete", "some_value", expiry_seconds=10 ) value = get_expirable_session_var(self.session, "test_set_and_get_with_delete", delete=True) self.assertEqual(value, "some_value") self.assertEqual( get_expirable_session_var(self.session, "test_set_and_get_with_delete"), None )
def test_set_and_get_basic(self) -> None: start_time = timezone_now() with mock.patch('zerver.lib.sessions.timezone_now', return_value=start_time): set_expirable_session_var(self.session, 'test_set_and_get_basic', 'some_value', expiry_seconds=10) value = get_expirable_session_var(self.session, 'test_set_and_get_basic') self.assertEqual(value, 'some_value') with mock.patch('zerver.lib.sessions.timezone_now', return_value=start_time + timedelta(seconds=11)): value = get_expirable_session_var(self.session, 'test_set_and_get_basic') self.assertEqual(value, None)
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 webapp 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 ) if multiuse_object_key: from_multiuse_invite = True multiuse_obj = Confirmation.objects.get(confirmation_key=multiuse_object_key).content_object realm = multiuse_obj.realm invited_as = multiuse_obj.invited_as else: from_multiuse_invite = False multiuse_obj = None 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)
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 webapp 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) if multiuse_object_key: from_multiuse_invite = True multiuse_obj = Confirmation.objects.get( confirmation_key=multiuse_object_key).content_object realm = multiuse_obj.realm invited_as = multiuse_obj.invited_as else: from_multiuse_invite = False multiuse_obj = None 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 = 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() # We want to create a confirmation link to create an account # in the current realm, i.e. one with a hostname of # realm.host. For the Apache REMOTE_USER_SSO auth code path, # this is preferable over realm.get_host() because the latter # contains the port number of the Apache instance and we want # to send the user back to nginx. But if we're in the realm # creation code path, there might not be a realm yet, so we # have to use request.get_host(). if realm is not None: host = realm.host else: host = request.get_host() # Mark 'host' as safe for use in a redirect. It's pulled from the # current request or realm, both of which only allow a limited set of # trusted hosts. confirmation_link = create_confirmation_link( prereg_user, mark_sanitized(host), 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)