security: Add tooling to nag users if a Zulip server is very old.

This will help ensure that users upgrade their Zulip server.

Essentially rewritten by tabbott.

Fixes part of #17826.
This commit is contained in:
Nyior 2021-04-24 11:25:20 +00:00 committed by Tim Abbott
parent a2c7f35e5c
commit 8e2042d378
5 changed files with 71 additions and 2 deletions

View File

@ -80,6 +80,8 @@ export function initialize() {
const ls = localstorage();
if (page_params.insecure_desktop_app) {
open($("[data-process='insecure-desktop-app']"));
} else if (page_params.server_needs_upgrade) {
open($("[data-process='server-needs-upgrade']"));
} else if (page_params.warn_no_email === true && page_params.is_admin) {
// if email has not been set up and the user is the admin,
// display a warning to tell them to set up an email server.

View File

@ -45,6 +45,15 @@
</div>
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}" role="button" tabindex=0>&times;</span>
</div>
<div data-process="server-needs-upgrade" class="alert alert-info red">
<div data-step="1">
{{ _("This Zulip server is running an old version and should be upgraded.") }}
<a class="alert-link" href="https://zulip.readthedocs.io/en/latest/production/upgrade-or-modify.html" target="_blank" rel="noopener noreferrer">
{{ _("Learn more.") }}
</a>
</div>
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}" role="button" tabindex=0>&times;</span>
</div>
<div data-process="bankruptcy" class="alert alert-info brankruptcy">
<div data-step="1">
{% trans count=page_params.unread_msgs.count %}

View File

@ -1,11 +1,15 @@
import calendar
import datetime
import os
import time
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
import pytz
from django.conf import settings
from django.http import HttpRequest
from django.utils import translation
from django.utils.timezone import now as timezone_now
from two_factor.utils import default_device
from zerver.lib.events import do_events_register
@ -35,6 +39,33 @@ class UserPermissionInfo:
show_webathena: bool
# LAST_SERVER_UPGRADE_TIME is the last time the server had a version deployed.
if settings.PRODUCTION: # nocoverage
timestamp = os.path.basename(os.path.abspath(settings.DEPLOY_ROOT))
LAST_SERVER_UPGRADE_TIME = datetime.datetime.strptime(timestamp, "%Y-%m-%d-%H-%M-%S").replace(
tzinfo=pytz.utc
)
else:
LAST_SERVER_UPGRADE_TIME = timezone_now()
def is_outdated_server(user_profile: Optional[UserProfile]) -> bool:
# TODO: We should ideally be using the minimum of this calculation
# and the date the release tarball was generated.
tzaware_last_upgrade_time = LAST_SERVER_UPGRADE_TIME
deadline = tzaware_last_upgrade_time + datetime.timedelta(
days=settings.SERVER_UPGRADE_NAG_DEADLINE
)
if user_profile is None or not user_profile.is_realm_admin:
# Administrators get warned at the deadline; all users 30 days later.
deadline = deadline + datetime.timedelta(days=30)
if timezone_now() > deadline:
return True
return False
def get_furthest_read_time(user_profile: Optional[UserProfile]) -> Optional[float]:
if user_profile is None:
return time.time()
@ -174,6 +205,7 @@ def build_page_params_for_home_page_load(
test_suite=settings.TEST_SUITE,
poll_timeout=settings.POLL_TIMEOUT,
insecure_desktop_app=insecure_desktop_app,
server_needs_upgrade=is_outdated_server(user_profile),
login_page=settings.HOME_NOT_LOGGED_IN,
root_domain_uri=settings.ROOT_DOMAIN_URI,
save_stacktraces=settings.SAVE_FRONTEND_STACKTRACES,

View File

@ -5,6 +5,7 @@ from typing import Any
from unittest.mock import patch
import orjson
import pytz
from django.conf import settings
from django.http import HttpResponse
from django.utils.timezone import now as timezone_now
@ -17,10 +18,10 @@ from zerver.lib.actions import (
do_create_user,
)
from zerver.lib.events import add_realm_logo_fields
from zerver.lib.home import get_furthest_read_time
from zerver.lib.home import LAST_SERVER_UPGRADE_TIME, get_furthest_read_time, is_outdated_server
from zerver.lib.soft_deactivation import do_soft_deactivate_users
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import get_user_messages, queries_captured
from zerver.lib.test_helpers import get_user_messages, override_settings, queries_captured
from zerver.lib.users import compute_show_invites_and_add_streams
from zerver.models import (
DefaultStream,
@ -211,6 +212,7 @@ class HomeTest(ZulipTestCase):
"server_inline_image_preview",
"server_inline_url_embed_preview",
"server_name_changes_disabled",
"server_needs_upgrade",
"settings_send_digest_emails",
"starred_message_counts",
"starred_messages",
@ -874,6 +876,27 @@ class HomeTest(ZulipTestCase):
compute_navbar_logo_url(page_params), "/static/images/logo/zulip-org-logo.svg?version=0"
)
@override_settings(SERVER_UPGRADE_NAG_DEADLINE=365)
def test_is_outdated_server(self) -> None:
# Check when server_upgrade_nag_deadline > last_server_upgrade_time
hamlet = self.example_user("hamlet")
iago = self.example_user("iago")
now = LAST_SERVER_UPGRADE_TIME.replace(tzinfo=pytz.utc)
with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=10)):
self.assertEqual(is_outdated_server(iago), False)
self.assertEqual(is_outdated_server(hamlet), False)
self.assertEqual(is_outdated_server(None), False)
with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=397)):
self.assertEqual(is_outdated_server(iago), True)
self.assertEqual(is_outdated_server(hamlet), True)
self.assertEqual(is_outdated_server(None), True)
with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=380)):
self.assertEqual(is_outdated_server(iago), True)
self.assertEqual(is_outdated_server(hamlet), False)
self.assertEqual(is_outdated_server(None), False)
def test_furthest_read_time(self) -> None:
msg_id = self.send_test_message("hello!", sender_name="iago")

View File

@ -441,3 +441,6 @@ NAGIOS_BOT_HOST = EXTERNAL_HOST
# Use half of the available CPUs for data import purposes.
DEFAULT_DATA_EXPORT_IMPORT_PARALLELISM = (len(os.sched_getaffinity(0)) // 2) or 1
# How long after the last upgrade to nag users that the server is insecure
SERVER_UPGRADE_NAG_DEADLINE = 365