Esempio n. 1
0
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)))
Esempio n. 2
0
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
Esempio n. 3
0
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)))
Esempio n. 4
0
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)))
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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