def generate_secrets(development=False): # type: (bool) -> None if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" current_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) lines = [] # type: List[str] if len(current_conf) == 0: lines = ['[secrets]\n'] def need_secret(name): # type: (str) -> bool return name not in current_conf def add_secret(name, value): # type: (str, str) -> None lines.append("%s = %s\n" % (name, value)) current_conf[name] = value for name in AUTOGENERATED_SETTINGS: if need_secret(name): add_secret(name, generate_random_token(64)) if development and need_secret("initial_password_salt"): add_secret("initial_password_salt", generate_random_token(64)) if development and need_secret("local_database_password"): add_secret("local_database_password", generate_random_token(64)) if need_secret('secret_key'): add_secret('secret_key', generate_django_secretkey()) if need_secret('camo_key'): add_secret('camo_key', get_random_string(64)) # zulip_org_key is generated using os.urandom(). # zulip_org_id does not require a secure CPRNG, # it only needs to be unique. if need_secret('zulip_org_key'): add_secret('zulip_org_key', get_random_string(64)) if need_secret('zulip_org_id'): add_secret('zulip_org_id', str(uuid.uuid4())) if not development: # Write the Camo config file directly generate_camo_config_file(current_conf['camo_key']) if len(lines) == 0: print("generate_secrets: No new secrets to generate.") return with open(OUTPUT_SETTINGS_FILENAME, 'a') as f: # Write a newline at the start, in case there was no newline at # the end of the file due to human editing. f.write("\n" + "".join(lines)) print("Generated new secrets in %s." % (OUTPUT_SETTINGS_FILENAME, ))
def process_billing_log_entry(processor: BillingProcessor, log_row: RealmAuditLog) -> None: processor.state = BillingProcessor.STARTED processor.log_row = log_row processor.save() customer = Customer.objects.get(realm=log_row.realm) timestamp = datetime_to_timestamp(log_row.event_time) idempotency_key = 'process_billing_log_entry:%s' % (log_row.id, ) if settings.TEST_SUITE: idempotency_key += '+' + generate_random_token(10) extra_args = {} # type: Dict[str, Any] if log_row.extra_data is not None: extra_args = ujson.loads(log_row.extra_data) processing_functions = { RealmAuditLog.STRIPE_PLAN_QUANTITY_RESET: do_set_subscription_quantity, RealmAuditLog.USER_CREATED: increment_subscription_quantity, RealmAuditLog.USER_ACTIVATED: increment_subscription_quantity, RealmAuditLog.USER_DEACTIVATED: decrement_subscription_quantity, RealmAuditLog.USER_REACTIVATED: increment_subscription_quantity, } # type: Dict[str, Callable[..., None]] processing_functions[log_row.event_type](customer, timestamp, idempotency_key, **extra_args) processor.state = BillingProcessor.DONE processor.save()
def create_missed_message_address(user_profile, message): # type: (UserProfile, Message) -> text_type if settings.EMAIL_GATEWAY_PATTERN == '': logging.warning("EMAIL_GATEWAY_PATTERN is an empty string, using " "NOREPLY_EMAIL_ADDRESS in the 'from' field.") return settings.NOREPLY_EMAIL_ADDRESS if message.recipient.type == Recipient.PERSONAL: # We need to reply to the sender so look up their personal recipient_id recipient_id = get_recipient(Recipient.PERSONAL, message.sender_id).id else: recipient_id = message.recipient_id data = { 'user_profile_id': user_profile.id, 'recipient_id': recipient_id, 'subject': message.subject, } while True: token = generate_random_token(32) key = missed_message_redis_key(token) if redis_client.hsetnx(key, 'uses_left', 1): break with redis_client.pipeline() as pipeline: pipeline.hmset(key, data) pipeline.expire(key, 60 * 60 * 24 * 5) pipeline.execute() address = u'mm' + token return settings.EMAIL_GATEWAY_PATTERN % (address, )
def upload_export_tarball(self, realm: Optional[Realm], tarball_path: str) -> str: def percent_callback(bytes_transferred: Any) -> None: sys.stdout.write('.') sys.stdout.flush() session = boto3.Session(settings.S3_KEY, settings.S3_SECRET_KEY) # We use the avatar bucket, because it's world-readable. bucket = get_bucket(session, settings.S3_AVATAR_BUCKET) key = bucket.Object( os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path))) key.upload_file(tarball_path, Callback=percent_callback) session = botocore.session.get_session() config = Config(signature_version=botocore.UNSIGNED) public_url = session.create_client( 's3', config=config).generate_presigned_url('get_object', Params={ 'Bucket': bucket.name, 'Key': key.key }, ExpiresIn=0) return public_url
def upload_export_tarball( self, realm: Optional[Realm], tarball_path: str, percent_callback: Optional[Callable[[Any], None]] = None) -> str: # We use the avatar bucket, because it's world-readable. key = self.avatar_bucket.Object( os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path))) key.upload_file(tarball_path, Callback=percent_callback) session = botocore.session.get_session() config = Config(signature_version=botocore.UNSIGNED) public_url = session.create_client( 's3', config=config).generate_presigned_url( 'get_object', Params={ 'Bucket': self.avatar_bucket.name, 'Key': key.key, }, ExpiresIn=0, ) return public_url
def create_missed_message_address(user_profile, message): # type: (UserProfile, Message) -> text_type if message.recipient.type == Recipient.PERSONAL: # We need to reply to the sender so look up their personal recipient_id recipient_id = get_recipient(Recipient.PERSONAL, message.sender_id).id else: recipient_id = message.recipient_id data = { 'user_profile_id': user_profile.id, 'recipient_id': recipient_id, 'subject': message.subject, } while True: token = generate_random_token(32) key = missed_message_redis_key(token) if redis_client.hsetnx(key, 'uses_left', 1): break with redis_client.pipeline() as pipeline: pipeline.hmset(key, data) pipeline.expire(key, 60 * 60 * 24 * 5) pipeline.execute() address = u'mm' + token return settings.EMAIL_GATEWAY_PATTERN % (address,)
def create_missed_message_address(user_profile: UserProfile, message: Message) -> str: if settings.EMAIL_GATEWAY_PATTERN == '': logger.warning("EMAIL_GATEWAY_PATTERN is an empty string, using " "NOREPLY_EMAIL_ADDRESS in the 'from' field.") return FromAddress.NOREPLY if message.recipient.type == Recipient.PERSONAL: # We need to reply to the sender so look up their personal recipient_id recipient_id = get_personal_recipient(message.sender_id).id else: recipient_id = message.recipient_id data = { 'user_profile_id': user_profile.id, 'recipient_id': recipient_id, 'subject': message.topic_name().encode('utf-8'), } while True: token = generate_random_token(32) key = missed_message_redis_key(token) if redis_client.hsetnx(key, 'uses_left', 1): break with redis_client.pipeline() as pipeline: pipeline.hmset(key, data) pipeline.expire(key, 60 * 60 * 24 * 5) pipeline.execute() address = 'mm' + token return settings.EMAIL_GATEWAY_PATTERN % (address,)
def generate_secrets(development=False): if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" lines = ["[secrets]\n"] def config_line(var, value): return "%s = %s\n" % (var, value) for name in AUTOGENERATED_SETTINGS: lines.append(config_line(name, generate_random_token(64))) lines.append(config_line("secret_key", generate_django_secretkey())) camo_key = get_random_string(64) lines.append(config_line("camo_key", camo_key)) if not development: # Write the Camo config file directly generate_camo_config_file(camo_key) out = open(OUTPUT_SETTINGS_FILENAME, "w") out.write("".join(lines)) out.close() print("Generated %s with auto-generated secrets!" % (OUTPUT_SETTINGS_FILENAME,))
def generate_secrets(development=False): # type: (bool) -> None if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" lines = [u'[secrets]\n'] def config_line(var, value): # type: (text_type, text_type) -> text_type return "%s = %s\n" % (var, value) old_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) for name in AUTOGENERATED_SETTINGS: lines.append(config_line(name, old_conf.get(name, generate_random_token(64)))) secret_key = old_conf.get('secret_key', generate_django_secretkey()) lines.append(config_line('secret_key', secret_key)) camo_key = old_conf.get('camo_key', get_random_string(64)) lines.append(config_line('camo_key', camo_key)) if not development: # Write the Camo config file directly generate_camo_config_file(camo_key) out = open(OUTPUT_SETTINGS_FILENAME, 'w') out.write(force_str("".join(lines))) out.close() print("Generated %s with auto-generated secrets!" % (OUTPUT_SETTINGS_FILENAME,))
def create_missed_message_address(user_profile, message): # type: (UserProfile, Message) -> text_type if settings.EMAIL_GATEWAY_PATTERN == "": logging.warning("EMAIL_GATEWAY_PATTERN is an empty string, using " "NOREPLY_EMAIL_ADDRESS in the 'from' field.") return settings.NOREPLY_EMAIL_ADDRESS if message.recipient.type == Recipient.PERSONAL: # We need to reply to the sender so look up their personal recipient_id recipient_id = get_recipient(Recipient.PERSONAL, message.sender_id).id else: recipient_id = message.recipient_id data = {"user_profile_id": user_profile.id, "recipient_id": recipient_id, "subject": message.subject} while True: token = generate_random_token(32) key = missed_message_redis_key(token) if redis_client.hsetnx(key, "uses_left", 1): break with redis_client.pipeline() as pipeline: pipeline.hmset(key, data) pipeline.expire(key, 60 * 60 * 24 * 5) pipeline.execute() address = u"mm" + token return settings.EMAIL_GATEWAY_PATTERN % (address,)
def create_missed_message_address(user_profile, message): if message.recipient.type == Recipient.PERSONAL: # We need to reply to the sender so look up their personal recipient_id recipient_id = get_recipient(Recipient.PERSONAL, message.sender_id).id else: recipient_id = message.recipient_id data = { 'user_profile_id': user_profile.id, 'recipient_id': recipient_id, 'subject': message.subject, } while True: token = generate_random_token(32) key = missed_message_redis_key(token) if redis_client.hsetnx(key, 'uses_left', 1): break with redis_client.pipeline() as pipeline: pipeline.hmset(key, data) pipeline.expire(key, 60 * 60 * 24 * 5) pipeline.execute() address = 'mm' + token return settings.EMAIL_GATEWAY_PATTERN % (address, )
def generate_secrets(development=False): # type: (bool) -> None if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" lines = [u'[secrets]\n'] def config_line(var, value): # type: (text_type, text_type) -> text_type return "%s = %s\n" % (var, value) old_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) for name in AUTOGENERATED_SETTINGS: lines.append( config_line(name, old_conf.get(name, generate_random_token(64)))) secret_key = old_conf.get('secret_key', generate_django_secretkey()) lines.append(config_line('secret_key', secret_key)) camo_key = old_conf.get('camo_key', get_random_string(64)) lines.append(config_line('camo_key', camo_key)) if not development: # Write the Camo config file directly generate_camo_config_file(camo_key) out = open(OUTPUT_SETTINGS_FILENAME, 'w') out.write(force_str("".join(lines))) out.close() print("Generated %s with auto-generated secrets!" % (OUTPUT_SETTINGS_FILENAME, ))
def random_token() -> str: # We do in-function imports so that we only do the expensive work # of importing cryptography modules when necessary. # # This helps optimize noop provision performance. from zerver.lib.utils import generate_random_token return generate_random_token(64)
def handle(self, *args: Any, **options: Any) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser output_dir = options["output_dir"] if output_dir is None: output_dir = tempfile.mkdtemp(prefix="zulip-export-") else: output_dir = os.path.realpath(os.path.expanduser(output_dir)) if os.path.exists(output_dir): shutil.rmtree(output_dir) os.makedirs(output_dir) print("Exporting realm %s" % (realm.string_id, )) num_threads = int(options['threads']) if num_threads < 1: raise CommandError('You must have at least one thread.') do_export_realm(realm, output_dir, threads=num_threads, public_only=options["public_only"]) print("Finished exporting to %s; tarring" % (output_dir, )) do_write_stats_file_for_realm_export(output_dir) tarball_path = output_dir.rstrip('/') + '.tar.gz' os.chdir(os.path.dirname(output_dir)) subprocess.check_call( ["tar", "-czf", tarball_path, os.path.basename(output_dir)]) print("Tarball written to %s" % (tarball_path, )) if not options["upload_to_s3"]: return def percent_callback(complete: Any, total: Any) -> None: sys.stdout.write('.') sys.stdout.flush() if settings.LOCAL_UPLOADS_DIR is not None: raise CommandError("S3 backend must be configured to upload to S3") print("Uploading export tarball to S3") from zerver.lib.upload import S3Connection, get_bucket, Key conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY) # We use the avatar bucket, because it's world-readable. bucket = get_bucket(conn, settings.S3_AVATAR_BUCKET) key = Key(bucket) key.key = os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path)) key.set_contents_from_filename(tarball_path, cb=percent_callback, num_cb=40) public_url = 'https://{bucket}.{host}/{key}'.format( host=conn.server_name(), bucket=bucket.name, key=key.key) print("Uploaded to %s" % (public_url, ))
def generate_secrets(development=False): # type: (bool) -> None if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" current_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) lines = [] # type: List[Text] if len(current_conf) == 0: lines = [u'[secrets]\n'] def need_secret(name): # type: (str) -> bool return name not in current_conf def add_secret(name, value): # type: (str, Text) -> None lines.append("%s = %s\n" % (name, value)) current_conf[name] = value for name in AUTOGENERATED_SETTINGS: if need_secret(name): add_secret(name, generate_random_token(64)) if need_secret('secret_key'): add_secret('secret_key', generate_django_secretkey()) if need_secret('camo_key'): add_secret('camo_key', get_random_string(64)) # zulip_org_key is generated using os.urandom(). # zulip_org_id does not require a secure CPRNG, # it only needs to be unique. if need_secret('zulip_org_key'): add_secret('zulip_org_key', get_random_string(64)) if need_secret('zulip_org_id'): add_secret('zulip_org_id', str(uuid.uuid4())) if not development: # Write the Camo config file directly generate_camo_config_file(current_conf['camo_key']) if len(lines) == 0: print("generate_secrets: No new secrets to generate.") return out = open(OUTPUT_SETTINGS_FILENAME, 'a') # Write a newline at the start, in case there was no newline at # the end of the file due to human editing. out.write("\n" + force_str("".join(lines))) out.close() print("Generated new secrets in %s." % (OUTPUT_SETTINGS_FILENAME,))
def handle(self, *args: Any, **options: Any) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser output_dir = options["output_dir"] if output_dir is None: output_dir = tempfile.mkdtemp(prefix="zulip-export-") else: output_dir = os.path.realpath(os.path.expanduser(output_dir)) if os.path.exists(output_dir): shutil.rmtree(output_dir) os.makedirs(output_dir) print("Exporting realm %s" % (realm.string_id,)) num_threads = int(options['threads']) if num_threads < 1: raise CommandError('You must have at least one thread.') do_export_realm(realm, output_dir, threads=num_threads, public_only=options["public_only"]) print("Finished exporting to %s; tarring" % (output_dir,)) do_write_stats_file_for_realm_export(output_dir) tarball_path = output_dir.rstrip('/') + '.tar.gz' os.chdir(os.path.dirname(output_dir)) subprocess.check_call(["tar", "-czf", tarball_path, os.path.basename(output_dir)]) print("Tarball written to %s" % (tarball_path,)) if not options["upload_to_s3"]: return def percent_callback(complete: Any, total: Any) -> None: sys.stdout.write('.') sys.stdout.flush() if settings.LOCAL_UPLOADS_DIR is not None: raise CommandError("S3 backend must be configured to upload to S3") print("Uploading export tarball to S3") from zerver.lib.upload import S3Connection, get_bucket, Key conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY) # We use the avatar bucket, because it's world-readable. bucket = get_bucket(conn, settings.S3_AVATAR_BUCKET) key = Key(bucket) key.key = os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path)) key.set_contents_from_filename(tarball_path, cb=percent_callback, num_cb=40) public_url = 'https://{bucket}.{host}/{key}'.format( host=conn.server_name(), bucket=bucket.name, key=key.key) print("Uploaded to %s" % (public_url,))
def generate_secrets(development=False): # type: (bool) -> None if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" current_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) lines = [] # type: List[Text] if len(current_conf) == 0: lines = [u'[secrets]\n'] def need_secret(name): # type: (str) -> bool return name not in current_conf def add_secret(name, value): # type: (str, Text) -> None lines.append("%s = %s\n" % (name, value)) current_conf[name] = value for name in AUTOGENERATED_SETTINGS: if need_secret(name): add_secret(name, generate_random_token(64)) if need_secret('secret_key'): add_secret('secret_key', generate_django_secretkey()) if need_secret('camo_key'): add_secret('camo_key', get_random_string(64)) if need_secret('zulip_org_key'): add_secret('zulip_org_key', get_random_string(64)) if need_secret('zulip_org_id'): add_secret('zulip_org_id', str(uuid.uuid4())) if not development: # Write the Camo config file directly generate_camo_config_file(current_conf['camo_key']) if len(lines) == 0: print("generate_secrets: No new secrets to generate.") return out = open(OUTPUT_SETTINGS_FILENAME, 'a') # Write a newline at the start, in case there was no newline at # the end of the file due to human editing. out.write("\n" + force_str("".join(lines))) out.close() print("Generated new secrets in %s." % (OUTPUT_SETTINGS_FILENAME, ))
def __init__(self, user, tokens, alert=None, badge=None, sound=None, category=None, **kwargs): self.frame = Frame() self.tokens = tokens expiry = time.time() + 24 * 3600 priority = 10 payload = Payload(alert=alert, badge=badge, sound=sound, category=category, custom=kwargs) for token in tokens: data = {'token': token, 'user': user} identifier = generate_random_token(32) key = get_apns_key(identifier) redis_client.hmset(key, data) redis_client.expire(key, expiry) self.frame.add_item(token, payload, identifier, expiry, priority)
def upload_export_tarball(self, realm: Optional[Realm], tarball_path: str) -> str: def percent_callback(complete: Any, total: Any) -> None: sys.stdout.write('.') sys.stdout.flush() conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY) # We use the avatar bucket, because it's world-readable. bucket = get_bucket(conn, settings.S3_AVATAR_BUCKET) key = Key(bucket) key.key = os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path)) key.set_contents_from_filename(tarball_path, cb=percent_callback, num_cb=40) public_url = 'https://{bucket}.{host}/{key}'.format( host=conn.server_name(), bucket=bucket.name, key=key.key) return public_url
def put_dict_in_redis(redis_client: redis.StrictRedis, key_format: str, data_to_store: Dict[str, Any], expiration_seconds: int, token_length: int = 64) -> str: key_length = len(key_format) - len('{token}') + token_length if key_length > MAX_KEY_LENGTH: error_msg = "Requested key too long in put_dict_in_redis. Key format: %s, token length: %s" raise ZulipRedisKeyTooLongError(error_msg % (key_format, token_length)) token = generate_random_token(token_length) key = key_format.format(token=token) with redis_client.pipeline() as pipeline: pipeline.set(key, ujson.dumps(data_to_store)) pipeline.expire(key, expiration_seconds) pipeline.execute() return key
def sign_string(string: str) -> Tuple[str, str]: salt = generate_random_token(64) signer = Signer(salt=salt) return signer.sign(string), salt
def generate_missed_message_token() -> str: return 'mm' + generate_random_token(32)
def generate_client_id(): # type: () -> Text return generate_random_token(32)
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, 'invite_as': PreregistrationUser.INVITE_AS, },) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) return response
def generate_email_token_for_stream(): return generate_random_token(32)
def generate_key(): # type: () -> Text return generate_random_token(40)
def home_real(request: HttpRequest) -> HttpResponse: # Before we do any real work, check if the app is banned. client_user_agent = request.META.get("HTTP_USER_AGENT", "") (insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app( client_user_agent) if banned_desktop_app: return render( request, 'zerver/insecure_desktop_app.html', context={ "auto_update_broken": auto_update_broken, }, ) (unsupported_browser, browser_name) = is_unsupported_browser(client_user_agent) if unsupported_browser: return render( request, 'zerver/unsupported_browser.html', context={ "browser_name": browser_name, }, ) # 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 if request.user.is_authenticated: user_profile = request.user else: # nocoverage # This code path should not be reachable because of zulip_login_required above. user_profile = None # If a user hasn't signed the current Terms of Service, send them there if need_accept_tos(user_profile): return accounts_accept_terms(request) narrow, narrow_stream, narrow_topic = detect_narrowed_window(request, user_profile) client_capabilities = { 'notification_settings_null': True, 'bulk_message_deletion': True, 'user_avatar_url_field_optional': True, } register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, slim_presence=True, client_capabilities=client_capabilities, narrow=narrow) update_last_reminder(user_profile) if user_profile is not None: 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() ) needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING else: # nocoverage first_in_realm = False prompt_for_invites = False # The current tutorial doesn't super make sense for logged-out users. needs_tutorial = False furthest_read_time = get_furthest_read_time(user_profile) # 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 and user_profile is not None # Pass parameters to the client-side JavaScript code. # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, poll_timeout = settings.POLL_TIMEOUT, insecure_desktop_app = insecure_desktop_app, login_page = settings.HOME_NOT_LOGGED_IN, root_domain_uri = settings.ROOT_DOMAIN_URI, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, warn_no_email = settings.WARN_NO_EMAIL, search_pills_enabled = settings.SEARCH_PILLS_ENABLED, # Misc. extra data. 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 = furthest_read_time, has_mobile_devices = user_profile is not None and 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 = narrow_stream.recipient try: max_message_id = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id except IndexError: max_message_id = -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"] = max_message_id page_params["enable_desktop_notifications"] = False statsd.incr('views.home') show_invites, show_add_streams = compute_show_invites_and_add_streams(user_profile) show_billing = False show_plans = False if settings.CORPORATE_ENABLED and user_profile is not None: from corporate.models import CustomerPlan, get_customer_by_realm if user_profile.has_billing_access: customer = get_customer_by_realm(user_profile.realm) if customer is not None: if customer.sponsorship_pending: show_billing = True elif CustomerPlan.objects.filter(customer=customer).exists(): show_billing = True if user_profile.realm.plan_type == Realm.LIMITED: show_plans = True request._log_data['extra'] = "[{}]".format(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) if user_profile is not None: color_scheme = user_profile.color_scheme is_guest = user_profile.is_guest is_realm_owner = user_profile.is_realm_owner is_realm_admin = user_profile.is_realm_admin show_webathena = user_profile.realm.webathena_enabled else: # nocoverage color_scheme = UserProfile.COLOR_SCHEME_AUTOMATIC is_guest = False is_realm_admin = False is_realm_owner = False show_webathena = False navbar_logo_url = compute_navbar_logo_url(page_params) # print("----------------->") # new_realm_users = [] # user_initial = user_profile.full_name[0] # pattern = '^{}'.format(user_initial) # for _ in range(len(page_params['realm_users'])): # current_user_fullname = page_params['realm_users'][_]['full_name'] # initials_matched_user = re.match(pattern,current_user_fullname) # if initials_matched_user: # new_realm_users.append(page_params['realm_users'][_]) # page_params['realm_users'] = new_realm_users # print(page_params['realm_users']) # print('\n') # print(page_params.keys()) # print("-----------------> UserProfile objects") # print(UserProfile.objects.all()) response = render(request, 'zerver/app/index.html', context={'user_profile': user_profile, 'page_params': page_params, 'csp_nonce': csp_nonce, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_add_streams': show_add_streams, 'show_billing': show_billing, 'corporate_enabled': settings.CORPORATE_ENABLED, 'show_plans': show_plans, 'is_owner': is_realm_owner, 'is_admin': is_realm_admin, 'is_guest': is_guest, 'color_scheme': color_scheme, 'navbar_logo_url': navbar_logo_url, 'show_webathena': show_webathena, 'embedded': narrow_stream is not None, 'invite_as': PreregistrationUser.INVITE_AS, 'max_file_upload_size_mib': settings.MAX_FILE_UPLOAD_SIZE, }) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) # print("########################### | These are page_params keys | ##############") # print(page_params.keys()) # print("########################### | Realm Users | #############################") # print(page_params['realm_users']) # print("############################ | Presences |############################") # print("\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/") # print(page_params['presences']) return response
remove_ratelimit_rule, RateLimitedObject, RateLimitedUser, RateLimiterBackend, RedisRateLimiterBackend, ) from zerver.lib.test_classes import ZulipTestCase from zerver.lib.utils import generate_random_token from typing import Dict, List, Tuple, Type import mock import time RANDOM_KEY_PREFIX = generate_random_token(32) class RateLimitedTestObject(RateLimitedObject): def __init__(self, name: str, rules: List[Tuple[int, int]], backend: Type[RateLimiterBackend]) -> None: self.name = name self._rules = rules self._rules.sort(key=lambda x: x[0]) super().__init__(backend) def key(self) -> str: return RANDOM_KEY_PREFIX + self.name def rules(self) -> List[Tuple[int, int]]: return self._rules
def generate_secrets(development: bool = False) -> None: if development: OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf" else: OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf" current_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME) lines = [] # type: List[str] if len(current_conf) == 0: lines = ['[secrets]\n'] def need_secret(name: str) -> bool: return name not in current_conf def add_secret(name: str, value: str) -> None: lines.append("%s = %s\n" % (name, value)) current_conf[name] = value for name in AUTOGENERATED_SETTINGS: if need_secret(name): add_secret(name, generate_random_token(64)) if development and need_secret("initial_password_salt"): add_secret("initial_password_salt", generate_random_token(64)) if development and need_secret("local_database_password"): add_secret("local_database_password", generate_random_token(64)) if need_secret('secret_key'): secret_key = generate_django_secretkey() add_secret('secret_key', secret_key) # To prevent Django ImproperlyConfigured error settings.SECRET_KEY = secret_key if need_secret('camo_key'): add_secret('camo_key', get_random_string(64)) if (not development and settings.MEMCACHED_LOCATION == "127.0.0.1:11211" and need_secret("memcached_password")): add_secret("memcached_password", generate_random_token(64)) if (not development and settings.REDIS_HOST == "127.0.0.1" and need_secret("redis_password")): # To prevent Puppet from restarting Redis, which would lose # data because we configured Redis to disable persistence, set # the Redis password on the running server and edit the config # file directly. import redis from zerver.lib.redis_utils import get_redis_client redis_password = generate_random_token(64) for filename in [ "/etc/redis/zuli-redis.conf", "/etc/redis/zulip-redis.conf" ]: if os.path.exists(filename): with open(filename, "a") as f: f.write( "# Set a Redis password based on zulip-secrets.conf\n" "requirepass '%s'\n" % (redis_password, )) break try: get_redis_client().config_set("requirepass", redis_password) except redis.exceptions.ConnectionError: pass add_secret("redis_password", redis_password) # zulip_org_key is generated using os.urandom(). # zulip_org_id does not require a secure CPRNG, # it only needs to be unique. if need_secret('zulip_org_key'): add_secret('zulip_org_key', get_random_string(64)) if need_secret('zulip_org_id'): add_secret('zulip_org_id', str(uuid.uuid4())) if len(lines) == 0: print("generate_secrets: No new secrets to generate.") return with open(OUTPUT_SETTINGS_FILENAME, 'a') as f: # Write a newline at the start, in case there was no newline at # the end of the file due to human editing. f.write("\n" + "".join(lines)) print("Generated new secrets in %s." % (OUTPUT_SETTINGS_FILENAME, ))
def generate_key(): return generate_random_token(40)
def generate_key(): # type: () -> text_type return generate_random_token(40)
def generate_client_id() -> str: return generate_random_token(32)
def home_real(request: HttpRequest) -> HttpResponse: # Before we do any real work, check if the app is banned. client_user_agent = request.META.get("HTTP_USER_AGENT", "") (insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app(client_user_agent) if banned_desktop_app: return render( request, 'zerver/insecure_desktop_app.html', context={ "auto_update_broken": auto_update_broken, }, ) (unsupported_browser, browser_name) = is_unsupported_browser(client_user_agent) if unsupported_browser: return render( request, 'zerver/unsupported_browser.html', context={ "browser_name": browser_name, }, ) # 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 if request.user.is_authenticated: user_profile = request.user else: # nocoverage # This code path should not be reachable because of zulip_login_required above. user_profile = None update_last_reminder(user_profile) statsd.incr('views.home') # If a user hasn't signed the current Terms of Service, send them there if need_accept_tos(user_profile): return accounts_accept_terms(request) narrow, narrow_stream, narrow_topic = detect_narrowed_window( request, user_profile) if user_profile is not None: 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()) needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING else: # nocoverage first_in_realm = False prompt_for_invites = False # The current tutorial doesn't super make sense for logged-out users. needs_tutorial = False has_mobile_devices = user_profile is not None and num_push_devices_for_user( user_profile) > 0 queue_id, page_params = build_page_params_for_home_page_load( request=request, user_profile=user_profile, insecure_desktop_app=insecure_desktop_app, has_mobile_devices=has_mobile_devices, narrow=narrow, narrow_stream=narrow_stream, narrow_topic=narrow_topic, first_in_realm=first_in_realm, prompt_for_invites=prompt_for_invites, needs_tutorial=needs_tutorial, ) show_invites, show_add_streams = compute_show_invites_and_add_streams( user_profile) billing_info = get_billing_info(user_profile) request._log_data['extra'] = "[{}]".format(queue_id) csp_nonce = generate_random_token(48) user_permission_info = get_user_permission_info(user_profile) navbar_logo_url = compute_navbar_logo_url(page_params) response = render(request, 'zerver/app/index.html', context={ 'user_profile': user_profile, 'page_params': page_params, 'csp_nonce': csp_nonce, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_add_streams': show_add_streams, 'show_billing': billing_info.show_billing, 'corporate_enabled': settings.CORPORATE_ENABLED, 'show_plans': billing_info.show_plans, 'is_owner': user_permission_info.is_realm_owner, 'is_admin': user_permission_info.is_realm_admin, 'is_guest': user_permission_info.is_guest, 'color_scheme': user_permission_info.color_scheme, 'navbar_logo_url': navbar_logo_url, 'show_webathena': user_permission_info.show_webathena, 'embedded': narrow_stream is not None, 'invite_as': PreregistrationUser.INVITE_AS, 'max_file_upload_size_mib': settings.MAX_FILE_UPLOAD_SIZE, }) 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[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: # Before we do any real work, check if the app is banned. (insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app( request.META.get("HTTP_USER_AGENT", "")) if banned_desktop_app: return render(request, 'zerver/insecure_desktop_app.html', context={ "auto_update_broken": auto_update_broken, }) # 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 if request.user.is_authenticated: user_profile = request.user else: # nocoverage # This code path should not be reachable because of zulip_login_required above. user_profile = None # If a user hasn't signed the current Terms of Service, send them there if need_accept_tos(user_profile): return accounts_accept_terms(request) narrow, narrow_stream, narrow_topic = detect_narrowed_window( request, user_profile) register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, slim_presence=True, notification_settings_null=True, narrow=narrow) user_has_messages = (register_ret['max_message_id'] != -1) update_last_reminder(user_profile) if user_profile is not None: 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()) needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING else: # nocoverage first_in_realm = False prompt_for_invites = False # The current tutorial doesn't super make sense for logged-out users. needs_tutorial = False if user_profile is None: # nocoverage furthest_read_time = time.time() # type: Optional[float] elif user_profile.pointer == -1: if 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 furthest_read_time = 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("User %s has invalid pointer %s" % (user_profile.id, user_profile.pointer)) furthest_read_time = sent_time_in_epoch_seconds(latest_read) # 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 and user_profile is not None # 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, insecure_desktop_app=insecure_desktop_app, login_page=settings.HOME_NOT_LOGGED_IN, root_domain_uri=settings.ROOT_DOMAIN_URI, max_file_upload_size=settings.MAX_FILE_UPLOAD_SIZE, max_avatar_file_size=settings.MAX_AVATAR_FILE_SIZE, server_generation=settings.SERVER_GENERATION, 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, server_avatar_changes_disabled=settings.AVATAR_CHANGES_DISABLED, server_name_changes_disabled=settings.NAME_CHANGES_DISABLED, # 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=furthest_read_time, has_mobile_devices=user_profile is not None and 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 = narrow_stream.recipient 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, show_add_streams = compute_show_invites_and_add_streams( user_profile) show_billing = False show_plans = False if settings.CORPORATE_ENABLED and user_profile is not None: from corporate.models import Customer, CustomerPlan 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 CustomerPlan.objects.filter( customer=customer).exists(): 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) if user_profile is not None: night_mode = user_profile.night_mode is_guest = user_profile.is_guest is_realm_admin = user_profile.is_realm_admin show_webathena = user_profile.realm.webathena_enabled else: # nocoverage night_mode = False is_guest = False is_realm_admin = False show_webathena = False navbar_logo_url = compute_navbar_logo_url(page_params) response = render( request, 'zerver/app/index.html', context={ 'user_profile': user_profile, 'page_params': page_params, 'csp_nonce': csp_nonce, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_add_streams': show_add_streams, 'show_billing': show_billing, 'show_plans': show_plans, 'is_admin': is_realm_admin, 'is_guest': is_guest, 'night_mode': night_mode, 'navbar_logo_url': navbar_logo_url, 'show_webathena': show_webathena, 'embedded': narrow_stream is not None, 'invite_as': PreregistrationUser.INVITE_AS, 'max_file_upload_size': settings.MAX_FILE_UPLOAD_SIZE, }, ) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) return response