def test_confirm_email_change(self) -> None: user_profile = self.example_user("hamlet") do_set_realm_property( user_profile.realm, "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_EVERYONE, acting_user=None, ) old_email = user_profile.delivery_email new_email = "*****@*****.**" new_realm = get_realm("zulip") self.login("hamlet") obj = EmailChangeStatus.objects.create( new_email=new_email, old_email=old_email, user_profile=user_profile, realm=user_profile.realm, ) url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE) response = self.client_get(url) self.assertEqual(response.status_code, 200) self.assert_in_success_response( ["This confirms that the email address for your Zulip"], response) user_profile = get_user_by_delivery_email(new_email, new_realm) self.assertTrue(bool(user_profile)) obj.refresh_from_db() self.assertEqual(obj.status, 1)
def test_unauthorized_email_change_from_email_confirmation_link( self) -> None: data = {"email": "*****@*****.**"} user_profile = self.example_user("hamlet") self.login_user(user_profile) url = "/json/settings" self.assert_length(mail.outbox, 0) result = self.client_patch(url, data) self.assert_length(mail.outbox, 1) self.assert_json_success(result) email_message = mail.outbox[0] self.assertEqual( email_message.subject, "Verify your new email address", ) body = email_message.body self.assertIn("We received a request to change the email", body) do_set_realm_property( user_profile.realm, "email_changes_disabled", True, acting_user=None, ) activation_url = [s for s in body.split("\n") if s][2] response = self.client_get(activation_url) self.assertEqual(response.status_code, 400) self.assert_in_response( "Email address changes are disabled in this organization.", response)
def test_delivery_email_presence_for_admins(self) -> None: user_profile = self.example_user("iago") self.assertTrue(user_profile.is_realm_admin) do_set_realm_property( user_profile.realm, "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_EVERYONE, acting_user=None, ) result = fetch_initial_state_data(user_profile) for key, value in result["raw_users"].items(): if key == user_profile.id: self.assertEqual(value["delivery_email"], user_profile.delivery_email) else: self.assertNotIn("delivery_email", value) do_set_realm_property( user_profile.realm, "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS, acting_user=None, ) result = fetch_initial_state_data(user_profile) for key, value in result["raw_users"].items(): self.assertIn("delivery_email", value)
def do_remove_realm_domain(realm_domain: RealmDomain, *, acting_user: Optional[UserProfile]) -> None: realm = realm_domain.realm domain = realm_domain.domain realm_domain.delete() removed_domain = RealmDomainDict( domain=realm_domain.domain, allow_subdomains=realm_domain.allow_subdomains, ) RealmAuditLog.objects.create( realm=realm, acting_user=acting_user, event_type=RealmAuditLog.REALM_DOMAIN_REMOVED, event_time=timezone_now(), extra_data=orjson.dumps({ "realm_domains": get_realm_domains(realm), "removed_domain": removed_domain, }).decode(), ) if RealmDomain.objects.filter( realm=realm).count() == 0 and realm.emails_restricted_to_domains: # If this was the last realm domain, we mark the realm as no # longer restricted to domain, because the feature doesn't do # anything if there are no domains, and this is probably less # confusing than the alternative. do_set_realm_property(realm, "emails_restricted_to_domains", False, acting_user=acting_user) event = dict(type="realm_domains", op="remove", domain=domain) transaction.on_commit( lambda: send_event(realm, event, active_user_ids(realm.id)))
def reset_emails_in_zulip_realm() -> None: realm = get_realm("zulip") do_set_realm_property( realm, "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_EVERYONE, acting_user=None, )
def test_logged_out_home(self) -> None: realm = get_realm("zulip") do_set_realm_property(realm, "enable_spectator_access", False, acting_user=None) # Redirect to login if spectator access is disabled. result = self.client_get("/") self.assertEqual(result.status_code, 302) self.assertEqual(result["Location"], "/login/") # Load webapp directly if spectator access is enabled. do_set_realm_property(realm, "enable_spectator_access", True, acting_user=None) result = self.client_get("/") self.assertEqual(result.status_code, 200) # Check no unnecessary params are passed to spectators. page_params = self._get_page_params(result) self.assertEqual(page_params["is_spectator"], True) actual_keys = sorted(str(k) for k in page_params.keys()) expected_keys = [ "apps_page_url", "bot_types", "corporate_enabled", "development_environment", "first_in_realm", "furthest_read_time", "insecure_desktop_app", "is_spectator", "language_cookie_name", "language_list", "login_page", "needs_tutorial", "no_event_queue", "promote_sponsoring_zulip", "prompt_for_invites", "queue_id", "realm_rendered_description", "request_language", "search_pills_enabled", "show_billing", "show_plans", "show_webathena", "test_suite", "translation_data", "two_fa_enabled", "two_fa_enabled_user", "warn_no_email", "webpack_public_path", ] self.assertEqual(actual_keys, expected_keys)
def call_enqueue_emails(realm: Realm) -> int: do_set_realm_property(realm, "digest_emails_enabled", True, acting_user=None) do_set_realm_property(realm, "digest_weekday", timezone_now().weekday(), acting_user=None) cutoff = timezone_now() - datetime.timedelta(days=0) with mock.patch( "zerver.worker.queue_processors.bulk_handle_digest_email" ) as queue_mock: enqueue_emails(cutoff) return 0 if queue_mock.call_args is None else len( queue_mock.call_args[0][0])
def test_promote_new_full_members(self) -> None: realm = get_realm("zulip") cordelia = self.example_user("cordelia") hamlet = self.example_user("hamlet") cordelia.date_joined = timezone_now() - timedelta(days=11) cordelia.save() hamlet.date_joined = timezone_now() - timedelta(days=8) hamlet.save() do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None) full_members_group = UserGroup.objects.get( realm=realm, name="@role:fullmembers", is_system_group=True ) self.assertTrue( UserGroupMembership.objects.filter( user_profile=cordelia, user_group=full_members_group ).exists() ) self.assertFalse( UserGroupMembership.objects.filter( user_profile=hamlet, user_group=full_members_group ).exists() ) current_time = timezone_now() with mock.patch( "zerver.actions.user_groups.timezone_now", return_value=current_time + timedelta(days=3) ): promote_new_full_members() self.assertTrue( UserGroupMembership.objects.filter( user_profile=cordelia, user_group=full_members_group ).exists() ) self.assertTrue( UserGroupMembership.objects.filter( user_profile=hamlet, user_group=full_members_group ).exists() )
def test_delete_all_user_sessions(self) -> None: self.do_test_session( self.example_user("hamlet"), lambda: delete_all_user_sessions(), get_realm("zulip"), True, ) lear_realm = get_realm("lear") do_set_realm_property(lear_realm, "enable_spectator_access", True, acting_user=None) self.make_stream( "web_public_stream", realm=lear_realm, is_web_public=True, ) self.do_test_session( self.lear_user("cordelia"), lambda: delete_all_user_sessions(), lear_realm, True, )
def test_unauthorized_email_change(self) -> None: data = {"email": "*****@*****.**"} user_profile = self.example_user("hamlet") self.login_user(user_profile) do_set_realm_property( user_profile.realm, "email_changes_disabled", True, acting_user=None, ) url = "/json/settings" result = self.client_patch(url, data) self.assert_length(mail.outbox, 0) self.assertEqual(result.status_code, 400) self.assert_in_response( "Email address changes are disabled in this organization.", result) # Realm admins can change their email address even setting is disabled. data = {"email": "*****@*****.**"} self.login("iago") url = "/json/settings" result = self.client_patch(url, data) self.assert_json_success(result)
def test_change_delivery_email_end_to_end_with_admins_visibility( self) -> None: user_profile = self.example_user("hamlet") do_set_realm_property( user_profile.realm, "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS, acting_user=None, ) self.login_user(user_profile) old_email = user_profile.delivery_email new_email = "*****@*****.**" obj = EmailChangeStatus.objects.create( new_email=new_email, old_email=old_email, user_profile=user_profile, realm=user_profile.realm, ) url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE) response = self.client_get(url) self.assertEqual(response.status_code, 200) self.assert_in_success_response( ["This confirms that the email address for your Zulip"], response) user_profile = get_user_profile_by_id(user_profile.id) self.assertEqual(user_profile.delivery_email, new_email) self.assertEqual(user_profile.email, f"user{user_profile.id}@zulip.testserver") obj.refresh_from_db() self.assertEqual(obj.status, 1) with self.assertRaises(UserProfile.DoesNotExist): get_user(old_email, user_profile.realm) with self.assertRaises(UserProfile.DoesNotExist): get_user_by_delivery_email(old_email, user_profile.realm) self.assertEqual( get_user_by_delivery_email(new_email, user_profile.realm), user_profile)
def test_get_realms_and_streams_for_archiving(self) -> None: zulip_realm = get_realm("zulip") zulip_realm.message_retention_days = 10 zulip_realm.save() verona = get_stream("Verona", zulip_realm) verona.message_retention_days = -1 # Block archiving for this stream verona.save() denmark = get_stream("Denmark", zulip_realm) denmark.message_retention_days = 1 denmark.save() zephyr_realm = get_realm("zephyr") zephyr_realm.message_retention_days = -1 zephyr_realm.save() self.make_stream("normal stream", realm=zephyr_realm) archiving_blocked_zephyr_stream = self.make_stream("no archiving", realm=zephyr_realm) archiving_blocked_zephyr_stream.message_retention_days = -1 archiving_blocked_zephyr_stream.save() archiving_enabled_zephyr_stream = self.make_stream("with archiving", realm=zephyr_realm) archiving_enabled_zephyr_stream.message_retention_days = 1 archiving_enabled_zephyr_stream.save() no_archiving_realm = do_create_realm(string_id="no_archiving", name="no_archiving") do_set_realm_property(no_archiving_realm, "invite_required", False, acting_user=None) do_set_realm_property(no_archiving_realm, "message_retention_days", -1, acting_user=None) # Realm for testing the edge case where it has a default retention policy, # but all streams disable it. realm_all_streams_archiving_disabled = do_create_realm( string_id="with_archiving", name="with_archiving") do_set_realm_property(realm_all_streams_archiving_disabled, "invite_required", False, acting_user=None) do_set_realm_property(realm_all_streams_archiving_disabled, "message_retention_days", 1, acting_user=None) Stream.objects.filter( realm=realm_all_streams_archiving_disabled).update( message_retention_days=-1) # We construct a list representing how the result of get_realms_and_streams_for_archiving should be. # One nuisance is that the ordering of the elements in the result structure is not deterministic, # so we use a helper to order both structures in a consistent manner. This wouldn't be necessary # if python had a true "unordered list" data structure. Set doesn't do the job, because it requires # elements to be hashable. expected_result = [ (zulip_realm, list( Stream.objects.filter(realm=zulip_realm).exclude( id=verona.id))), (zephyr_realm, [archiving_enabled_zephyr_stream]), (realm_all_streams_archiving_disabled, []), ] self.fix_ordering_of_result(expected_result) simple_algorithm_result = self.simple_get_realms_and_streams_for_archiving( ) self.fix_ordering_of_result(simple_algorithm_result) result = get_realms_and_streams_for_archiving() self.fix_ordering_of_result(result) self.assert_length(result, len(expected_result)) self.assertEqual(result, expected_result) self.assert_length(result, len(simple_algorithm_result)) self.assertEqual(result, simple_algorithm_result)
def update_realm( request: HttpRequest, user_profile: UserProfile, name: Optional[str] = REQ(str_validator=check_capped_string( Realm.MAX_REALM_NAME_LENGTH), default=None), description: Optional[str] = REQ(str_validator=check_capped_string( Realm.MAX_REALM_DESCRIPTION_LENGTH), default=None), emails_restricted_to_domains: Optional[bool] = REQ( json_validator=check_bool, default=None), disallow_disposable_email_addresses: Optional[bool] = REQ( json_validator=check_bool, default=None), invite_required: Optional[bool] = REQ(json_validator=check_bool, default=None), invite_to_realm_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.INVITE_TO_REALM_POLICY_TYPES), default=None), name_changes_disabled: Optional[bool] = REQ(json_validator=check_bool, default=None), email_changes_disabled: Optional[bool] = REQ(json_validator=check_bool, default=None), avatar_changes_disabled: Optional[bool] = REQ(json_validator=check_bool, default=None), inline_image_preview: Optional[bool] = REQ(json_validator=check_bool, default=None), inline_url_embed_preview: Optional[bool] = REQ(json_validator=check_bool, default=None), add_custom_emoji_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.COMMON_POLICY_TYPES), default=None), delete_own_message_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.COMMON_MESSAGE_POLICY_TYPES), default=None), message_content_delete_limit_seconds_raw: Optional[Union[int, str]] = REQ( "message_content_delete_limit_seconds", json_validator=check_string_or_int, default=None), allow_message_editing: Optional[bool] = REQ(json_validator=check_bool, default=None), edit_topic_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.COMMON_MESSAGE_POLICY_TYPES), default=None), mandatory_topics: Optional[bool] = REQ(json_validator=check_bool, default=None), message_content_edit_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_edit_history: Optional[bool] = REQ(json_validator=check_bool, default=None), default_language: Optional[str] = REQ(default=None), waiting_period_threshold: Optional[int] = REQ( converter=to_non_negative_int, default=None), authentication_methods: Optional[Dict[str, Any]] = REQ( json_validator=check_dict([]), default=None), notifications_stream_id: Optional[int] = REQ(json_validator=check_int, default=None), signup_notifications_stream_id: Optional[int] = REQ( json_validator=check_int, default=None), message_retention_days_raw: Optional[Union[int, str]] = REQ( "message_retention_days", json_validator=check_string_or_int, default=None), send_welcome_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), digest_emails_enabled: Optional[bool] = REQ(json_validator=check_bool, default=None), message_content_allowed_in_email_notifications: Optional[bool] = REQ( json_validator=check_bool, default=None), bot_creation_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.BOT_CREATION_POLICY_TYPES), default=None), create_public_stream_policy: Optional[int] = REQ( json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None), create_private_stream_policy: Optional[int] = REQ( json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None), create_web_public_stream_policy: Optional[int] = REQ( json_validator=check_int_in( Realm.CREATE_WEB_PUBLIC_STREAM_POLICY_TYPES), default=None), invite_to_stream_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.COMMON_POLICY_TYPES), default=None), move_messages_between_streams_policy: Optional[int] = REQ( json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None), user_group_edit_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.COMMON_POLICY_TYPES), default=None), private_message_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.PRIVATE_MESSAGE_POLICY_TYPES), default=None), wildcard_mention_policy: Optional[int] = REQ(json_validator=check_int_in( Realm.WILDCARD_MENTION_POLICY_TYPES), default=None), email_address_visibility: Optional[int] = REQ(json_validator=check_int_in( Realm.EMAIL_ADDRESS_VISIBILITY_TYPES), default=None), video_chat_provider: Optional[int] = REQ(json_validator=check_int, default=None), giphy_rating: Optional[int] = REQ(json_validator=check_int, default=None), default_code_block_language: Optional[str] = REQ(default=None), digest_weekday: Optional[int] = REQ(json_validator=check_int_in( Realm.DIGEST_WEEKDAY_VALUES), default=None), string_id: Optional[str] = REQ( str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH), default=None, ), org_type: Optional[int] = REQ(json_validator=check_int_in(ORG_TYPE_IDS), default=None), enable_spectator_access: Optional[bool] = REQ(json_validator=check_bool, default=None), want_advertise_in_communities_directory: Optional[bool] = REQ( json_validator=check_bool, default=None), ) -> HttpResponse: realm = user_profile.realm # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes( ): raise JsonableError( _("Invalid language '{}'").format(default_language)) if authentication_methods is not None: if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() if True not in list(authentication_methods.values()): raise JsonableError( _("At least one authentication method must be enabled.")) if video_chat_provider is not None and video_chat_provider not in { p["id"] for p in Realm.VIDEO_CHAT_PROVIDERS.values() }: raise JsonableError( _("Invalid video_chat_provider {}").format(video_chat_provider)) if giphy_rating is not None and giphy_rating not in { p["id"] for p in Realm.GIPHY_RATING_OPTIONS.values() }: raise JsonableError(_("Invalid giphy_rating {}").format(giphy_rating)) message_retention_days: Optional[int] = None if message_retention_days_raw is not None: if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() realm.ensure_not_on_limited_plan() message_retention_days = parse_message_retention_days( message_retention_days_raw, Realm.MESSAGE_RETENTION_SPECIAL_VALUES_MAP) if invite_to_realm_policy is not None and not user_profile.is_realm_owner: raise OrganizationOwnerRequired() data: Dict[str, Any] = {} message_content_delete_limit_seconds: Optional[int] = None if message_content_delete_limit_seconds_raw is not None: message_content_delete_limit_seconds = parse_message_content_delete_limit( message_content_delete_limit_seconds_raw, Realm.MESSAGE_CONTENT_DELETE_LIMIT_SPECIAL_VALUES_MAP, ) do_set_realm_property( realm, "message_content_delete_limit_seconds", message_content_delete_limit_seconds, acting_user=user_profile, ) data[ "message_content_delete_limit_seconds"] = message_content_delete_limit_seconds # The user of `locals()` here is a bit of a code smell, but it's # restricted to the elements present in realm.property_types. # # TODO: It should be possible to deduplicate this function up # further by some more advanced usage of the # `REQ/has_request_variables` extraction. req_vars = { k: v for k, v in list(locals().items()) if k in realm.property_types } for k, v in list(req_vars.items()): if v is not None and getattr(realm, k) != v: do_set_realm_property(realm, k, v, acting_user=user_profile) if isinstance(v, str): data[k] = "updated" else: data[k] = v # The following realm properties do not fit the pattern above # authentication_methods is not supported by the do_set_realm_property # framework because of its bitfield. if authentication_methods is not None and ( realm.authentication_methods_dict() != authentication_methods): do_set_realm_authentication_methods(realm, authentication_methods, acting_user=user_profile) data["authentication_methods"] = authentication_methods # The message_editing settings are coupled to each other, and thus don't fit # into the do_set_realm_property framework. if ((allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or (message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds) or (edit_topic_policy is not None and realm.edit_topic_policy != edit_topic_policy)): if allow_message_editing is None: allow_message_editing = realm.allow_message_editing if message_content_edit_limit_seconds is None: message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds if edit_topic_policy is None: edit_topic_policy = realm.edit_topic_policy do_set_realm_message_editing( realm, allow_message_editing, message_content_edit_limit_seconds, edit_topic_policy, acting_user=user_profile, ) data["allow_message_editing"] = allow_message_editing data[ "message_content_edit_limit_seconds"] = message_content_edit_limit_seconds data["edit_topic_policy"] = edit_topic_policy # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean, # str or integer field, and thus doesn't fit into the do_set_realm_property framework. if notifications_stream_id is not None: if realm.notifications_stream is None or (realm.notifications_stream.id != notifications_stream_id): new_notifications_stream = None if notifications_stream_id >= 0: (new_notifications_stream, sub) = access_stream_by_id(user_profile, notifications_stream_id) do_set_realm_notifications_stream(realm, new_notifications_stream, notifications_stream_id, acting_user=user_profile) data["notifications_stream_id"] = notifications_stream_id if signup_notifications_stream_id is not None: if realm.signup_notifications_stream is None or ( realm.signup_notifications_stream.id != signup_notifications_stream_id): new_signup_notifications_stream = None if signup_notifications_stream_id >= 0: (new_signup_notifications_stream, sub) = access_stream_by_id(user_profile, signup_notifications_stream_id) do_set_realm_signup_notifications_stream( realm, new_signup_notifications_stream, signup_notifications_stream_id, acting_user=user_profile, ) data[ "signup_notifications_stream_id"] = signup_notifications_stream_id if default_code_block_language is not None: # Migrate '', used in the API to encode the default/None behavior of this feature. if default_code_block_language == "": data["default_code_block_language"] = None else: data["default_code_block_language"] = default_code_block_language if string_id is not None: if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() if realm.demo_organization_scheduled_deletion_date is None: raise JsonableError(_("Must be a demo organization.")) try: check_subdomain(string_id) except ValidationError as err: raise JsonableError(str(err.message)) do_change_realm_subdomain(realm, string_id, acting_user=user_profile) data["realm_uri"] = realm.uri if org_type is not None: do_change_realm_org_type(realm, org_type, acting_user=user_profile) data["org_type"] = org_type return json_success(request, data)
def test_user_settings_for_adding_custom_emoji(self) -> None: othello = self.example_user("othello") self.login_user(othello) do_change_user_role(othello, UserProfile.ROLE_MODERATOR, acting_user=None) do_set_realm_property(othello.realm, "add_custom_emoji_policy", Realm.POLICY_ADMINS_ONLY, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_1", info=emoji_data) self.assert_json_error(result, "Insufficient permission") do_change_user_role(othello, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_1", info=emoji_data) self.assert_json_success(result) do_set_realm_property(othello.realm, "add_custom_emoji_policy", Realm.POLICY_MODERATORS_ONLY, acting_user=None) do_change_user_role(othello, UserProfile.ROLE_MEMBER, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_2", info=emoji_data) self.assert_json_error(result, "Insufficient permission") do_change_user_role(othello, UserProfile.ROLE_MODERATOR, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_2", info=emoji_data) self.assert_json_success(result) do_set_realm_property( othello.realm, "add_custom_emoji_policy", Realm.POLICY_FULL_MEMBERS_ONLY, acting_user=None, ) do_set_realm_property(othello.realm, "waiting_period_threshold", 100000, acting_user=None) do_change_user_role(othello, UserProfile.ROLE_MEMBER, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_3", info=emoji_data) self.assert_json_error(result, "Insufficient permission") do_set_realm_property(othello.realm, "waiting_period_threshold", 0, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_3", info=emoji_data) self.assert_json_success(result) do_set_realm_property(othello.realm, "add_custom_emoji_policy", Realm.POLICY_MEMBERS_ONLY, acting_user=None) do_change_user_role(othello, UserProfile.ROLE_GUEST, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_4", info=emoji_data) self.assert_json_error(result, "Not allowed for guest users") do_change_user_role(othello, UserProfile.ROLE_MEMBER, acting_user=None) with get_test_image_file("img.png") as fp1: emoji_data = {"f1": fp1} result = self.client_post("/json/realm/emoji/my_emoji_4", info=emoji_data) self.assert_json_success(result)
def setUp(self) -> None: realm = get_realm("zulip") do_set_realm_property(realm, "emails_restricted_to_domains", True, acting_user=None)
def test_search(self) -> None: reset_emails_in_zulip_realm() def assert_user_details_in_html_response( html_response: "TestHttpResponse", full_name: str, email: str, role: str) -> None: self.assert_in_success_response( [ '<span class="label">user</span>\n', f"<h3>{full_name}</h3>", f"<b>Email</b>: {email}", "<b>Is active</b>: True<br />", f"<b>Role</b>: {role}<br />", ], html_response, ) def check_hamlet_user_query_result(result: "TestHttpResponse") -> None: assert_user_details_in_html_response(result, "King Hamlet", self.example_email("hamlet"), "Member") self.assert_in_success_response( [ f"<b>Admins</b>: {self.example_email('iago')}\n", f"<b>Owners</b>: {self.example_email('desdemona')}\n", 'class="copy-button" data-copytext="{}">'.format( self.example_email("iago")), 'class="copy-button" data-copytext="{}">'.format( self.example_email("desdemona")), ], result, ) def check_othello_user_query_result( result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, "Othello, the Moor of Venice", self.example_email("othello"), "Member") def check_polonius_user_query_result( result: "TestHttpResponse") -> None: assert_user_details_in_html_response( result, "Polonius", self.example_email("polonius"), "Guest") def check_zulip_realm_query_result(result: "TestHttpResponse") -> None: zulip_realm = get_realm("zulip") first_human_user = zulip_realm.get_first_human_user() assert first_human_user is not None self.assert_in_success_response( [ f"<b>First human user</b>: {first_human_user.delivery_email}\n", f'<input type="hidden" name="realm_id" value="{zulip_realm.id}"', "Zulip Dev</h3>", '<option value="1" selected>Self-hosted</option>', '<option value="2" >Limited</option>', 'input type="number" name="discount" value="None"', '<option value="active" selected>Active</option>', '<option value="deactivated" >Deactivated</option>', f'<option value="{zulip_realm.org_type}" selected>', 'scrub-realm-button">', 'data-string-id="zulip"', ], result, ) def check_lear_realm_query_result(result: "TestHttpResponse") -> None: lear_realm = get_realm("lear") self.assert_in_success_response( [ f'<input type="hidden" name="realm_id" value="{lear_realm.id}"', "Lear & Co.</h3>", '<option value="1" selected>Self-hosted</option>', '<option value="2" >Limited</option>', 'input type="number" name="discount" value="None"', '<option value="active" selected>Active</option>', '<option value="deactivated" >Deactivated</option>', 'scrub-realm-button">', 'data-string-id="lear"', "<b>Name</b>: Zulip Cloud Standard", "<b>Status</b>: Active", "<b>Billing schedule</b>: Annual", "<b>Licenses</b>: 2/10 (Manual)", "<b>Price per license</b>: $80.0", "<b>Next invoice date</b>: 02 January 2017", '<option value="send_invoice" selected>', '<option value="charge_automatically" >', ], result, ) def check_preregistration_user_query_result( result: "TestHttpResponse", email: str, invite: bool = False) -> None: self.assert_in_success_response( [ '<span class="label">preregistration user</span>\n', f"<b>Email</b>: {email}", ], result, ) if invite: self.assert_in_success_response( ['<span class="label">invite</span>'], result) self.assert_in_success_response( [ "<b>Expires in</b>: 1\xa0week, 3\xa0days", "<b>Status</b>: Link has never been clicked", ], result, ) self.assert_in_success_response([], result) else: self.assert_not_in_success_response( ['<span class="label">invite</span>'], result) self.assert_in_success_response( [ "<b>Expires in</b>: 1\xa0day", "<b>Status</b>: Link has never been clicked", ], result, ) def check_realm_creation_query_result(result: "TestHttpResponse", email: str) -> None: self.assert_in_success_response( [ '<span class="label">preregistration user</span>\n', '<span class="label">realm creation</span>\n', "<b>Link</b>: http://testserver/accounts/do_confirm/", "<b>Expires in</b>: 1\xa0day", ], result, ) def check_multiuse_invite_link_query_result( result: "TestHttpResponse") -> None: self.assert_in_success_response( [ '<span class="label">multiuse invite</span>\n', "<b>Link</b>: http://zulip.testserver/join/", "<b>Expires in</b>: 1\xa0week, 3\xa0days", ], result, ) def check_realm_reactivation_link_query_result( result: "TestHttpResponse") -> None: self.assert_in_success_response( [ '<span class="label">realm reactivation</span>\n', "<b>Link</b>: http://zulip.testserver/reactivate/", "<b>Expires in</b>: 1\xa0day", ], result, ) self.login("cordelia") result = self.client_get("/activity/support") self.assertEqual(result.status_code, 302) self.assertEqual(result["Location"], "/login/") self.login("iago") do_set_realm_property( get_realm("zulip"), "email_address_visibility", Realm.EMAIL_ADDRESS_VISIBILITY_NOBODY, acting_user=None, ) customer = Customer.objects.create(realm=get_realm("lear"), stripe_customer_id="cus_123") now = datetime(2016, 1, 2, tzinfo=timezone.utc) plan = CustomerPlan.objects.create( customer=customer, billing_cycle_anchor=now, billing_schedule=CustomerPlan.ANNUAL, tier=CustomerPlan.STANDARD, price_per_license=8000, next_invoice_date=add_months(now, 12), ) LicenseLedger.objects.create( licenses=10, licenses_at_next_renewal=10, event_time=timezone_now(), is_renewal=True, plan=plan, ) result = self.client_get("/activity/support") self.assert_in_success_response( ['<input type="text" name="q" class="input-xxlarge search-query"'], result) result = self.client_get("/activity/support", {"q": self.example_email("hamlet")}) check_hamlet_user_query_result(result) check_zulip_realm_query_result(result) result = self.client_get("/activity/support", {"q": self.example_email("polonius")}) check_polonius_user_query_result(result) check_zulip_realm_query_result(result) result = self.client_get("/activity/support", {"q": "lear"}) check_lear_realm_query_result(result) result = self.client_get("/activity/support", {"q": "http://lear.testserver"}) check_lear_realm_query_result(result) with self.settings(REALM_HOSTS={"zulip": "localhost"}): result = self.client_get("/activity/support", {"q": "http://localhost"}) check_zulip_realm_query_result(result) result = self.client_get("/activity/support", {"q": "[email protected], lear"}) check_hamlet_user_query_result(result) check_zulip_realm_query_result(result) check_lear_realm_query_result(result) result = self.client_get("/activity/support", {"q": "King hamlet,lear"}) check_hamlet_user_query_result(result) check_zulip_realm_query_result(result) check_lear_realm_query_result(result) result = self.client_get("/activity/support", {"q": "Othello, the Moor of Venice"}) check_othello_user_query_result(result) check_zulip_realm_query_result(result) result = self.client_get("/activity/support", {"q": "lear, Hamlet <*****@*****.**>"}) check_hamlet_user_query_result(result) check_zulip_realm_query_result(result) check_lear_realm_query_result(result) with mock.patch( "analytics.views.support.timezone_now", return_value=timezone_now() - timedelta(minutes=50), ): self.client_post("/accounts/home/", {"email": self.nonreg_email("test")}) self.login("iago") result = self.client_get("/activity/support", {"q": self.nonreg_email("test")}) check_preregistration_user_query_result(result, self.nonreg_email("test")) check_zulip_realm_query_result(result) invite_expires_in_minutes = 10 * 24 * 60 stream_ids = [self.get_stream_id("Denmark")] invitee_emails = [self.nonreg_email("test1")] self.client_post( "/json/invites", { "invitee_emails": invitee_emails, "stream_ids": orjson.dumps(stream_ids).decode(), "invite_expires_in_minutes": invite_expires_in_minutes, "invite_as": PreregistrationUser.INVITE_AS["MEMBER"], }, ) result = self.client_get("/activity/support", {"q": self.nonreg_email("test1")}) check_preregistration_user_query_result(result, self.nonreg_email("test1"), invite=True) check_zulip_realm_query_result(result) email = self.nonreg_email("alice") self.client_post("/new/", {"email": email}) result = self.client_get("/activity/support", {"q": email}) check_realm_creation_query_result(result, email) do_create_multiuse_invite_link( self.example_user("hamlet"), invited_as=1, invite_expires_in_minutes=invite_expires_in_minutes, ) result = self.client_get("/activity/support", {"q": "zulip"}) check_multiuse_invite_link_query_result(result) check_zulip_realm_query_result(result) MultiuseInvite.objects.all().delete() do_send_realm_reactivation_email(get_realm("zulip"), acting_user=None) result = self.client_get("/activity/support", {"q": "zulip"}) check_realm_reactivation_link_query_result(result) check_zulip_realm_query_result(result)
def test_user_group_edit_policy_for_creating_and_deleting_user_group(self) -> None: hamlet = self.example_user("hamlet") realm = hamlet.realm def check_create_user_group(acting_user: str, error_msg: Optional[str] = None) -> None: self.login(acting_user) params = { "name": "support", "members": orjson.dumps([hamlet.id]).decode(), "description": "Support Team", } result = self.client_post("/json/user_groups/create", info=params) if error_msg is None: self.assert_json_success(result) # One group already exists in the test database. self.assert_length(UserGroup.objects.filter(realm=realm), 9) else: self.assert_json_error(result, error_msg) def check_delete_user_group(acting_user: str, error_msg: Optional[str] = None) -> None: self.login(acting_user) user_group = UserGroup.objects.get(name="support") result = self.client_delete(f"/json/user_groups/{user_group.id}") if error_msg is None: self.assert_json_success(result) self.assert_length(UserGroup.objects.filter(realm=realm), 8) else: self.assert_json_error(result, error_msg) realm = hamlet.realm # Check only admins are allowed to create/delete user group. Admins are allowed even if # they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_ADMINS_ONLY, acting_user=None, ) check_create_user_group("shiva", "Insufficient permission") check_create_user_group("iago") check_delete_user_group("shiva", "Insufficient permission") check_delete_user_group("iago") # Check moderators are allowed to create/delete user group but not members. Moderators are # allowed even if they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MODERATORS_ONLY, acting_user=None, ) check_create_user_group("cordelia", "Insufficient permission") check_create_user_group("shiva") check_delete_user_group("hamlet", "Insufficient permission") check_delete_user_group("shiva") # Check only members are allowed to create the user group and they are allowed to delete # a user group only if they are a member of that group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MEMBERS_ONLY, acting_user=None, ) check_create_user_group("polonius", "Not allowed for guest users") check_create_user_group("cordelia") check_delete_user_group("polonius", "Not allowed for guest users") check_delete_user_group("cordelia", "Insufficient permission") check_delete_user_group("hamlet") # Check only full members are allowed to create the user group and they are allowed to delete # a user group only if they are a member of that group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_FULL_MEMBERS_ONLY, acting_user=None, ) cordelia = self.example_user("cordelia") do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None) cordelia.date_joined = timezone_now() - timedelta(days=9) cordelia.save() check_create_user_group("cordelia", "Insufficient permission") cordelia.date_joined = timezone_now() - timedelta(days=11) cordelia.save() check_create_user_group("cordelia") hamlet.date_joined = timezone_now() - timedelta(days=9) hamlet.save() check_delete_user_group("cordelia", "Insufficient permission") check_delete_user_group("hamlet", "Insufficient permission") hamlet.date_joined = timezone_now() - timedelta(days=11) hamlet.save() check_delete_user_group("hamlet")
def test_user_group_edit_policy_for_updating_user_groups(self) -> None: othello = self.example_user("othello") self.login("othello") params = { "name": "support", "members": orjson.dumps([othello.id]).decode(), "description": "Support team", } self.client_post("/json/user_groups/create", info=params) user_group = UserGroup.objects.get(name="support") def check_update_user_group( new_name: str, new_description: str, acting_user: str, error_msg: Optional[str] = None, ) -> None: self.login(acting_user) params = { "name": new_name, "description": new_description, } result = self.client_patch(f"/json/user_groups/{user_group.id}", info=params) if error_msg is None: self.assert_json_success(result) else: self.assert_json_error(result, error_msg) realm = othello.realm # Check only admins are allowed to update user group. Admins are allowed even if # they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_ADMINS_ONLY, acting_user=None, ) check_update_user_group("help", "Troubleshooting team", "shiva", "Insufficient permission") check_update_user_group("help", "Troubleshooting team", "iago") # Check moderators are allowed to update user group but not members. Moderators are # allowed even if they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MODERATORS_ONLY, acting_user=None, ) check_update_user_group("support", "Support team", "othello", "Insufficient permission") check_update_user_group("support", "Support team", "iago") # Check only members are allowed to update the user group and only if belong to the # user group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MEMBERS_ONLY, acting_user=None, ) check_update_user_group( "help", "Troubleshooting team", "polonius", "Not allowed for guest users" ) check_update_user_group( "help", "Troubleshooting team", "cordelia", "Insufficient permission", ) check_update_user_group("help", "Troubleshooting team", "othello") # Check only full members are allowed to update the user group and only if belong to the # user group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_FULL_MEMBERS_ONLY, acting_user=None ) do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None) othello = self.example_user("othello") othello.date_joined = timezone_now() - timedelta(days=9) othello.save() cordelia = self.example_user("cordelia") cordelia.date_joined = timezone_now() - timedelta(days=11) cordelia.save() check_update_user_group( "support", "Support team", "cordelia", "Insufficient permission", ) check_update_user_group("support", "Support team", "othello", "Insufficient permission") othello.date_joined = timezone_now() - timedelta(days=11) othello.save() check_update_user_group("support", "Support team", "othello")
def test_user_group_edit_policy_for_updating_members(self) -> None: user_group = self.create_user_group_for_test("support") aaron = self.example_user("aaron") othello = self.example_user("othello") cordelia = self.example_user("cordelia") def check_adding_members_to_group( acting_user: str, error_msg: Optional[str] = None ) -> None: self.login(acting_user) params = {"add": orjson.dumps([aaron.id]).decode()} result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params) if error_msg is None: self.assert_json_success(result) self.assertEqual( UserGroupMembership.objects.filter(user_group=user_group).count(), 2 ) members = get_direct_memberships_of_users(user_group, [aaron, othello]) self.assert_length(members, 2) else: self.assert_json_error(result, error_msg) def check_removing_members_from_group( acting_user: str, error_msg: Optional[str] = None ) -> None: self.login(acting_user) params = {"delete": orjson.dumps([aaron.id]).decode()} result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params) if error_msg is None: self.assert_json_success(result) self.assertEqual( UserGroupMembership.objects.filter(user_group=user_group).count(), 1 ) members = get_direct_memberships_of_users(user_group, [aaron, othello]) self.assert_length(members, 1) else: self.assert_json_error(result, error_msg) realm = get_realm("zulip") # Check only admins are allowed to add/remove users from the group. Admins are allowed even if # they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_ADMINS_ONLY, acting_user=None, ) check_adding_members_to_group("shiva", "Insufficient permission") check_adding_members_to_group("iago") check_removing_members_from_group("shiva", "Insufficient permission") check_removing_members_from_group("iago") # Check moderators are allowed to add/remove users from the group but not members. Moderators are # allowed even if they are not a member of the group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MODERATORS_ONLY, acting_user=None, ) check_adding_members_to_group("cordelia", "Insufficient permission") check_adding_members_to_group("shiva") check_removing_members_from_group("hamlet", "Insufficient permission") check_removing_members_from_group("shiva") # Check only members are allowed to add/remove users in the group and only if belong to the # user group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_MEMBERS_ONLY, acting_user=None, ) check_adding_members_to_group("polonius", "Not allowed for guest users") check_adding_members_to_group("cordelia", "Insufficient permission") check_adding_members_to_group("othello") check_removing_members_from_group("polonius", "Not allowed for guest users") check_removing_members_from_group("cordelia", "Insufficient permission") check_removing_members_from_group("othello") # Check only full members are allowed to add/remove users in the group and only if belong to the # user group. do_set_realm_property( realm, "user_group_edit_policy", Realm.POLICY_FULL_MEMBERS_ONLY, acting_user=None, ) do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None) othello.date_joined = timezone_now() - timedelta(days=9) othello.save() check_adding_members_to_group("cordelia", "Insufficient permission") cordelia.date_joined = timezone_now() - timedelta(days=11) cordelia.save() check_adding_members_to_group("cordelia", "Insufficient permission") othello.date_joined = timezone_now() - timedelta(days=11) othello.save() check_adding_members_to_group("othello") othello.date_joined = timezone_now() - timedelta(days=9) othello.save() check_removing_members_from_group("cordelia", "Insufficient permission") check_removing_members_from_group("othello", "Insufficient permission") othello.date_joined = timezone_now() - timedelta(days=11) othello.save() check_removing_members_from_group("othello")