class TestJSONEncoder(ZulipTestCase): # Test EncoderForHTML # Taken from # https://github.com/simplejson/simplejson/blob/8edc82afcf6f7512b05fba32baa536fe756bd273/simplejson/tests/test_encode_for_html.py # License: MIT decoder = json.JSONDecoder() encoder = JSONEncoderForHTML() def test_basic_encode(self) -> None: self.assertEqual(r'"\u0026"', self.encoder.encode('&')) self.assertEqual(r'"\u003c"', self.encoder.encode('<')) self.assertEqual(r'"\u003e"', self.encoder.encode('>')) def test_basic_roundtrip(self) -> None: for char in '&<>': self.assertEqual(char, self.decoder.decode(self.encoder.encode(char))) def test_prevent_script_breakout(self) -> None: bad_string = '</script><script>alert("gotcha")</script>' self.assertEqual( r'"\u003c/script\u003e\u003cscript\u003e' r'alert(\"gotcha\")\u003c/script\u003e"', self.encoder.encode(bad_string)) self.assertEqual(bad_string, self.decoder.decode(self.encoder.encode(bad_string)))
def initial_upgrade(request: HttpRequest) -> HttpResponse: if not settings.BILLING_ENABLED: return render(request, "404.html") user = request.user customer = Customer.objects.filter(realm=user.realm).first() if customer is not None and CustomerPlan.objects.filter(customer=customer).exists(): return HttpResponseRedirect(reverse('corporate.views.billing_home')) percent_off = 0 if customer is not None and customer.default_discount is not None: percent_off = customer.default_discount seat_count = get_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context = { 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'email': user.email, 'seat_count': seat_count, 'signed_seat_count': signed_seat_count, 'salt': salt, 'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES), 'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE, 'plan': "Zulip Standard", 'page_params': JSONEncoderForHTML().encode({ 'seat_count': seat_count, 'annual_price': 8000, 'monthly_price': 800, 'percent_off': float(percent_off), }), } # type: Dict[str, Any] response = render(request, 'corporate/upgrade.html', context=context) return response
def render_stats(request: HttpRequest, realm: Realm) -> HttpRequest: page_params = dict( is_staff = request.user.is_staff, stats_realm = realm.string_id, debug_mode = False, ) return render(request, 'analytics/stats.html', context=dict(target_realm_name=realm.name, page_params=JSONEncoderForHTML().encode(page_params)))
def render_stats(request: HttpRequest, data_url_suffix: str, target_name: str, for_installation: bool=False) -> HttpRequest: page_params = dict( data_url_suffix=data_url_suffix, for_installation=for_installation, debug_mode=False, ) return render(request, 'analytics/stats.html', context=dict(target_name=target_name, page_params=JSONEncoderForHTML().encode(page_params)))
def initial_upgrade(request: HttpRequest) -> HttpResponse: if not settings.BILLING_ENABLED: return render(request, "404.html") user = request.user customer = Customer.objects.filter(realm=user.realm).first() if customer is not None and customer.has_billing_relationship: return HttpResponseRedirect(reverse('corporate.views.billing_home')) percent_off = 0 if customer is not None: stripe_customer = stripe_get_customer(customer.stripe_customer_id) if stripe_customer.discount is not None: percent_off = stripe_customer.discount.coupon.percent_off seat_count = get_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context = { 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'email': user.email, 'seat_count': seat_count, 'signed_seat_count': signed_seat_count, 'salt': salt, 'min_seat_count_for_invoice': max(seat_count, MIN_INVOICED_SEAT_COUNT), 'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE, 'plan': "Zulip Standard", 'nickname_monthly': Plan.CLOUD_MONTHLY, 'nickname_annual': Plan.CLOUD_ANNUAL, 'page_params': JSONEncoderForHTML().encode({ 'seat_count': seat_count, 'nickname_annual': Plan.CLOUD_ANNUAL, 'nickname_monthly': Plan.CLOUD_MONTHLY, 'annual_price': 8000, 'monthly_price': 800, 'percent_off': percent_off, }), } # type: Dict[str, Any] response = render(request, 'corporate/upgrade.html', context=context) return response
def home_real(request: HttpRequest) -> HttpResponse: # We need to modify the session object every two weeks or it will expire. # This line makes reloading the page a sufficient action to keep the # session alive. request.session.modified = True user_profile = request.user # If a user hasn't signed the current Terms of Service, send them there if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \ int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version(): return accounts_accept_terms(request) narrow = [] # type: List[List[str]] narrow_stream = None narrow_topic = request.GET.get("topic") if request.GET.get("stream"): try: narrow_stream_name = request.GET.get("stream") (narrow_stream, ignored_rec, ignored_sub) = access_stream_by_name(user_profile, narrow_stream_name) narrow = [["stream", narrow_stream.name]] except Exception: logging.exception("Narrow parsing exception", extra=dict(request=request)) if narrow_stream is not None and narrow_topic is not None: narrow.append(["topic", narrow_topic]) register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, narrow=narrow) user_has_messages = (register_ret['max_message_id'] != -1) # Reset our don't-spam-users-with-email counter since the # user has since logged in if user_profile.last_reminder is not None: # nocoverage # TODO: Look into the history of last_reminder; we may have # eliminated that as a useful concept for non-bot users. user_profile.last_reminder = None user_profile.save(update_fields=["last_reminder"]) # Brand new users get narrowed to PM with welcome-bot needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING first_in_realm = realm_user_count(user_profile.realm) == 1 # If you are the only person in the realm and you didn't invite # anyone, we'll continue to encourage you to do so on the frontend. prompt_for_invites = first_in_realm and \ not PreregistrationUser.objects.filter(referred_by=user_profile).count() if user_profile.pointer == -1 and user_has_messages: # Put the new user's pointer at the bottom # # This improves performance, because we limit backfilling of messages # before the pointer. It's also likely that someone joining an # organization is interested in recent messages more than the very # first messages on the system. register_ret['pointer'] = register_ret['max_message_id'] user_profile.last_pointer_updater = request.session.session_key if user_profile.pointer == -1: latest_read = None else: latest_read = get_usermessage_by_message_id(user_profile, user_profile.pointer) if latest_read is None: # Don't completely fail if your saved pointer ID is invalid logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer)) # We pick a language for the user as follows: # * First priority is the language in the URL, for debugging. # * If not in the URL, we use the language from the user's settings. request_language = translation.get_language_from_path(request.path_info) if request_language is None: request_language = register_ret['default_language'] translation.activate(request_language) # We also save the language to the user's session, so that # something reasonable will happen in logged-in portico pages. request.session[ translation.LANGUAGE_SESSION_KEY] = translation.get_language() two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED # Pass parameters to the client-side JavaScript code. # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. development_environment=settings.DEVELOPMENT, debug_mode=settings.DEBUG, test_suite=settings.TEST_SUITE, poll_timeout=settings.POLL_TIMEOUT, login_page=settings.HOME_NOT_LOGGED_IN, root_domain_uri=settings.ROOT_DOMAIN_URI, maxfilesize=settings.MAX_FILE_UPLOAD_SIZE, max_avatar_file_size=settings.MAX_AVATAR_FILE_SIZE, server_generation=settings.SERVER_GENERATION, use_websockets=settings.USE_WEBSOCKETS, save_stacktraces=settings.SAVE_FRONTEND_STACKTRACES, warn_no_email=settings.WARN_NO_EMAIL, server_inline_image_preview=settings.INLINE_IMAGE_PREVIEW, server_inline_url_embed_preview=settings.INLINE_URL_EMBED_PREVIEW, password_min_length=settings.PASSWORD_MIN_LENGTH, password_min_guesses=settings.PASSWORD_MIN_GUESSES, jitsi_server_url=settings.JITSI_SERVER_URL, search_pills_enabled=settings.SEARCH_PILLS_ENABLED, # Misc. extra data. have_initial_messages=user_has_messages, initial_servertime=time.time( ), # Used for calculating relative presence age default_language_name=get_language_name( register_ret['default_language']), language_list_dbl_col=get_language_list_for_templates( register_ret['default_language']), language_list=get_language_list(), needs_tutorial=needs_tutorial, first_in_realm=first_in_realm, prompt_for_invites=prompt_for_invites, furthest_read_time=sent_time_in_epoch_seconds(latest_read), has_mobile_devices=num_push_devices_for_user(user_profile) > 0, bot_types=get_bot_types(user_profile), two_fa_enabled=two_fa_enabled, # Adding two_fa_enabled as condition saves us 3 queries when # 2FA is not enabled. two_fa_enabled_user=two_fa_enabled and bool(default_device(user_profile)), ) undesired_register_ret_fields = [ 'streams', ] for field_name in set( register_ret.keys()) - set(undesired_register_ret_fields): page_params[field_name] = register_ret[field_name] if narrow_stream is not None: # In narrow_stream context, initial pointer is just latest message recipient = get_stream_recipient(narrow_stream.id) try: initial_pointer = Message.objects.filter( recipient=recipient).order_by('id').reverse()[0].id except IndexError: initial_pointer = -1 page_params["narrow_stream"] = narrow_stream.name if narrow_topic is not None: page_params["narrow_topic"] = narrow_topic page_params["narrow"] = [ dict(operator=term[0], operand=term[1]) for term in narrow ] page_params["max_message_id"] = initial_pointer page_params["pointer"] = initial_pointer page_params["have_initial_messages"] = (initial_pointer != -1) page_params["enable_desktop_notifications"] = False statsd.incr('views.home') show_invites = True # Some realms only allow admins to invite users if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: show_invites = False if user_profile.is_guest: show_invites = False show_billing = False show_plans = False if settings.CORPORATE_ENABLED: from corporate.models import Customer if user_profile.is_billing_admin or user_profile.is_realm_admin: customer = Customer.objects.filter( realm=user_profile.realm).first() if customer is not None and customer.has_billing_relationship: show_billing = True if user_profile.realm.plan_type == Realm.LIMITED: show_plans = True request._log_data['extra'] = "[%s]" % (register_ret["queue_id"], ) page_params['translation_data'] = {} if request_language != 'en': page_params['translation_data'] = get_language_translation_data( request_language) csp_nonce = generate_random_token(48) emojiset = user_profile.emojiset if emojiset == UserProfile.TEXT_EMOJISET: # If current emojiset is `TEXT_EMOJISET`, then fallback to # GOOGLE_EMOJISET for picking which spritesheet's CSS to # include (and thus how to display emojis in the emoji picker # and composebox typeahead). emojiset = UserProfile.GOOGLE_BLOB_EMOJISET response = render( request, 'zerver/app/index.html', context={ 'user_profile': user_profile, 'emojiset': emojiset, 'page_params': JSONEncoderForHTML().encode(page_params), 'csp_nonce': csp_nonce, 'avatar_url': avatar_url(user_profile), 'show_debug': settings.DEBUG and ('show_debug' in request.GET), 'pipeline': settings.PIPELINE_ENABLED, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_billing': show_billing, 'show_plans': show_plans, 'is_admin': user_profile.is_realm_admin, 'is_guest': user_profile.is_guest, 'show_webathena': user_profile.realm.webathena_enabled, 'enable_feedback': settings.ENABLE_FEEDBACK, 'embedded': narrow_stream is not None, }, ) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) return response
def home_real(request: HttpRequest) -> HttpResponse: # We need to modify the session object every two weeks or it will expire. # This line makes reloading the page a sufficient action to keep the # session alive. request.session.modified = True user_profile = request.user # If a user hasn't signed the current Terms of Service, send them there if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \ int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version(): return accounts_accept_terms(request) narrow = [] # type: List[List[Text]] narrow_stream = None narrow_topic = request.GET.get("topic") if request.GET.get("stream"): try: narrow_stream_name = request.GET.get("stream") (narrow_stream, ignored_rec, ignored_sub) = access_stream_by_name( user_profile, narrow_stream_name) narrow = [["stream", narrow_stream.name]] except Exception: logging.exception("Narrow parsing") if narrow_stream is not None and narrow_topic is not None: narrow.append(["topic", narrow_topic]) register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, narrow=narrow) user_has_messages = (register_ret['max_message_id'] != -1) # Reset our don't-spam-users-with-email counter since the # user has since logged in if user_profile.last_reminder is not None: user_profile.last_reminder = None user_profile.save(update_fields=["last_reminder"]) # Brand new users get narrowed to PM with welcome-bot needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING first_in_realm = realm_user_count(user_profile.realm) == 1 # If you are the only person in the realm and you didn't invite # anyone, we'll continue to encourage you to do so on the frontend. prompt_for_invites = first_in_realm and \ not PreregistrationUser.objects.filter(referred_by=user_profile).count() if user_profile.pointer == -1 and user_has_messages: # Put the new user's pointer at the bottom # # This improves performance, because we limit backfilling of messages # before the pointer. It's also likely that someone joining an # organization is interested in recent messages more than the very # first messages on the system. register_ret['pointer'] = register_ret['max_message_id'] user_profile.last_pointer_updater = request.session.session_key if user_profile.pointer == -1: latest_read = None else: try: latest_read = UserMessage.objects.get(user_profile=user_profile, message__id=user_profile.pointer) except UserMessage.DoesNotExist: # Don't completely fail if your saved pointer ID is invalid logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer)) latest_read = None # Set default language and make it persist default_language = register_ret['default_language'] url_lang = '/{}'.format(request.LANGUAGE_CODE) if not request.path.startswith(url_lang): translation.activate(default_language) request.session[translation.LANGUAGE_SESSION_KEY] = translation.get_language() # Pass parameters to the client-side JavaScript code. # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. development_environment = settings.DEVELOPMENT, debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, poll_timeout = settings.POLL_TIMEOUT, login_page = settings.HOME_NOT_LOGGED_IN, root_domain_uri = settings.ROOT_DOMAIN_URI, maxfilesize = settings.MAX_FILE_UPLOAD_SIZE, max_avatar_file_size = settings.MAX_AVATAR_FILE_SIZE, server_generation = settings.SERVER_GENERATION, use_websockets = settings.USE_WEBSOCKETS, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, server_inline_image_preview = settings.INLINE_IMAGE_PREVIEW, server_inline_url_embed_preview = settings.INLINE_URL_EMBED_PREVIEW, password_min_length = settings.PASSWORD_MIN_LENGTH, password_min_guesses = settings.PASSWORD_MIN_GUESSES, # Misc. extra data. have_initial_messages = user_has_messages, initial_servertime = time.time(), # Used for calculating relative presence age default_language_name = get_language_name(register_ret['default_language']), language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']), language_list = get_language_list(), needs_tutorial = needs_tutorial, first_in_realm = first_in_realm, prompt_for_invites = prompt_for_invites, furthest_read_time = sent_time_in_epoch_seconds(latest_read), has_mobile_devices = num_push_devices_for_user(user_profile) > 0, bot_types = get_bot_types(), ) undesired_register_ret_fields = [ 'streams', ] for field_name in set(register_ret.keys()) - set(undesired_register_ret_fields): page_params[field_name] = register_ret[field_name] if narrow_stream is not None: # In narrow_stream context, initial pointer is just latest message recipient = get_stream_recipient(narrow_stream.id) try: initial_pointer = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id except IndexError: initial_pointer = -1 page_params["narrow_stream"] = narrow_stream.name if narrow_topic is not None: page_params["narrow_topic"] = narrow_topic page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] page_params["max_message_id"] = initial_pointer page_params["pointer"] = initial_pointer page_params["have_initial_messages"] = (initial_pointer != -1) page_params["enable_desktop_notifications"] = False statsd.incr('views.home') show_invites = True # Some realms only allow admins to invite users if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: show_invites = False request._log_data['extra'] = "[%s]" % (register_ret["queue_id"],) response = render(request, 'zerver/index.html', context={'user_profile': user_profile, 'page_params': JSONEncoderForHTML().encode(page_params), 'nofontface': is_buggy_ua(request.META.get("HTTP_USER_AGENT", "Unspecified")), 'avatar_url': avatar_url(user_profile), 'show_debug': settings.DEBUG and ('show_debug' in request.GET), 'pipeline': settings.PIPELINE_ENABLED, 'show_invites': show_invites, 'is_admin': user_profile.is_realm_admin, 'show_webathena': user_profile.realm.webathena_enabled, 'enable_feedback': settings.ENABLE_FEEDBACK, 'embedded': narrow_stream is not None, },) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) return response
def initial_upgrade(request: HttpRequest) -> HttpResponse: if not settings.BILLING_ENABLED: return render(request, "404.html") user = request.user error_message = "" error_description = "" # only used in tests customer = Customer.objects.filter(realm=user.realm).first() if customer is not None and customer.has_billing_relationship: return HttpResponseRedirect(reverse('corporate.views.billing_home')) percent_off = 0 if customer is not None: stripe_customer = stripe_get_customer(customer.stripe_customer_id) if stripe_customer.discount is not None: percent_off = stripe_customer.discount.coupon.percent_off if request.method == 'POST': try: plan, seat_count = unsign_and_check_upgrade_parameters( user, request.POST['plan'], request.POST['signed_seat_count'], request.POST['salt'], request.POST['billing_modality']) if request.POST['billing_modality'] == 'send_invoice': try: invoiced_seat_count = int(request.POST['invoiced_seat_count']) except (KeyError, ValueError): invoiced_seat_count = -1 min_required_seat_count = max(seat_count, MIN_INVOICED_SEAT_COUNT) if invoiced_seat_count < min_required_seat_count: raise BillingError( 'lowball seat count', "You must invoice for at least %d users." % (min_required_seat_count,)) seat_count = invoiced_seat_count process_initial_upgrade(user, plan, seat_count, request.POST.get('stripe_token', None)) except BillingError as e: error_message = e.message error_description = e.description except Exception as e: billing_logger.exception("Uncaught exception in billing: %s" % (e,)) error_message = BillingError.CONTACT_SUPPORT error_description = "uncaught exception during upgrade" else: return HttpResponseRedirect(reverse('corporate.views.billing_home')) seat_count = get_seat_count(user.realm) signed_seat_count, salt = sign_string(str(seat_count)) context = { 'publishable_key': STRIPE_PUBLISHABLE_KEY, 'email': user.email, 'seat_count': seat_count, 'signed_seat_count': signed_seat_count, 'salt': salt, 'min_seat_count_for_invoice': max(seat_count, MIN_INVOICED_SEAT_COUNT), 'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE, 'plan': "Zulip Standard", 'nickname_monthly': Plan.CLOUD_MONTHLY, 'nickname_annual': Plan.CLOUD_ANNUAL, 'error_message': error_message, 'page_params': JSONEncoderForHTML().encode({ 'seat_count': seat_count, 'nickname_annual': Plan.CLOUD_ANNUAL, 'nickname_monthly': Plan.CLOUD_MONTHLY, 'annual_price': 8000, 'monthly_price': 800, 'percent_off': percent_off, }), } # type: Dict[str, Any] response = render(request, 'corporate/upgrade.html', context=context) response['error_description'] = error_description return response