Example #1
0
    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)

        old_email = user_profile.email
        new_email = '*****@*****.**'
        self.login(self.example_email('hamlet'))
        obj = EmailChangeStatus.objects.create(new_email=new_email,
                                               old_email=old_email,
                                               user_profile=user_profile,
                                               realm=user_profile.realm)
        key = generate_key()
        Confirmation.objects.create(content_object=obj,
                                    date_sent=now(),
                                    confirmation_key=key,
                                    type=Confirmation.EMAIL_CHANGE)
        url = confirmation_url(key, user_profile.realm.host, 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, "*****@*****.**")
        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)
Example #2
0
def create_user_backend(request: HttpRequest, user_profile: UserProfile,
                        email: Text=REQ(), password: Text=REQ(), full_name_raw: Text=REQ("full_name"),
                        short_name: Text=REQ()) -> HttpResponse:
    full_name = check_full_name(full_name_raw)
    form = CreateUserForm({'full_name': full_name, 'email': email})
    if not form.is_valid():
        return json_error(_('Bad name or username'))

    # Check that the new user's email address belongs to the admin's realm
    # (Since this is an admin API, we don't require the user to have been
    # invited first.)
    realm = user_profile.realm
    try:
        email_allowed_for_realm(email, user_profile.realm)
    except DomainNotAllowedForRealmError:
        return json_error(_("Email '%(email)s' not allowed in this organization") %
                          {'email': email})
    except DisposableEmailError:
        return json_error(_("Disposable email addresses are not allowed in this organization"))

    try:
        get_user(email, user_profile.realm)
        return json_error(_("Email '%s' already in use") % (email,))
    except UserProfile.DoesNotExist:
        pass

    do_create_user(email, password, realm, full_name, short_name)
    return json_success()
Example #3
0
def add_bot_backend(request, user_profile, full_name_raw=REQ("full_name"), short_name=REQ(),
                    default_sending_stream_name=REQ('default_sending_stream', default=None),
                    default_events_register_stream_name=REQ('default_events_register_stream', default=None),
                    default_all_public_streams=REQ(validator=check_bool, default=None)):
    # type: (HttpRequest, UserProfile, Text, Text, Optional[Text], Optional[Text], Optional[bool]) -> HttpResponse
    short_name += "-bot"
    full_name = check_full_name(full_name_raw)
    email = '%s@%s' % (short_name, user_profile.realm.get_bot_domain())
    form = CreateUserForm({'full_name': full_name, 'email': email})
    if not form.is_valid():
        # We validate client-side as well
        return json_error(_('Bad name or username'))

    try:
        get_user(email, user_profile.realm)
        return json_error(_("Username already in use"))
    except UserProfile.DoesNotExist:
        pass

    if len(request.FILES) == 0:
        avatar_source = UserProfile.AVATAR_FROM_GRAVATAR
    elif len(request.FILES) != 1:
        return json_error(_("You may only upload one file at a time"))
    else:
        avatar_source = UserProfile.AVATAR_FROM_USER

    default_sending_stream = None
    if default_sending_stream_name is not None:
        (default_sending_stream, ignored_rec, ignored_sub) = access_stream_by_name(
            user_profile, default_sending_stream_name)

    default_events_register_stream = None
    if default_events_register_stream_name is not None:
        (default_events_register_stream, ignored_rec, ignored_sub) = access_stream_by_name(
            user_profile, default_events_register_stream_name)

    bot_profile = do_create_user(email=email, password='',
                                 realm=user_profile.realm, full_name=full_name,
                                 short_name=short_name, active=True,
                                 bot_type=UserProfile.DEFAULT_BOT,
                                 bot_owner=user_profile,
                                 avatar_source=avatar_source,
                                 default_sending_stream=default_sending_stream,
                                 default_events_register_stream=default_events_register_stream,
                                 default_all_public_streams=default_all_public_streams)
    if len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        upload_avatar_image(user_file, user_profile, bot_profile)
    json_result = dict(
        api_key=bot_profile.api_key,
        avatar_url=avatar_url(bot_profile),
        default_sending_stream=get_stream_name(bot_profile.default_sending_stream),
        default_events_register_stream=get_stream_name(bot_profile.default_events_register_stream),
        default_all_public_streams=bot_profile.default_all_public_streams,
    )
    return json_success(json_result)
Example #4
0
    def test_confirm_email_change(self) -> None:
        user_profile = self.example_user('hamlet')
        old_email = user_profile.email
        new_email = '*****@*****.**'
        new_realm = get_realm('zulip')
        self.login(self.example_email('hamlet'))
        obj = EmailChangeStatus.objects.create(new_email=new_email,
                                               old_email=old_email,
                                               user_profile=user_profile,
                                               realm=user_profile.realm)
        key = generate_key()
        Confirmation.objects.create(content_object=obj,
                                    date_sent=now(),
                                    confirmation_key=key,
                                    type=Confirmation.EMAIL_CHANGE)
        url = confirmation_url(key, user_profile.realm.host, 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(new_email, new_realm)
        self.assertTrue(bool(user_profile))
        obj.refresh_from_db()
        self.assertEqual(obj.status, 1)
Example #5
0
 def _login(self, user_email, user_realm, password=None):
     # type: (Text, Realm, str) -> None
     if password:
         user_profile = get_user(user_email, user_realm)
         user_profile.set_password(password)
         user_profile.save()
     self.login(user_email, password)
Example #6
0
    def test_create_outgoing_webhook_bot(self, **extras: Any) -> None:
        self.login(self.example_email('hamlet'))
        bot_info = {
            'full_name': 'Outgoing Webhook test bot',
            'short_name': 'outgoingservicebot',
            'bot_type': UserProfile.OUTGOING_WEBHOOK_BOT,
            'payload_url': ujson.dumps('http://127.0.0.1:5002/bots/followup'),
        }
        bot_info.update(extras)
        result = self.client_post("/json/bots", bot_info)
        self.assert_json_success(result)

        bot_email = "*****@*****.**"
        bot_realm = get_realm('zulip')
        bot = get_user(bot_email, bot_realm)
        services = get_bot_services(bot.id)
        service = services[0]

        self.assertEqual(len(services), 1)
        self.assertEqual(service.name, "outgoingservicebot")
        self.assertEqual(service.base_url, "http://127.0.0.1:5002/bots/followup")
        self.assertEqual(service.user_profile, bot)

        # invalid URL test case.
        bot_info['payload_url'] = ujson.dumps('http://127.0.0.:5002/bots/followup')
        result = self.client_post("/json/bots", bot_info)
        self.assert_json_error(result, "Enter a valid URL.")
Example #7
0
def get_presence_backend(request: HttpRequest, user_profile: UserProfile,
                         email: Text) -> HttpResponse:
    try:
        target = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such user'))
    if not target.is_active:
        return json_error(_('No such user'))
    if target.is_bot:
        return json_error(_('Presence is not supported for bot users.'))

    presence_dict = UserPresence.get_status_dict_by_user(target)
    if len(presence_dict) == 0:
        return json_error(_('No presence data for %s' % (target.email,)))

    # For initial version, we just include the status and timestamp keys
    result = dict(presence=presence_dict[target.email])
    aggregated_info = result['presence']['aggregated']
    aggr_status_duration = datetime_to_timestamp(timezone_now()) - aggregated_info['timestamp']
    if aggr_status_duration > settings.OFFLINE_THRESHOLD_SECS:
        aggregated_info['status'] = 'offline'
    for val in result['presence'].values():
        val.pop('client', None)
        val.pop('pushable', None)
    return json_success(result)
Example #8
0
    def test_notify_bot_owner_on_invalid_json(self) -> None:
        @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=False)
        def my_webhook_no_notify(request: HttpRequest, user_profile: UserProfile) -> None:
            raise InvalidJSONError("Malformed JSON")

        @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=True)
        def my_webhook_notify(request: HttpRequest, user_profile: UserProfile) -> None:
            raise InvalidJSONError("Malformed JSON")

        webhook_bot_email = '*****@*****.**'
        webhook_bot_realm = get_realm('zulip')
        webhook_bot = get_user(webhook_bot_email, webhook_bot_realm)
        webhook_bot_api_key = get_api_key(webhook_bot)
        request = HostRequestMock()
        request.POST['api_key'] = webhook_bot_api_key
        request.host = "zulip.testserver"
        expected_msg = INVALID_JSON_MESSAGE.format(webhook_name='ClientName')

        last_message_id = self.get_last_message().id
        with self.assertRaisesRegex(JsonableError, "Malformed JSON"):
            my_webhook_no_notify(request)  # type: ignore # mypy doesn't seem to apply the decorator

        # First verify that without the setting, it doesn't send a PM to bot owner.
        msg = self.get_last_message()
        self.assertEqual(msg.id, last_message_id)
        self.assertNotEqual(msg.content, expected_msg.strip())

        # Then verify that with the setting, it does send such a message.
        with self.assertRaisesRegex(JsonableError, "Malformed JSON"):
            my_webhook_notify(request)  # type: ignore # mypy doesn't seem to apply the decorator
        msg = self.get_last_message()
        self.assertNotEqual(msg.id, last_message_id)
        self.assertEqual(msg.sender.email, self.notification_bot().email)
        self.assertEqual(msg.content, expected_msg.strip())
Example #9
0
def common_get_active_user(email: str, realm: Realm,
                           return_data: Dict[str, Any]=None) -> Optional[UserProfile]:
    try:
        user_profile = get_user(email, realm)
    except UserProfile.DoesNotExist:
        # If the user doesn't have an account in the target realm, we
        # check whether they might have an account in another realm,
        # and if so, provide a helpful error message via
        # `invalid_subdomain`.
        if not UserProfile.objects.filter(email__iexact=email).exists():
            return None
        if return_data is not None:
            return_data['invalid_subdomain'] = True
        return None
    if not user_profile.is_active:
        if return_data is not None:
            if user_profile.is_mirror_dummy:
                # Record whether it's a mirror dummy account
                return_data['is_mirror_dummy'] = True
            return_data['inactive_user'] = True
        return None
    if user_profile.realm.deactivated:
        if return_data is not None:
            return_data['inactive_realm'] = True
        return None
    return user_profile
Example #10
0
    def test_file_download_authorization_public(self):
        # type: () -> None
        subscribed_users = [self.example_email("hamlet"), self.example_email("iago")]
        unsubscribed_users = [self.example_email("othello"), self.example_email("prospero")]
        realm = get_realm("zulip")
        for email in subscribed_users:
            self.subscribe(get_user(email, realm), "test-subscribe")

        self.login(self.example_email("hamlet"))
        fp = StringIO("zulip!")
        fp.name = "zulip.txt"
        result = self.client_post("/json/user_uploads", {'file': fp})
        uri = result.json()['uri']
        fp_path_id = re.sub('/user_uploads/', '', uri)
        body = "First message ...[zulip.txt](http://localhost:9991/user_uploads/" + fp_path_id + ")"
        self.send_stream_message(self.example_email("hamlet"), "test-subscribe", body, "test")
        self.logout()

        # Now all users should be able to access the files
        for user in subscribed_users + unsubscribed_users:
            self.login(user)
            response = self.client_get(uri)
            data = b"".join(response.streaming_content)
            self.assertEqual(b"zulip!", data)
            self.logout()
Example #11
0
    def test_get_accounts_for_email(self) -> None:
        def check_account_present_in_accounts(user: UserProfile, accounts: List[Dict[str, Optional[str]]]) -> None:
            for account in accounts:
                realm = user.realm
                if account["avatar"] == avatar_url(user) and account["full_name"] == user.full_name \
                        and account["realm_name"] == realm.name and account["string_id"] == realm.string_id:
                    return
            raise AssertionError("Account not found")

        lear_realm = get_realm("lear")
        cordelia_in_zulip = self.example_user("cordelia")
        cordelia_in_lear = get_user("*****@*****.**", lear_realm)

        email = "*****@*****.**"
        accounts = get_accounts_for_email(email)
        self.assert_length(accounts, 2)
        check_account_present_in_accounts(cordelia_in_zulip, accounts)
        check_account_present_in_accounts(cordelia_in_lear, accounts)

        email = "*****@*****.**"
        accounts = get_accounts_for_email(email)
        self.assert_length(accounts, 2)
        check_account_present_in_accounts(cordelia_in_zulip, accounts)
        check_account_present_in_accounts(cordelia_in_lear, accounts)

        email = "*****@*****.**"
        accounts = get_accounts_for_email(email)
        self.assert_length(accounts, 1)
        check_account_present_in_accounts(self.example_user("iago"), accounts)
Example #12
0
def get_assignee_mention(assignee_email: str, realm: Realm) -> str:
    if assignee_email != '':
        try:
            assignee_name = get_user(assignee_email, realm).full_name
        except UserProfile.DoesNotExist:
            assignee_name = assignee_email
        return u"**{}**".format(assignee_name)
    return ''
Example #13
0
def api_yo_app_webhook(request: HttpRequest, user_profile: UserProfile,
                       email: str = REQ(default=""),
                       username: str = REQ(default='Yo Bot'),
                       topic: Optional[str] = REQ(default=None),
                       user_ip: Optional[str] = REQ(default=None)) -> HttpResponse:
    body = ('Yo from %s') % (username,)
    receiving_user = get_user(email, user_profile.realm)
    check_send_private_message(user_profile, request.client, receiving_user, body)
    return json_success()
Example #14
0
def deactivate_bot_backend(request: HttpRequest, user_profile: UserProfile,
                           email: Text) -> HttpResponse:
    try:
        target = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such bot'))
    if not target.is_bot:
        return json_error(_('No such bot'))
    return _deactivate_user_profile_backend(request, user_profile, target)
Example #15
0
def principal_to_user_profile(agent: UserProfile, principal: Text) -> UserProfile:
    try:
        return get_user(principal, agent.realm)
    except UserProfile.DoesNotExist:
        # We have to make sure we don't leak information about which users
        # are registered for Zulip in a different realm.  We could do
        # something a little more clever and check the domain part of the
        # principal to maybe give a better error message
        raise PrincipalError(principal)
Example #16
0
 def get_streams(self, email: str, realm: Realm) -> List[str]:
     """
     Helper function to get the stream names for a user
     """
     user_profile = get_user(email, realm)
     subs = get_stream_subscriptions_for_user(user_profile).filter(
         active=True,
     )
     return [cast(str, get_display_recipient(sub.recipient)) for sub in subs]
Example #17
0
    def test_webhook_http_header_header_exists(self) -> None:
        webhook_bot = get_user('*****@*****.**', get_realm('zulip'))
        request = HostRequestMock()
        request.META['HTTP_X_CUSTOM_HEADER'] = 'custom_value'
        request.user = webhook_bot

        header_value = validate_extract_webhook_http_header(request, 'X_CUSTOM_HEADER',
                                                            'test_webhook')

        self.assertEqual(header_value, 'custom_value')
Example #18
0
    def test_add_bot_with_default_sending_stream_not_subscribed(self) -> None:
        email = '*****@*****.**'
        realm = get_realm('zulip')
        self.login(self.example_email('hamlet'))
        self.assert_num_bots_equal(0)
        result = self.create_bot(default_sending_stream='Rome')
        self.assert_num_bots_equal(1)
        self.assertEqual(result['default_sending_stream'], 'Rome')

        profile = get_user(email, realm)
        self.assertEqual(profile.default_sending_stream.name, 'Rome')
Example #19
0
    def send_personal_message(self, from_email: str, to_email: str, content: str="test content",
                              sender_realm: str="zulip") -> int:
        sender = get_user(from_email, get_realm(sender_realm))

        recipient_list = [to_email]
        (sending_client, _) = Client.objects.get_or_create(name="test suite")

        return check_send_message(
            sender, sending_client, 'private', recipient_list, None,
            content
        )
Example #20
0
    def test_add_bot_with_bot_type_incoming_webhook(self) -> None:
        bot_email = '*****@*****.**'
        bot_realm = get_realm('zulip')

        self.login(self.example_email('hamlet'))
        self.assert_num_bots_equal(0)
        self.create_bot(bot_type=UserProfile.INCOMING_WEBHOOK_BOT)
        self.assert_num_bots_equal(1)

        profile = get_user(bot_email, bot_realm)
        self.assertEqual(profile.bot_type, UserProfile.INCOMING_WEBHOOK_BOT)
Example #21
0
    def test_add_bot_with_default_all_public_streams(self) -> None:
        self.login(self.example_email('hamlet'))
        self.assert_num_bots_equal(0)
        result = self.create_bot(default_all_public_streams=ujson.dumps(True))
        self.assert_num_bots_equal(1)
        self.assertTrue(result['default_all_public_streams'])

        bot_email = '*****@*****.**'
        bot_realm = get_realm('zulip')
        profile = get_user(bot_email, bot_realm)
        self.assertEqual(profile.default_all_public_streams, True)
Example #22
0
    def test_add_bot_with_bot_type_default(self) -> None:
        bot_email = '*****@*****.**'
        bot_realm = get_realm('zulip')

        self.login(self.example_email('hamlet'))
        self.assert_num_bots_equal(0)
        self.create_bot(bot_type=UserProfile.DEFAULT_BOT)
        self.assert_num_bots_equal(1)

        profile = get_user(bot_email, bot_realm)
        self.assertEqual(profile.bot_type, UserProfile.DEFAULT_BOT)
Example #23
0
 def test_api_update_pointer(self) -> None:
     """
     Same as above, but for the API view
     """
     user = self.example_user('hamlet')
     email = user.email
     self.assertEqual(user.pointer, -1)
     msg_id = self.send_stream_message(self.example_email("othello"), "Verona")
     result = self.api_post(email, "/api/v1/users/me/pointer", {"pointer": msg_id})
     self.assert_json_success(result)
     self.assertEqual(get_user(email, user.realm).pointer, msg_id)
Example #24
0
 def test_authenticated_json_post_view_if_user_realm_is_deactivated(self):
     # type: () -> None
     user_email = self.example_email('hamlet')
     user_realm = get_realm('zulip')
     user_profile = get_user(user_email, user_realm)
     self._login(user_email, user_realm)
     # we deactivate user's realm manually because do_deactivate_user removes user session
     user_profile.realm.deactivated = True
     user_profile.realm.save()
     self.assert_json_error_contains(self._do_test(user_email), "Realm for account has been deactivated")
     do_reactivate_realm(user_profile.realm)
Example #25
0
    def test_create_embedded_bot(self, **extras: Any) -> None:
        self.login(self.example_email('hamlet'))

        # Test to create embedded bot with correct service_name
        bot_config_info = {'key': 'value'}
        bot_info = {
            'full_name': 'Embedded test bot',
            'short_name': 'embeddedservicebot',
            'bot_type': UserProfile.EMBEDDED_BOT,
            'service_name': 'followup',
            'config_data': ujson.dumps(bot_config_info),
        }
        bot_info.update(extras)
        with self.settings(EMBEDDED_BOTS_ENABLED=False):
            result = self.client_post("/json/bots", bot_info)
            self.assert_json_error(result, 'Embedded bots are not enabled.')

        result = self.client_post("/json/bots", bot_info)
        self.assert_json_success(result)

        bot_email = "*****@*****.**"
        bot_realm = get_realm('zulip')
        bot = get_user(bot_email, bot_realm)
        services = get_bot_services(bot.id)
        service = services[0]
        bot_config = get_bot_config(bot)
        self.assertEqual(bot_config, bot_config_info)
        self.assertEqual(len(services), 1)
        self.assertEqual(service.name, "followup")
        self.assertEqual(service.user_profile, bot)

        # Test to create embedded bot with incorrect service_name
        bot_info = {
            'full_name': 'Embedded test bot',
            'short_name': 'embeddedservicebot',
            'bot_type': UserProfile.EMBEDDED_BOT,
            'service_name': 'not_existing_service',
        }
        bot_info.update(extras)
        result = self.client_post("/json/bots", bot_info)
        self.assert_json_error(result, 'Invalid embedded bot name.')

        # Test to create embedded bot with an invalid config value
        malformatted_bot_config_info = {'foo': ['bar', 'baz']}
        bot_info = {
            'full_name': 'Embedded test bot',
            'short_name': 'embeddedservicebot2',
            'bot_type': UserProfile.EMBEDDED_BOT,
            'service_name': 'followup',
            'config_data': ujson.dumps(malformatted_bot_config_info)
        }
        bot_info.update(extras)
        result = self.client_post("/json/bots", bot_info)
        self.assert_json_error(result, 'config_data contains a value that is not a string')
Example #26
0
    def test_non_generic_payload_ignore_pm_notification(self) -> None:
        expected_message = MISCONFIGURED_PAYLOAD_TYPE_ERROR_MESSAGE.format(
            bot_name=get_user('*****@*****.**', get_realm('zulip')).full_name,
            support_email=FromAddress.SUPPORT
        ).strip()
        payload = self.get_body('slack_non_generic_payload')
        self.client_post(self.url, payload, content_type="application/json")
        msg = self.get_last_message()

        self.assertEqual(msg.content, expected_message)
        self.assertEqual(msg.recipient.type, Recipient.PERSONAL)
Example #27
0
def deactivate_user_backend(request: HttpRequest, user_profile: UserProfile,
                            email: Text) -> HttpResponse:
    try:
        target = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such user'))
    if target.is_bot:
        return json_error(_('No such user'))
    if check_last_admin(target):
        return json_error(_('Cannot deactivate the only organization administrator'))
    return _deactivate_user_profile_backend(request, user_profile, target)
Example #28
0
 def get_streams(self, email, realm):
     # type: (Text, Realm) -> List[Text]
     """
     Helper function to get the stream names for a user
     """
     user_profile = get_user(email, realm)
     subs = Subscription.objects.filter(
         user_profile=user_profile,
         active=True,
         recipient__type=Recipient.STREAM)
     return [cast(Text, get_display_recipient(sub.recipient)) for sub in subs]
Example #29
0
    def send_huddle_message(self, from_email: str, to_emails: List[str], content: str="test content",
                            sender_realm: str="zulip") -> int:
        sender = get_user(from_email, get_realm(sender_realm))

        assert(len(to_emails) >= 2)

        (sending_client, _) = Client.objects.get_or_create(name="test suite")

        return check_send_message(
            sender, sending_client, 'private', to_emails, None,
            content
        )
Example #30
0
def reactivate_user_backend(request: HttpRequest, user_profile: UserProfile,
                            email: Text) -> HttpResponse:
    try:
        target = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such user'))

    if not user_profile.can_admin_user(target):
        return json_error(_('Insufficient permission'))

    do_reactivate_user(target, acting_user=user_profile)
    return json_success()
Example #31
0
def patch_bot_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    email: Text,
    full_name: Optional[Text] = REQ(default=None),
    bot_owner: Optional[Text] = REQ(default=None),
    config_data: Optional[Dict[Text, Text]] = REQ(
        default=None, validator=check_dict(value_validator=check_string)),
    service_payload_url: Optional[Text] = REQ(validator=check_url,
                                              default=None),
    service_interface: Optional[int] = REQ(validator=check_int, default=1),
    default_sending_stream: Optional[Text] = REQ(default=None),
    default_events_register_stream: Optional[Text] = REQ(default=None),
    default_all_public_streams: Optional[bool] = REQ(default=None,
                                                     validator=check_bool)
) -> HttpResponse:
    try:
        bot = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such user'))

    if not bot.is_bot:
        return json_error(_('No such bot'))
    if not user_profile.can_admin_user(bot):
        return json_error(_('Insufficient permission'))

    if full_name is not None:
        check_change_full_name(bot, full_name, user_profile)
    if bot_owner is not None:
        try:
            owner = get_user(bot_owner, user_profile.realm)
        except UserProfile.DoesNotExist:
            return json_error(_('Failed to change owner, no such user'))
        if not owner.is_active:
            return json_error(_('Failed to change owner, user is deactivated'))
        if owner.is_bot:
            return json_error(
                _("Failed to change owner, bots can't own other bots"))
        do_change_bot_owner(bot, owner, user_profile)

    if default_sending_stream is not None:
        if default_sending_stream == "":
            stream = None  # type: Optional[Stream]
        else:
            (stream, recipient,
             sub) = access_stream_by_name(user_profile, default_sending_stream)
        do_change_default_sending_stream(bot, stream)
    if default_events_register_stream is not None:
        if default_events_register_stream == "":
            stream = None
        else:
            (stream, recipient,
             sub) = access_stream_by_name(user_profile,
                                          default_events_register_stream)
        do_change_default_events_register_stream(bot, stream)
    if default_all_public_streams is not None:
        do_change_default_all_public_streams(bot, default_all_public_streams)

    if service_payload_url is not None:
        check_valid_interface_type(service_interface)
        do_update_outgoing_webhook_service(bot, service_interface,
                                           service_payload_url)

    if config_data is not None:
        do_update_bot_config_data(bot, config_data)

    if len(request.FILES) == 0:
        pass
    elif len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        upload_avatar_image(user_file, user_profile, bot)
        avatar_source = UserProfile.AVATAR_FROM_USER
        do_change_avatar_fields(bot, avatar_source)
    else:
        return json_error(_("You may only upload one file at a time"))

    json_result = dict(
        full_name=bot.full_name,
        avatar_url=avatar_url(bot),
        service_interface=service_interface,
        service_payload_url=service_payload_url,
        config_data=config_data,
        default_sending_stream=get_stream_name(bot.default_sending_stream),
        default_events_register_stream=get_stream_name(
            bot.default_events_register_stream),
        default_all_public_streams=bot.default_all_public_streams,
    )

    # Don't include the bot owner in case it is not set.
    # Default bots have no owner.
    if bot.bot_owner is not None:
        json_result['bot_owner'] = bot.bot_owner.email

    return json_success(json_result)
Example #32
0
 def create_user(email: Text, realm_id: Text) -> UserProfile:
     self.register(email, 'test', subdomain=realm_id)
     return get_user(email, get_realm(realm_id))
Example #33
0
def create_user_groups() -> None:
    zulip = get_realm('zulip')
    members = [get_user('*****@*****.**', zulip),
               get_user('*****@*****.**', zulip)]
    create_user_group("hamletcharacters", members, zulip,
                      description="Characters of Hamlet")
    def test_send_all_webhook_fixture_messages_for_success_with_non_json_fixtures(self) -> None:
        bot = get_user("*****@*****.**", self.zulip_realm)
        url = f"/api/v1/external/wordpress?api_key={bot.api_key}&stream=Denmark&topic=WordPress bulk notifications"
        target_url = "/devtools/integrations/send_all_webhook_fixture_messages"

        data = {
            "url": url,
            "custom_headers": "{}",
            "integration_name": "wordpress",
        }

        response = self.client_post(target_url, data)
        expected_responses = [
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "user_register.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "publish_post_no_data_provided.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "unknown_action_no_data.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "publish_page.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "unknown_action_no_hook_provided.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "publish_post_type_not_provided.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "wp_login.txt",
                "status_code": 400,
            },
            {
                "message": {
                    "msg": "Unknown WordPress webhook action: WordPress action",
                    "result": "error",
                    "code": "BAD_REQUEST",
                },
                "fixture_name": "publish_post.txt",
                "status_code": 400,
            },
        ]
        responses = orjson.loads(response.content)["responses"]
        for r in responses:
            r["message"] = orjson.loads(r["message"])
        self.assertEqual(response.status_code, 200)
        for r in responses:
            # We have to use this roundabout manner since the order may vary each time. This is not
            # an issue. Basically, we're trying to compare 2 lists and since we're not resorting to
            # using sets or a sorted order, we're sticking with O(n*m) time complexity for this
            # comparison (where n and m are the lengths of the two lists respectively). But since
            # this is just a unit test and more importantly n = m = some-low-number we don't really
            # care about the time complexity being what it is.
            self.assertTrue(r in expected_responses)
            expected_responses.remove(r)
Example #35
0
    def handle(self, *args: Any, **options: Any) -> None:
        # Suppress spammy output from the push notifications logger
        push_notifications_logger.disabled = True

        if options["percent_huddles"] + options["percent_personals"] > 100:
            self.stderr.write("Error!  More than 100% of messages allocated.\n")
            return

        # Get consistent data for backend tests.
        if options["test_suite"]:
            random.seed(0)

            with connection.cursor() as cursor:
                # Sometimes bugs relating to confusing recipient.id for recipient.type_id
                # or <object>.id for <object>.recipient_id remain undiscovered by the test suite
                # due to these numbers happening to coincide in such a way that it makes tests
                # accidentally pass. By bumping the Recipient.id sequence by a large enough number,
                # we can have those ids in a completely different range of values than object ids,
                # eliminatng the possibility of such coincidences.
                cursor.execute("SELECT setval('zerver_recipient_id_seq', 100)")

        # If max_topics is not set, we set it proportional to the
        # number of messages.
        if options["max_topics"] is None:
            options["max_topics"] = 1 + options["num_messages"] // 100

        if options["delete"]:
            # Start by clearing all the data in our database
            clear_database()

            # Create our three default realms
            # Could in theory be done via zerver.lib.actions.do_create_realm, but
            # welcome-bot (needed for do_create_realm) hasn't been created yet
            create_internal_realm()
            zulip_realm = do_create_realm(
                string_id="zulip",
                name="Zulip Dev",
                emails_restricted_to_domains=False,
                email_address_visibility=Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS,
                description="The Zulip development environment default organization."
                "  It's great for testing!",
                invite_required=False,
                plan_type=Realm.SELF_HOSTED,
                org_type=Realm.ORG_TYPES["business"]["id"],
            )
            RealmDomain.objects.create(realm=zulip_realm, domain="zulip.com")
            assert zulip_realm.notifications_stream is not None
            zulip_realm.notifications_stream.name = "Verona"
            zulip_realm.notifications_stream.description = "A city in Italy"
            zulip_realm.notifications_stream.save(update_fields=["name", "description"])

            if options["test_suite"]:
                mit_realm = do_create_realm(
                    string_id="zephyr",
                    name="MIT",
                    emails_restricted_to_domains=True,
                    invite_required=False,
                    plan_type=Realm.SELF_HOSTED,
                    org_type=Realm.ORG_TYPES["business"]["id"],
                )
                RealmDomain.objects.create(realm=mit_realm, domain="mit.edu")

                lear_realm = do_create_realm(
                    string_id="lear",
                    name="Lear & Co.",
                    emails_restricted_to_domains=False,
                    invite_required=False,
                    plan_type=Realm.SELF_HOSTED,
                    org_type=Realm.ORG_TYPES["business"]["id"],
                )

                # Default to allowing all members to send mentions in
                # large streams for the test suite to keep
                # mention-related tests simple.
                zulip_realm.wildcard_mention_policy = Realm.WILDCARD_MENTION_POLICY_MEMBERS
                zulip_realm.save(update_fields=["wildcard_mention_policy"])

            # Create test Users (UserProfiles are automatically created,
            # as are subscriptions to the ability to receive personals).
            names = [
                ("Zoe", "*****@*****.**"),
                ("Othello, the Moor of Venice", "*****@*****.**"),
                ("Iago", "*****@*****.**"),
                ("Prospero from The Tempest", "*****@*****.**"),
                ("Cordelia, Lear's daughter", "*****@*****.**"),
                ("King Hamlet", "*****@*****.**"),
                ("aaron", "*****@*****.**"),
                ("Polonius", "*****@*****.**"),
                ("Desdemona", "*****@*****.**"),
                ("शिव", "*****@*****.**"),
            ]

            # For testing really large batches:
            # Create extra users with semi realistic names to make search
            # functions somewhat realistic.  We'll still create 1000 users
            # like Extra222 User for some predicability.
            num_names = options["extra_users"]
            num_boring_names = 300

            for i in range(min(num_names, num_boring_names)):
                full_name = f"Extra{i:03} User"
                names.append((full_name, f"extrauser{i}@zulip.com"))

            if num_names > num_boring_names:
                fnames = [
                    "Amber",
                    "Arpita",
                    "Bob",
                    "Cindy",
                    "Daniela",
                    "Dan",
                    "Dinesh",
                    "Faye",
                    "François",
                    "George",
                    "Hank",
                    "Irene",
                    "James",
                    "Janice",
                    "Jenny",
                    "Jill",
                    "John",
                    "Kate",
                    "Katelyn",
                    "Kobe",
                    "Lexi",
                    "Manish",
                    "Mark",
                    "Matt",
                    "Mayna",
                    "Michael",
                    "Pete",
                    "Peter",
                    "Phil",
                    "Phillipa",
                    "Preston",
                    "Sally",
                    "Scott",
                    "Sandra",
                    "Steve",
                    "Stephanie",
                    "Vera",
                ]
                mnames = ["de", "van", "von", "Shaw", "T."]
                lnames = [
                    "Adams",
                    "Agarwal",
                    "Beal",
                    "Benson",
                    "Bonita",
                    "Davis",
                    "George",
                    "Harden",
                    "James",
                    "Jones",
                    "Johnson",
                    "Jordan",
                    "Lee",
                    "Leonard",
                    "Singh",
                    "Smith",
                    "Patel",
                    "Towns",
                    "Wall",
                ]
                non_ascii_names = [
                    "Günter",
                    "أحمد",
                    "Magnús",
                    "आशी",
                    "イツキ",
                    "语嫣",
                    "அருண்",
                    "Александр",
                    "José",
                ]
                # to imitate emoji insertions in usernames
                raw_emojis = ["😎", "😂", "🐱‍👤"]

            for i in range(num_boring_names, num_names):
                fname = random.choice(fnames) + str(i)
                full_name = fname
                if random.random() < 0.7:
                    if random.random() < 0.3:
                        full_name += " " + random.choice(non_ascii_names)
                    else:
                        full_name += " " + random.choice(mnames)
                    if random.random() < 0.1:
                        full_name += " {} ".format(random.choice(raw_emojis))
                    else:
                        full_name += " " + random.choice(lnames)
                email = fname.lower() + "@zulip.com"
                names.append((full_name, email))

            create_users(zulip_realm, names, tos_version=settings.TOS_VERSION)

            iago = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            do_change_user_role(iago, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
            iago.is_staff = True
            iago.save(update_fields=["is_staff"])

            # We need to create at least two test draft for Iago for the sake
            # of the cURL tests. Two since one will be deleted.
            Draft.objects.create(
                user_profile=iago,
                recipient=None,
                topic="Release Notes",
                content="Release 4.0 will contain ...",
                last_edit_time=datetime.now(),
            )
            Draft.objects.create(
                user_profile=iago,
                recipient=None,
                topic="Release Notes",
                content="Release 4.0 will contain many new features such as ... ",
                last_edit_time=datetime.now(),
            )

            desdemona = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            do_change_user_role(desdemona, UserProfile.ROLE_REALM_OWNER, acting_user=None)

            shiva = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            do_change_user_role(shiva, UserProfile.ROLE_MODERATOR, acting_user=None)

            guest_user = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            guest_user.role = UserProfile.ROLE_GUEST
            guest_user.save(update_fields=["role"])

            # These bots are directly referenced from code and thus
            # are needed for the test suite.
            zulip_realm_bots = [
                ("Zulip Error Bot", "*****@*****.**"),
                ("Zulip Default Bot", "*****@*****.**"),
            ]
            for i in range(options["extra_bots"]):
                zulip_realm_bots.append((f"Extra Bot {i}", f"extrabot{i}@zulip.com"))

            create_users(zulip_realm, zulip_realm_bots, bot_type=UserProfile.DEFAULT_BOT)

            zoe = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            zulip_webhook_bots = [
                ("Zulip Webhook Bot", "*****@*****.**"),
            ]
            # If a stream is not supplied in the webhook URL, the webhook
            # will (in some cases) send the notification as a PM to the
            # owner of the webhook bot, so bot_owner can't be None
            create_users(
                zulip_realm,
                zulip_webhook_bots,
                bot_type=UserProfile.INCOMING_WEBHOOK_BOT,
                bot_owner=zoe,
            )
            aaron = get_user_by_delivery_email("*****@*****.**", zulip_realm)

            zulip_outgoing_bots = [
                ("Outgoing Webhook", "*****@*****.**"),
            ]
            create_users(
                zulip_realm,
                zulip_outgoing_bots,
                bot_type=UserProfile.OUTGOING_WEBHOOK_BOT,
                bot_owner=aaron,
            )
            outgoing_webhook = get_user("*****@*****.**", zulip_realm)
            add_service(
                "outgoing-webhook",
                user_profile=outgoing_webhook,
                interface=Service.GENERIC,
                base_url="http://127.0.0.1:5002",
                token=generate_api_key(),
            )

            # Add the realm internal bots to each realm.
            create_if_missing_realm_internal_bots()

            # Create public streams.
            signups_stream = Realm.INITIAL_PRIVATE_STREAM_NAME

            stream_list = [
                "Verona",
                "Denmark",
                "Scotland",
                "Venice",
                "Rome",
                signups_stream,
            ]
            stream_dict: Dict[str, Dict[str, Any]] = {
                "Denmark": {"description": "A Scandinavian country"},
                "Scotland": {"description": "Located in the United Kingdom"},
                "Venice": {"description": "A northeastern Italian city"},
                "Rome": {"description": "Yet another Italian city", "is_web_public": True},
            }

            bulk_create_streams(zulip_realm, stream_dict)
            recipient_streams: List[int] = [
                Stream.objects.get(name=name, realm=zulip_realm).id for name in stream_list
            ]

            # Create subscriptions to streams.  The following
            # algorithm will give each of the users a different but
            # deterministic subset of the streams (given a fixed list
            # of users). For the test suite, we have a fixed list of
            # subscriptions to make sure test data is consistent
            # across platforms.

            subscriptions_list: List[Tuple[UserProfile, Recipient]] = []
            profiles: Sequence[UserProfile] = list(
                UserProfile.objects.select_related().filter(is_bot=False).order_by("email")
            )

            if options["test_suite"]:
                subscriptions_map = {
                    "*****@*****.**": ["Verona"],
                    "*****@*****.**": ["Verona"],
                    "*****@*****.**": ["Verona", "Denmark", signups_stream],
                    "*****@*****.**": [
                        "Verona",
                        "Denmark",
                        "Scotland",
                        signups_stream,
                    ],
                    "*****@*****.**": ["Verona", "Denmark", "Scotland"],
                    "*****@*****.**": ["Verona", "Denmark", "Scotland", "Venice"],
                    "*****@*****.**": ["Verona", "Denmark", "Scotland", "Venice", "Rome"],
                    "*****@*****.**": ["Verona"],
                    "*****@*****.**": [
                        "Verona",
                        "Denmark",
                        "Venice",
                        signups_stream,
                    ],
                    "*****@*****.**": ["Verona", "Denmark", "Scotland"],
                }

                for profile in profiles:
                    email = profile.delivery_email
                    if email not in subscriptions_map:
                        raise Exception(f"Subscriptions not listed for user {email}")

                    for stream_name in subscriptions_map[email]:
                        stream = Stream.objects.get(name=stream_name, realm=zulip_realm)
                        r = Recipient.objects.get(type=Recipient.STREAM, type_id=stream.id)
                        subscriptions_list.append((profile, r))
            else:
                num_streams = len(recipient_streams)
                num_users = len(profiles)
                for i, profile in enumerate(profiles):
                    # Subscribe to some streams.
                    fraction = float(i) / num_users
                    num_recips = int(num_streams * fraction) + 1

                    for type_id in recipient_streams[:num_recips]:
                        r = Recipient.objects.get(type=Recipient.STREAM, type_id=type_id)
                        subscriptions_list.append((profile, r))

            subscriptions_to_add: List[Subscription] = []
            event_time = timezone_now()
            all_subscription_logs: (List[RealmAuditLog]) = []

            i = 0
            for profile, recipient in subscriptions_list:
                i += 1
                color = STREAM_ASSIGNMENT_COLORS[i % len(STREAM_ASSIGNMENT_COLORS)]
                s = Subscription(
                    recipient=recipient,
                    user_profile=profile,
                    is_user_active=profile.is_active,
                    color=color,
                )

                subscriptions_to_add.append(s)

                log = RealmAuditLog(
                    realm=profile.realm,
                    modified_user=profile,
                    modified_stream_id=recipient.type_id,
                    event_last_message_id=0,
                    event_type=RealmAuditLog.SUBSCRIPTION_CREATED,
                    event_time=event_time,
                )
                all_subscription_logs.append(log)

            Subscription.objects.bulk_create(subscriptions_to_add)
            RealmAuditLog.objects.bulk_create(all_subscription_logs)

            # Create custom profile field data
            phone_number = try_add_realm_custom_profile_field(
                zulip_realm, "Phone number", CustomProfileField.SHORT_TEXT, hint=""
            )
            biography = try_add_realm_custom_profile_field(
                zulip_realm,
                "Biography",
                CustomProfileField.LONG_TEXT,
                hint="What are you known for?",
            )
            favorite_food = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite food",
                CustomProfileField.SHORT_TEXT,
                hint="Or drink, if you'd prefer",
            )
            field_data: ProfileFieldData = {
                "vim": {"text": "Vim", "order": "1"},
                "emacs": {"text": "Emacs", "order": "2"},
            }
            favorite_editor = try_add_realm_custom_profile_field(
                zulip_realm, "Favorite editor", CustomProfileField.SELECT, field_data=field_data
            )
            birthday = try_add_realm_custom_profile_field(
                zulip_realm, "Birthday", CustomProfileField.DATE
            )
            favorite_website = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite website",
                CustomProfileField.URL,
                hint="Or your personal blog's URL",
            )
            mentor = try_add_realm_custom_profile_field(
                zulip_realm, "Mentor", CustomProfileField.USER
            )
            github_profile = try_add_realm_default_custom_profile_field(zulip_realm, "github")

            # Fill in values for Iago and Hamlet
            hamlet = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            do_update_user_custom_profile_data_if_changed(
                iago,
                [
                    {"id": phone_number.id, "value": "+1-234-567-8901"},
                    {"id": biography.id, "value": "Betrayer of Othello."},
                    {"id": favorite_food.id, "value": "Apples"},
                    {"id": favorite_editor.id, "value": "emacs"},
                    {"id": birthday.id, "value": "2000-01-01"},
                    {"id": favorite_website.id, "value": "https://zulip.readthedocs.io/en/latest/"},
                    {"id": mentor.id, "value": [hamlet.id]},
                    {"id": github_profile.id, "value": "zulip"},
                ],
            )
            do_update_user_custom_profile_data_if_changed(
                hamlet,
                [
                    {"id": phone_number.id, "value": "+0-11-23-456-7890"},
                    {
                        "id": biography.id,
                        "value": "I am:\n* The prince of Denmark\n* Nephew to the usurping Claudius",
                    },
                    {"id": favorite_food.id, "value": "Dark chocolate"},
                    {"id": favorite_editor.id, "value": "vim"},
                    {"id": birthday.id, "value": "1900-01-01"},
                    {"id": favorite_website.id, "value": "https://blog.zulig.org"},
                    {"id": mentor.id, "value": [iago.id]},
                    {"id": github_profile.id, "value": "zulipbot"},
                ],
            )
        else:
            zulip_realm = get_realm("zulip")
            recipient_streams = [
                klass.type_id for klass in Recipient.objects.filter(type=Recipient.STREAM)
            ]

        # Extract a list of all users
        user_profiles: List[UserProfile] = list(UserProfile.objects.filter(is_bot=False))

        # Create a test realm emoji.
        IMAGE_FILE_PATH = static_path("images/test-images/checkbox.png")
        with open(IMAGE_FILE_PATH, "rb") as fp:
            check_add_realm_emoji(zulip_realm, "green_tick", iago, File(fp))

        if not options["test_suite"]:
            # Populate users with some bar data
            for user in user_profiles:
                status: int = UserPresence.ACTIVE
                date = timezone_now()
                client = get_client("website")
                if user.full_name[0] <= "H":
                    client = get_client("ZulipAndroid")
                UserPresence.objects.get_or_create(
                    user_profile=user,
                    realm_id=user.realm_id,
                    client=client,
                    timestamp=date,
                    status=status,
                )

        user_profiles_ids = [user_profile.id for user_profile in user_profiles]

        # Create several initial huddles
        for i in range(options["num_huddles"]):
            get_huddle(random.sample(user_profiles_ids, random.randint(3, 4)))

        # Create several initial pairs for personals
        personals_pairs = [
            random.sample(user_profiles_ids, 2) for i in range(options["num_personals"])
        ]

        create_alert_words(zulip_realm.id)

        # Generate a new set of test data.
        create_test_data()

        # prepopulate the URL preview/embed data for the links present
        # in the config.generate_data.json data set.  This makes it
        # possible for populate_db to run happily without Internet
        # access.
        with open("zerver/tests/fixtures/docs_url_preview_data.json", "rb") as f:
            urls_with_preview_data = orjson.loads(f.read())
            for url in urls_with_preview_data:
                cache_set(url, urls_with_preview_data[url], PREVIEW_CACHE_NAME)

        if options["delete"]:
            if options["test_suite"]:
                # Create test users; the MIT ones are needed to test
                # the Zephyr mirroring codepaths.
                testsuite_mit_users = [
                    ("Fred Sipb (MIT)", "*****@*****.**"),
                    ("Athena Consulting Exchange User (MIT)", "*****@*****.**"),
                    ("Esp Classroom (MIT)", "*****@*****.**"),
                ]
                create_users(mit_realm, testsuite_mit_users, tos_version=settings.TOS_VERSION)

                testsuite_lear_users = [
                    ("King Lear", "*****@*****.**"),
                    ("Cordelia, Lear's daughter", "*****@*****.**"),
                ]
                create_users(lear_realm, testsuite_lear_users, tos_version=settings.TOS_VERSION)

            if not options["test_suite"]:
                # To keep the messages.json fixtures file for the test
                # suite fast, don't add these users and subscriptions
                # when running populate_db for the test suite

                # to imitate emoji insertions in stream names
                raw_emojis = ["😎", "😂", "🐱‍👤"]

                zulip_stream_dict: Dict[str, Dict[str, Any]] = {
                    "devel": {"description": "For developing"},
                    # ビデオゲーム - VideoGames (japanese)
                    "ビデオゲーム": {
                        "description": "Share your favorite video games!  {}".format(raw_emojis[2])
                    },
                    "announce": {
                        "description": "For announcements",
                        "stream_post_policy": Stream.STREAM_POST_POLICY_ADMINS,
                    },
                    "design": {"description": "For design"},
                    "support": {"description": "For support"},
                    "social": {"description": "For socializing"},
                    "test": {"description": "For testing `code`"},
                    "errors": {"description": "For errors"},
                    # 조리법 - Recipes (Korean) , Пельмени - Dumplings (Russian)
                    "조리법 "
                    + raw_emojis[0]: {"description": "Everything cooking, from pasta to Пельмени"},
                }

                extra_stream_names = [
                    "802.11a",
                    "Ad Hoc Network",
                    "Augmented Reality",
                    "Cycling",
                    "DPI",
                    "FAQ",
                    "FiFo",
                    "commits",
                    "Control panel",
                    "desktop",
                    "компьютеры",
                    "Data security",
                    "desktop",
                    "काम",
                    "discussions",
                    "Cloud storage",
                    "GCI",
                    "Vaporware",
                    "Recent Trends",
                    "issues",
                    "live",
                    "Health",
                    "mobile",
                    "空間",
                    "provision",
                    "hidrógeno",
                    "HR",
                    "アニメ",
                ]

                # Add stream names and stream descriptions
                for i in range(options["extra_streams"]):
                    extra_stream_name = random.choice(extra_stream_names) + " " + str(i)

                    # to imitate emoji insertions in stream names
                    if random.random() <= 0.15:
                        extra_stream_name += random.choice(raw_emojis)

                    zulip_stream_dict[extra_stream_name] = {
                        "description": "Auto-generated extra stream.",
                    }

                bulk_create_streams(zulip_realm, zulip_stream_dict)
                # Now that we've created the notifications stream, configure it properly.
                zulip_realm.notifications_stream = get_stream("announce", zulip_realm)
                zulip_realm.save(update_fields=["notifications_stream"])

                # Add a few default streams
                for default_stream_name in ["design", "devel", "social", "support"]:
                    DefaultStream.objects.create(
                        realm=zulip_realm, stream=get_stream(default_stream_name, zulip_realm)
                    )

                # Now subscribe everyone to these streams
                subscribe_users_to_streams(zulip_realm, zulip_stream_dict)

            create_user_groups()

            if not options["test_suite"]:
                # We populate the analytics database here for
                # development purpose only
                call_command("populate_analytics_db")

        threads = options["threads"]
        jobs: List[Tuple[int, List[List[int]], Dict[str, Any], int]] = []
        for i in range(threads):
            count = options["num_messages"] // threads
            if i < options["num_messages"] % threads:
                count += 1
            jobs.append((count, personals_pairs, options, random.randint(0, 10 ** 10)))

        for job in jobs:
            generate_and_send_messages(job)

        if options["delete"]:
            if not options["test_suite"]:
                # These bots are not needed by the test suite
                # Also, we don't want interacting with each other
                # in dev setup.
                internal_zulip_users_nosubs = [
                    ("Zulip Commit Bot", "*****@*****.**"),
                    ("Zulip Trac Bot", "*****@*****.**"),
                    ("Zulip Nagios Bot", "*****@*****.**"),
                ]
                create_users(
                    zulip_realm, internal_zulip_users_nosubs, bot_type=UserProfile.DEFAULT_BOT
                )

            mark_all_messages_as_read()
            self.stdout.write("Successfully populated test database.\n")

        push_notifications_logger.disabled = False
Example #36
0
    def test_do_convert_data(self) -> None:
        mattermost_data_dir = self.fixture_file_name("", "mattermost_fixtures")
        output_dir = self.make_import_output_dir("mattermost")

        with patch("builtins.print") as mock_print, self.assertLogs(
                level="WARNING") as warn_log:
            do_convert_data(
                mattermost_data_dir=mattermost_data_dir,
                output_dir=output_dir,
                masking_content=False,
            )
        self.assertEqual(
            mock_print.mock_calls,
            [
                call("Generating data for", "gryffindor"),
                call("Generating data for", "slytherin")
            ],
        )
        self.assertEqual(
            warn_log.output,
            [
                "WARNING:root:Skipping importing huddles and PMs since there are multiple teams in the export",
                "WARNING:root:Skipping importing huddles and PMs since there are multiple teams in the export",
            ],
        )

        harry_team_output_dir = self.team_output_dir(output_dir, "gryffindor")
        self.assertEqual(
            os.path.exists(os.path.join(harry_team_output_dir, "avatars")),
            True)
        self.assertEqual(
            os.path.exists(os.path.join(harry_team_output_dir, "emoji")), True)
        self.assertEqual(
            os.path.exists(
                os.path.join(harry_team_output_dir, "attachment.json")), True)

        realm = self.read_file(harry_team_output_dir, "realm.json")

        self.assertEqual("Organization imported from Mattermost!",
                         realm["zerver_realm"][0]["description"])

        exported_user_ids = self.get_set(realm["zerver_userprofile"], "id")
        exported_user_full_names = self.get_set(realm["zerver_userprofile"],
                                                "full_name")
        self.assertEqual({"Harry Potter", "Ron Weasley", "Severus Snape"},
                         exported_user_full_names)

        exported_user_emails = self.get_set(realm["zerver_userprofile"],
                                            "email")
        self.assertEqual(
            {"*****@*****.**", "*****@*****.**", "*****@*****.**"},
            exported_user_emails)

        self.assert_length(realm["zerver_stream"], 3)
        exported_stream_names = self.get_set(realm["zerver_stream"], "name")
        self.assertEqual(
            exported_stream_names,
            {
                "Gryffindor common room", "Gryffindor quidditch team",
                "Dumbledores army"
            },
        )
        self.assertEqual(self.get_set(realm["zerver_stream"], "realm"),
                         {realm["zerver_realm"][0]["id"]})
        self.assertEqual(self.get_set(realm["zerver_stream"], "deactivated"),
                         {False})

        self.assert_length(realm["zerver_defaultstream"], 0)

        exported_recipient_ids = self.get_set(realm["zerver_recipient"], "id")
        self.assert_length(exported_recipient_ids, 6)
        exported_recipient_types = self.get_set(realm["zerver_recipient"],
                                                "type")
        self.assertEqual(exported_recipient_types, {1, 2})
        exported_recipient_type_ids = self.get_set(realm["zerver_recipient"],
                                                   "type_id")
        self.assert_length(exported_recipient_type_ids, 3)

        exported_subscription_userprofile = self.get_set(
            realm["zerver_subscription"], "user_profile")
        self.assert_length(exported_subscription_userprofile, 3)
        exported_subscription_recipients = self.get_set(
            realm["zerver_subscription"], "recipient")
        self.assert_length(exported_subscription_recipients, 6)

        messages = self.read_file(harry_team_output_dir,
                                  "messages-000001.json")

        exported_messages_id = self.get_set(messages["zerver_message"], "id")
        self.assertIn(messages["zerver_message"][0]["sender"],
                      exported_user_ids)
        self.assertIn(messages["zerver_message"][0]["recipient"],
                      exported_recipient_ids)
        self.assertIn(messages["zerver_message"][0]["content"],
                      "harry joined the channel.\n\n")

        exported_usermessage_userprofiles = self.get_set(
            messages["zerver_usermessage"], "user_profile")
        self.assert_length(exported_usermessage_userprofiles, 3)
        exported_usermessage_messages = self.get_set(
            messages["zerver_usermessage"], "message")
        self.assertEqual(exported_usermessage_messages, exported_messages_id)

        with self.assertLogs(level="INFO"):
            do_import_realm(
                import_dir=harry_team_output_dir,
                subdomain="gryffindor",
            )

        realm = get_realm("gryffindor")

        self.assertFalse(get_user("*****@*****.**", realm).is_mirror_dummy)
        self.assertFalse(get_user("*****@*****.**", realm).is_mirror_dummy)
        self.assertTrue(get_user("*****@*****.**", realm).is_mirror_dummy)

        messages = Message.objects.filter(sender__realm=realm)
        for message in messages:
            self.assertIsNotNone(message.rendered_content)
Example #37
0
 def example_user(self, name: str) -> UserProfile:
     email = self.example_user_map[name]
     return get_user(email, get_realm('zulip'))
Example #38
0
 def nonreg_user(self, name: str) -> UserProfile:
     email = self.nonreg_user_map[name]
     return get_user(email, get_realm("zulip"))
Example #39
0
 def test_user(self) -> UserProfile:
     return get_user(self.TEST_USER_EMAIL, get_realm("zulip"))
Example #40
0
def apply_event(state: Dict[str, Any],
                event: Dict[str, Any],
                user_profile: UserProfile,
                client_gravatar: bool,
                include_subscribers: bool) -> None:
    if event['type'] == "message":
        state['max_message_id'] = max(state['max_message_id'], event['message']['id'])
        if 'raw_unread_msgs' in state:
            apply_unread_message_event(
                user_profile,
                state['raw_unread_msgs'],
                event['message'],
                event['flags'],
            )

        # Below, we handle maintaining first_message_id.
        if event['message']['type'] != "stream":
            return

        for sub_dict in state['subscriptions']:
            if event['message']['stream_id'] == sub_dict['stream_id']:
                if sub_dict['first_message_id'] is None:
                    sub_dict['first_message_id'] = event['message']['id']
        for stream_dict in state['streams']:
            if event['message']['stream_id'] == stream_dict['stream_id']:
                if stream_dict['first_message_id'] is None:
                    stream_dict['first_message_id'] = event['message']['id']

    elif event['type'] == "hotspots":
        state['hotspots'] = event['hotspots']
    elif event['type'] == "custom_profile_fields":
        state['custom_profile_fields'] = event['fields']
    elif event['type'] == "pointer":
        state['pointer'] = max(state['pointer'], event['pointer'])
    elif event['type'] == "realm_user":
        person = event['person']
        person_user_id = person['user_id']

        if event['op'] == "add":
            person = copy.deepcopy(person)
            if client_gravatar:
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
            person['is_active'] = True
            if not person['is_bot']:
                person['profile_data'] = {}
            state['raw_users'][person_user_id] = person
        elif event['op'] == "remove":
            state['raw_users'][person_user_id]['is_active'] = False
        elif event['op'] == 'update':
            is_me = (person_user_id == user_profile.id)

            if is_me:
                if ('avatar_url' in person and 'avatar_url' in state):
                    state['avatar_source'] = person['avatar_source']
                    state['avatar_url'] = person['avatar_url']
                    state['avatar_url_medium'] = person['avatar_url_medium']

                for field in ['is_admin', 'delivery_email', 'email', 'full_name']:
                    if field in person and field in state:
                        state[field] = person[field]

                # In the unlikely event that the current user
                # just changed to/from being an admin, we need
                # to add/remove the data on all bots in the
                # realm.  This is ugly and probably better
                # solved by removing the all-realm-bots data
                # given to admin users from this flow.
                if ('is_admin' in person and 'realm_bots' in state):
                    prev_state = state['raw_users'][user_profile.id]
                    was_admin = prev_state['is_admin']
                    now_admin = person['is_admin']

                    if was_admin and not now_admin:
                        state['realm_bots'] = []
                    if not was_admin and now_admin:
                        state['realm_bots'] = get_owned_bot_dicts(user_profile)

            if client_gravatar and 'avatar_url' in person:
                # Respect the client_gravatar setting in the `users` data.
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
                    person['avatar_url_medium'] = None

            if person_user_id in state['raw_users']:
                p = state['raw_users'][person_user_id]
                for field in p:
                    if field in person:
                        p[field] = person[field]
                    if 'custom_profile_field' in person:
                        custom_field_id = person['custom_profile_field']['id']
                        custom_field_new_value = person['custom_profile_field']['value']
                        if 'rendered_value' in person['custom_profile_field']:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value,
                                'rendered_value': person['custom_profile_field']['rendered_value']
                            }
                        else:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value
                            }

    elif event['type'] == 'realm_bot':
        if event['op'] == 'add':
            state['realm_bots'].append(event['bot'])

        if event['op'] == 'remove':
            email = event['bot']['email']
            for bot in state['realm_bots']:
                if bot['email'] == email:
                    bot['is_active'] = False

        if event['op'] == 'delete':
            state['realm_bots'] = [item for item
                                   in state['realm_bots'] if item['email'] != event['bot']['email']]

        if event['op'] == 'update':
            for bot in state['realm_bots']:
                if bot['email'] == event['bot']['email']:
                    if 'owner_id' in event['bot']:
                        bot['owner'] = get_user_profile_by_id(event['bot']['owner_id']).email
                    else:
                        bot.update(event['bot'])

    elif event['type'] == 'stream':
        if event['op'] == 'create':
            for stream in event['streams']:
                if not stream['invite_only']:
                    stream_data = copy.deepcopy(stream)
                    if include_subscribers:
                        stream_data['subscribers'] = []
                    stream_data['stream_weekly_traffic'] = None
                    stream_data['is_old_stream'] = False
                    stream_data['is_announcement_only'] = False
                    # Add stream to never_subscribed (if not invite_only)
                    state['never_subscribed'].append(stream_data)
                state['streams'].append(stream)
            state['streams'].sort(key=lambda elt: elt["name"])

        if event['op'] == 'delete':
            deleted_stream_ids = {stream['stream_id'] for stream in event['streams']}
            state['streams'] = [s for s in state['streams'] if s['stream_id'] not in deleted_stream_ids]
            state['never_subscribed'] = [stream for stream in state['never_subscribed'] if
                                         stream['stream_id'] not in deleted_stream_ids]

        if event['op'] == 'update':
            # For legacy reasons, we call stream data 'subscriptions' in
            # the state var here, for the benefit of the JS code.
            for obj in state['subscriptions']:
                if obj['name'].lower() == event['name'].lower():
                    obj[event['property']] = event['value']
                    if event['property'] == "description":
                        obj['rendered_description'] = event['rendered_description']
            # Also update the pure streams data
            for stream in state['streams']:
                if stream['name'].lower() == event['name'].lower():
                    prop = event['property']
                    if prop in stream:
                        stream[prop] = event['value']
                        if prop == 'description':
                            stream['rendered_description'] = event['rendered_description']
        elif event['op'] == "occupy":
            state['streams'] += event['streams']
        elif event['op'] == "vacate":
            stream_ids = [s["stream_id"] for s in event['streams']]
            state['streams'] = [s for s in state['streams'] if s["stream_id"] not in stream_ids]
    elif event['type'] == 'default_streams':
        state['realm_default_streams'] = event['default_streams']
    elif event['type'] == 'default_stream_groups':
        state['realm_default_stream_groups'] = event['default_stream_groups']
    elif event['type'] == 'realm':
        if event['op'] == "update":
            field = 'realm_' + event['property']
            state[field] = event['value']

            # Tricky interaction: Whether we can create streams can get changed here.
            if (field in ['realm_create_stream_by_admins_only',
                          'realm_waiting_period_threshold']) and 'can_create_streams' in state:
                state['can_create_streams'] = user_profile.can_create_streams()
                state['can_subscribe_other_users'] = user_profile.can_subscribe_other_users()
        elif event['op'] == "update_dict":
            for key, value in event['data'].items():
                state['realm_' + key] = value
                # It's a bit messy, but this is where we need to
                # update the state for whether password authentication
                # is enabled on this server.
                if key == 'authentication_methods':
                    state['realm_password_auth_enabled'] = (value['Email'] or value['LDAP'])
                    state['realm_email_auth_enabled'] = value['Email']
    elif event['type'] == "subscription":
        if not include_subscribers and event['op'] in ['peer_add', 'peer_remove']:
            return

        if event['op'] in ["add"]:
            if not include_subscribers:
                # Avoid letting 'subscribers' entries end up in the list
                for i, sub in enumerate(event['subscriptions']):
                    event['subscriptions'][i] = copy.deepcopy(event['subscriptions'][i])
                    del event['subscriptions'][i]['subscribers']

        def name(sub: Dict[str, Any]) -> str:
            return sub['name'].lower()

        if event['op'] == "add":
            added_names = set(map(name, event["subscriptions"]))
            was_added = lambda s: name(s) in added_names

            # add the new subscriptions
            state['subscriptions'] += event['subscriptions']

            # remove them from unsubscribed if they had been there
            state['unsubscribed'] = [s for s in state['unsubscribed'] if not was_added(s)]

            # remove them from never_subscribed if they had been there
            state['never_subscribed'] = [s for s in state['never_subscribed'] if not was_added(s)]

        elif event['op'] == "remove":
            removed_names = set(map(name, event["subscriptions"]))
            was_removed = lambda s: name(s) in removed_names

            # Find the subs we are affecting.
            removed_subs = list(filter(was_removed, state['subscriptions']))

            # Remove our user from the subscribers of the removed subscriptions.
            if include_subscribers:
                for sub in removed_subs:
                    sub['subscribers'] = [id for id in sub['subscribers'] if id != user_profile.id]

            # We must effectively copy the removed subscriptions from subscriptions to
            # unsubscribe, since we only have the name in our data structure.
            state['unsubscribed'] += removed_subs

            # Now filter out the removed subscriptions from subscriptions.
            state['subscriptions'] = [s for s in state['subscriptions'] if not was_removed(s)]

        elif event['op'] == 'update':
            for sub in state['subscriptions']:
                if sub['name'].lower() == event['name'].lower():
                    sub[event['property']] = event['value']
        elif event['op'] == 'peer_add':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions'] and
                        user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
            for sub in state['never_subscribed']:
                if (sub['name'] in event['subscriptions'] and
                        user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
        elif event['op'] == 'peer_remove':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions'] and
                        user_id in sub['subscribers']):
                    sub['subscribers'].remove(user_id)
    elif event['type'] == "presence":
        # TODO: Add user_id to presence update events / state format!
        presence_user_profile = get_user(event['email'], user_profile.realm)
        state['presences'][event['email']] = UserPresence.get_status_dict_by_user(
            presence_user_profile)[event['email']]
    elif event['type'] == "update_message":
        # We don't return messages in /register, so we don't need to
        # do anything for content updates, but we may need to update
        # the unread_msgs data if the topic of an unread message changed.
        if TOPIC_NAME in event:
            stream_dict = state['raw_unread_msgs']['stream_dict']
            topic = event[TOPIC_NAME]
            for message_id in event['message_ids']:
                if message_id in stream_dict:
                    stream_dict[message_id]['topic'] = topic
    elif event['type'] == "delete_message":
        max_message = Message.objects.filter(
            usermessage__user_profile=user_profile).order_by('-id').first()
        if max_message:
            state['max_message_id'] = max_message.id
        else:
            state['max_message_id'] = -1

        remove_id = event['message_id']
        remove_message_id_from_unread_mgs(state, remove_id)
    elif event['type'] == "reaction":
        # The client will get the message with the reactions directly
        pass
    elif event['type'] == "submessage":
        # The client will get submessages with their messages
        pass
    elif event['type'] == 'typing':
        # Typing notification events are transient and thus ignored
        pass
    elif event['type'] == "attachment":
        # Attachment events are just for updating the "uploads" UI;
        # they are not sent directly.
        pass
    elif event['type'] == "update_message_flags":
        # We don't return messages in `/register`, so most flags we
        # can ignore, but we do need to update the unread_msgs data if
        # unread state is changed.
        if event['flag'] == 'read' and event['operation'] == 'add':
            for remove_id in event['messages']:
                remove_message_id_from_unread_mgs(state, remove_id)
        if event['flag'] == 'starred' and event['operation'] == 'add':
            state['starred_messages'] += event['messages']
        if event['flag'] == 'starred' and event['operation'] == 'remove':
            state['starred_messages'] = [message for message in state['starred_messages']
                                         if not (message in event['messages'])]
    elif event['type'] == "realm_domains":
        if event['op'] == 'add':
            state['realm_domains'].append(event['realm_domain'])
        elif event['op'] == 'change':
            for realm_domain in state['realm_domains']:
                if realm_domain['domain'] == event['realm_domain']['domain']:
                    realm_domain['allow_subdomains'] = event['realm_domain']['allow_subdomains']
        elif event['op'] == 'remove':
            state['realm_domains'] = [realm_domain for realm_domain in state['realm_domains']
                                      if realm_domain['domain'] != event['domain']]
    elif event['type'] == "realm_emoji":
        state['realm_emoji'] = event['realm_emoji']
    elif event['type'] == "alert_words":
        state['alert_words'] = event['alert_words']
    elif event['type'] == "muted_topics":
        state['muted_topics'] = event["muted_topics"]
    elif event['type'] == "realm_filters":
        state['realm_filters'] = event["realm_filters"]
    elif event['type'] == "update_display_settings":
        assert event['setting_name'] in UserProfile.property_types
        state[event['setting_name']] = event['setting']
    elif event['type'] == "update_global_notifications":
        assert event['notification_name'] in UserProfile.notification_setting_types
        state[event['notification_name']] = event['setting']
    elif event['type'] == "invites_changed":
        pass
    elif event['type'] == "user_group":
        if event['op'] == 'add':
            state['realm_user_groups'].append(event['group'])
            state['realm_user_groups'].sort(key=lambda group: group['id'])
        elif event['op'] == 'update':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group.update(event['data'])
        elif event['op'] == 'add_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group['members'].extend(event['user_ids'])
                    user_group['members'].sort()
        elif event['op'] == 'remove_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    members = set(user_group['members'])
                    user_group['members'] = list(members - set(event['user_ids']))
                    user_group['members'].sort()
        elif event['op'] == 'remove':
            state['realm_user_groups'] = [ug for ug in state['realm_user_groups']
                                          if ug['id'] != event['group_id']]
    elif event['type'] == 'user_status':
        user_id = event['user_id']
        user_status = state['user_status']
        away = event.get('away')
        status_text = event.get('status_text')

        if user_id not in user_status:
            user_status[user_id] = dict()

        if away is not None:
            if away:
                user_status[user_id]['away'] = True
            else:
                user_status[user_id].pop('away', None)

        if status_text is not None:
            if status_text == '':
                user_status[user_id].pop('status_text', None)
            else:
                user_status[user_id]['status_text'] = status_text

        if not user_status[user_id]:
            user_status.pop(user_id, None)

        state['user_status'] = user_status
    else:
        raise AssertionError("Unexpected event type %s" % (event['type'],))
Example #41
0
def apply_event(state, event, user_profile, include_subscribers):
    # type: (Dict[str, Any], Dict[str, Any], UserProfile, bool) -> None
    if event['type'] == "message":
        state['max_message_id'] = max(state['max_message_id'],
                                      event['message']['id'])
    elif event['type'] == "hotspots":
        state['hotspots'] = event['hotspots']
    elif event['type'] == "custom_profile_fields":
        state['custom_profile_fields'] = event['fields']
    elif event['type'] == "pointer":
        state['pointer'] = max(state['pointer'], event['pointer'])
    elif event['type'] == "realm_user":
        person = event['person']

        def our_person(p):
            # type: (Dict[str, Any]) -> bool
            return p['user_id'] == person['user_id']

        if event['op'] == "add":
            state['realm_users'].append(person)
        elif event['op'] == "remove":
            state['realm_users'] = [
                user for user in state['realm_users'] if not our_person(user)
            ]
        elif event['op'] == 'update':
            if (person['user_id'] == user_profile.id and 'avatar_url' in person
                    and 'avatar_url' in state):
                state['avatar_source'] = person['avatar_source']
                state['avatar_url'] = person['avatar_url']
                state['avatar_url_medium'] = person['avatar_url_medium']
            if 'avatar_source' in person:
                # Drop these so that they don't modify the
                # `realm_user` structure in the `p.update()` line
                # later; they're only used in the above lines
                del person['avatar_source']
                del person['avatar_url_medium']

            for field in ['is_admin', 'email', 'full_name']:
                if person[
                        'user_id'] == user_profile.id and field in person and field in state:
                    state[field] = person[field]

            for p in state['realm_users']:
                if our_person(p):
                    # In the unlikely event that the current user
                    # just changed to/from being an admin, we need
                    # to add/remove the data on all bots in the
                    # realm.  This is ugly and probably better
                    # solved by removing the all-realm-bots data
                    # given to admin users from this flow.
                    if ('is_admin' in person and 'realm_bots' in state
                            and user_profile.email == person['email']):
                        if p['is_admin'] and not person['is_admin']:
                            state['realm_bots'] = []
                        if not p['is_admin'] and person['is_admin']:
                            state['realm_bots'] = get_owned_bot_dicts(
                                user_profile)

                    # Now update the person
                    p.update(person)
    elif event['type'] == 'realm_bot':
        if event['op'] == 'add':
            state['realm_bots'].append(event['bot'])

        if event['op'] == 'remove':
            email = event['bot']['email']
            for bot in state['realm_bots']:
                if bot['email'] == email:
                    bot['is_active'] = False

        if event['op'] == 'update':
            for bot in state['realm_bots']:
                if bot['email'] == event['bot']['email']:
                    if 'owner_id' in event['bot']:
                        bot['owner'] = get_user_profile_by_id(
                            event['bot']['owner_id']).email
                    else:
                        bot.update(event['bot'])

    elif event['type'] == 'stream':
        if event['op'] == 'create':
            for stream in event['streams']:
                if not stream['invite_only']:
                    stream_data = copy.deepcopy(stream)
                    if include_subscribers:
                        stream_data['subscribers'] = []
                    # Add stream to never_subscribed (if not invite_only)
                    state['never_subscribed'].append(stream_data)
                state['streams'].append(stream)
            state['streams'].sort(key=lambda elt: elt["name"])

        if event['op'] == 'delete':
            deleted_stream_ids = {
                stream['stream_id']
                for stream in event['streams']
            }
            state['streams'] = [
                s for s in state['streams']
                if s['stream_id'] not in deleted_stream_ids
            ]
            state['never_subscribed'] = [
                stream for stream in state['never_subscribed']
                if stream['stream_id'] not in deleted_stream_ids
            ]

        if event['op'] == 'update':
            # For legacy reasons, we call stream data 'subscriptions' in
            # the state var here, for the benefit of the JS code.
            for obj in state['subscriptions']:
                if obj['name'].lower() == event['name'].lower():
                    obj[event['property']] = event['value']
            # Also update the pure streams data
            for stream in state['streams']:
                if stream['name'].lower() == event['name'].lower():
                    prop = event['property']
                    if prop in stream:
                        stream[prop] = event['value']
        elif event['op'] == "occupy":
            state['streams'] += event['streams']
        elif event['op'] == "vacate":
            stream_ids = [s["stream_id"] for s in event['streams']]
            state['streams'] = [
                s for s in state['streams'] if s["stream_id"] not in stream_ids
            ]
    elif event['type'] == 'default_streams':
        state['realm_default_streams'] = event['default_streams']
    elif event['type'] == 'realm':
        if event['op'] == "update":
            field = 'realm_' + event['property']
            state[field] = event['value']

            # Tricky interaction: Whether we can create streams can get changed here.
            if (field in [
                    'realm_create_stream_by_admins_only',
                    'realm_waiting_period_threshold'
            ]) and 'can_create_streams' in state:
                state['can_create_streams'] = user_profile.can_create_streams()
        elif event['op'] == "update_dict":
            for key, value in event['data'].items():
                state['realm_' + key] = value
                # It's a bit messy, but this is where we need to
                # update the state for whether password authentication
                # is enabled on this server.
                if key == 'authentication_methods':
                    state['realm_password_auth_enabled'] = (value['Email']
                                                            or value['LDAP'])
    elif event['type'] == "subscription":
        if not include_subscribers and event['op'] in [
                'peer_add', 'peer_remove'
        ]:
            return

        if event['op'] in ["add"]:
            if include_subscribers:
                # Convert the emails to user_profile IDs since that's what register() returns
                # TODO: Clean up this situation by making the event also have IDs
                for item in event["subscriptions"]:
                    item["subscribers"] = [
                        get_user(email, user_profile.realm).id
                        for email in item["subscribers"]
                    ]
            else:
                # Avoid letting 'subscribers' entries end up in the list
                for i, sub in enumerate(event['subscriptions']):
                    event['subscriptions'][i] = copy.deepcopy(
                        event['subscriptions'][i])
                    del event['subscriptions'][i]['subscribers']

        def name(sub):
            # type: (Dict[str, Any]) -> Text
            return sub['name'].lower()

        if event['op'] == "add":
            added_names = set(map(name, event["subscriptions"]))
            was_added = lambda s: name(s) in added_names

            # add the new subscriptions
            state['subscriptions'] += event['subscriptions']

            # remove them from unsubscribed if they had been there
            state['unsubscribed'] = [
                s for s in state['unsubscribed'] if not was_added(s)
            ]

            # remove them from never_subscribed if they had been there
            state['never_subscribed'] = [
                s for s in state['never_subscribed'] if not was_added(s)
            ]

        elif event['op'] == "remove":
            removed_names = set(map(name, event["subscriptions"]))
            was_removed = lambda s: name(s) in removed_names

            # Find the subs we are affecting.
            removed_subs = list(filter(was_removed, state['subscriptions']))

            # Remove our user from the subscribers of the removed subscriptions.
            if include_subscribers:
                for sub in removed_subs:
                    sub['subscribers'] = [
                        id for id in sub['subscribers']
                        if id != user_profile.id
                    ]

            # We must effectively copy the removed subscriptions from subscriptions to
            # unsubscribe, since we only have the name in our data structure.
            state['unsubscribed'] += removed_subs

            # Now filter out the removed subscriptions from subscriptions.
            state['subscriptions'] = [
                s for s in state['subscriptions'] if not was_removed(s)
            ]

        elif event['op'] == 'update':
            for sub in state['subscriptions']:
                if sub['name'].lower() == event['name'].lower():
                    sub[event['property']] = event['value']
        elif event['op'] == 'peer_add':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions']
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
            for sub in state['never_subscribed']:
                if (sub['name'] in event['subscriptions']
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
        elif event['op'] == 'peer_remove':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions']
                        and user_id in sub['subscribers']):
                    sub['subscribers'].remove(user_id)
    elif event['type'] == "presence":
        # TODO: Add user_id to presence update events / state format!
        presence_user_profile = get_user(event['email'], user_profile.realm)
        state['presences'][
            event['email']] = UserPresence.get_status_dict_by_user(
                presence_user_profile)[event['email']]
    elif event['type'] == "update_message":
        # The client will get the updated message directly
        pass
    elif event['type'] == "reaction":
        # The client will get the message with the reactions directly
        pass
    elif event['type'] == "referral":
        state['referrals'] = event['referrals']
    elif event['type'] == 'typing':
        # Typing notification events are transient and thus ignored
        pass
    elif event['type'] == "update_message_flags":
        # The client will get the message with the updated flags directly
        pass
    elif event['type'] == "realm_domains":
        if event['op'] == 'add':
            state['realm_domains'].append(event['realm_domain'])
        elif event['op'] == 'change':
            for realm_domain in state['realm_domains']:
                if realm_domain['domain'] == event['realm_domain']['domain']:
                    realm_domain['allow_subdomains'] = event['realm_domain'][
                        'allow_subdomains']
        elif event['op'] == 'remove':
            state['realm_domains'] = [
                realm_domain for realm_domain in state['realm_domains']
                if realm_domain['domain'] != event['domain']
            ]
    elif event['type'] == "realm_emoji":
        state['realm_emoji'] = event['realm_emoji']
    elif event['type'] == "alert_words":
        state['alert_words'] = event['alert_words']
    elif event['type'] == "muted_topics":
        state['muted_topics'] = event["muted_topics"]
    elif event['type'] == "realm_filters":
        state['realm_filters'] = event["realm_filters"]
    elif event['type'] == "update_display_settings":
        if event['setting_name'] == "twenty_four_hour_time":
            state['twenty_four_hour_time'] = event["setting"]
        if event['setting_name'] == 'left_side_userlist':
            state['left_side_userlist'] = event["setting"]
        if event['setting_name'] == 'emoji_alt_code':
            state['emoji_alt_code'] = event["setting"]
        if event['setting_name'] == 'emojiset':
            state['emojiset'] = event["setting"]
        if event['setting_name'] == 'default_language':
            state['default_language'] = event["setting"]
        if event['setting_name'] == 'timezone':
            state['timezone'] = event["setting"]
    elif event['type'] == "update_global_notifications":
        if event['notification_name'] == "enable_stream_desktop_notifications":
            state['enable_stream_desktop_notifications'] = event['setting']
        elif event['notification_name'] == "enable_stream_sounds":
            state['enable_stream_sounds'] = event['setting']
        elif event['notification_name'] == "enable_desktop_notifications":
            state['enable_desktop_notifications'] = event['setting']
        elif event['notification_name'] == "enable_sounds":
            state['enable_sounds'] = event['setting']
        elif event[
                'notification_name'] == "enable_offline_email_notifications":
            state['enable_offline_email_notifications'] = event['setting']
        elif event['notification_name'] == "enable_offline_push_notifications":
            state['enable_offline_push_notifications'] = event['setting']
        elif event['notification_name'] == "enable_online_push_notifications":
            state['enable_online_push_notifications'] = event['setting']
        elif event['notification_name'] == "enable_digest_emails":
            state['enable_digest_emails'] = event['setting']
        elif event[
                'notification_name'] == "pm_content_in_desktop_notifications":
            state['pm_content_in_desktop_notifications'] = event['setting']
    else:
        raise AssertionError("Unexpected event type %s" % (event['type'], ))
Example #42
0
 def unsubscribe_from_stream(self, email, stream_name, realm):
     # type: (Text, Text, Realm) -> None
     user_profile = get_user(email, realm)
     stream = get_stream(stream_name, realm)
     bulk_remove_subscriptions([user_profile], [stream])
Example #43
0
    def test_create_user_backend(self) -> None:

        # This test should give us complete coverage on
        # create_user_backend.  It mostly exercises error
        # conditions, and it also does a basic test of the success
        # path.

        admin = self.example_user('hamlet')
        admin_email = admin.email
        realm = admin.realm
        self.login(admin_email)
        do_change_is_admin(admin, True)

        result = self.client_post("/json/users", dict())
        self.assert_json_error(result, "Missing 'email' argument")

        result = self.client_post("/json/users",
                                  dict(email='*****@*****.**', ))
        self.assert_json_error(result, "Missing 'password' argument")

        result = self.client_post(
            "/json/users", dict(
                email='*****@*****.**',
                password='******',
            ))
        self.assert_json_error(result, "Missing 'full_name' argument")

        result = self.client_post(
            "/json/users",
            dict(
                email='*****@*****.**',
                password='******',
                full_name='Romeo Montague',
            ))
        self.assert_json_error(result, "Missing 'short_name' argument")

        result = self.client_post(
            "/json/users",
            dict(
                email='broken',
                password='******',
                full_name='Romeo Montague',
                short_name='Romeo',
            ))
        self.assert_json_error(result, "Bad name or username")

        result = self.client_post(
            "/json/users",
            dict(
                email='*****@*****.**',
                password='******',
                full_name='Romeo Montague',
                short_name='Romeo',
            ))
        self.assert_json_error(
            result,
            "Email '*****@*****.**' not allowed in this organization")

        RealmDomain.objects.create(realm=get_realm('zulip'),
                                   domain='zulip.net')
        valid_params = dict(
            email='*****@*****.**',
            password='******',
            full_name='Romeo Montague',
            short_name='Romeo',
        )
        result = self.client_post("/json/users", valid_params)
        self.assert_json_success(result)

        # Romeo is a newly registered user
        new_user = get_user('*****@*****.**', get_realm('zulip'))
        self.assertEqual(new_user.full_name, 'Romeo Montague')
        self.assertEqual(new_user.short_name, 'Romeo')

        # we can't create the same user twice.
        result = self.client_post("/json/users", valid_params)
        self.assert_json_error(result,
                               "Email '*****@*****.**' already in use")

        # Don't allow user to sign up with disposable email.
        realm.restricted_to_domain = False
        realm.disallow_disposable_email_addresses = True
        realm.save()

        valid_params["email"] = "*****@*****.**"
        result = self.client_post("/json/users", valid_params)
        self.assert_json_error(
            result,
            "Disposable email addresses are not allowed in this organization")
Example #44
0
 def nonreg_user(self, name):
     # type: (str) -> UserProfile
     email = self.nonreg_user_map[name]
     return get_user(email, get_realm_by_email_domain(email))
Example #45
0
def patch_bot_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    email: Text,
    full_name: Optional[Text] = REQ(default=None),
    bot_owner: Optional[Text] = REQ(default=None),
    default_sending_stream: Optional[Text] = REQ(default=None),
    default_events_register_stream: Optional[Text] = REQ(default=None),
    default_all_public_streams: Optional[bool] = REQ(default=None,
                                                     validator=check_bool)
) -> HttpResponse:
    try:
        bot = get_user(email, user_profile.realm)
    except UserProfile.DoesNotExist:
        return json_error(_('No such user'))

    if not user_profile.can_admin_user(bot):
        return json_error(_('Insufficient permission'))

    if full_name is not None:
        check_change_full_name(bot, full_name, user_profile)
    if bot_owner is not None:
        owner = get_user(bot_owner, user_profile.realm)
        do_change_bot_owner(bot, owner, user_profile)
    if default_sending_stream is not None:
        if default_sending_stream == "":
            stream = None  # type: Optional[Stream]
        else:
            (stream, recipient,
             sub) = access_stream_by_name(user_profile, default_sending_stream)
        do_change_default_sending_stream(bot, stream)
    if default_events_register_stream is not None:
        if default_events_register_stream == "":
            stream = None
        else:
            (stream, recipient,
             sub) = access_stream_by_name(user_profile,
                                          default_events_register_stream)
        do_change_default_events_register_stream(bot, stream)
    if default_all_public_streams is not None:
        do_change_default_all_public_streams(bot, default_all_public_streams)

    if len(request.FILES) == 0:
        pass
    elif len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        upload_avatar_image(user_file, user_profile, bot)
        avatar_source = UserProfile.AVATAR_FROM_USER
        do_change_avatar_fields(bot, avatar_source)
    else:
        return json_error(_("You may only upload one file at a time"))

    json_result = dict(
        full_name=bot.full_name,
        avatar_url=avatar_url(bot),
        default_sending_stream=get_stream_name(bot.default_sending_stream),
        default_events_register_stream=get_stream_name(
            bot.default_events_register_stream),
        default_all_public_streams=bot.default_all_public_streams,
    )

    # Don't include the bot owner in case it is not set.
    # Default bots have no owner.
    if bot.bot_owner is not None:
        json_result['bot_owner'] = bot.bot_owner.email

    return json_success(json_result)
Example #46
0
    def handle(self, **options: Any) -> None:
        if options["percent_huddles"] + options["percent_personals"] > 100:
            self.stderr.write(
                "Error!  More than 100% of messages allocated.\n")
            return

        if options["delete"]:
            # Start by clearing all the data in our database
            clear_database()

            # Create our two default realms
            # Could in theory be done via zerver.lib.actions.do_create_realm, but
            # welcome-bot (needed for do_create_realm) hasn't been created yet
            zulip_realm = Realm.objects.create(
                string_id="zulip",
                name="Zulip Dev",
                restricted_to_domain=True,
                description=
                "The Zulip development environment default organization."
                "  It's great for testing!",
                invite_required=False,
                org_type=Realm.CORPORATE)
            RealmDomain.objects.create(realm=zulip_realm, domain="zulip.com")
            if options["test_suite"]:
                mit_realm = Realm.objects.create(string_id="zephyr",
                                                 name="MIT",
                                                 restricted_to_domain=True,
                                                 invite_required=False,
                                                 org_type=Realm.CORPORATE)
                RealmDomain.objects.create(realm=mit_realm, domain="mit.edu")

                lear_realm = Realm.objects.create(string_id="lear",
                                                  name="Lear & Co.",
                                                  restricted_to_domain=False,
                                                  invite_required=False,
                                                  org_type=Realm.CORPORATE)

            # Create test Users (UserProfiles are automatically created,
            # as are subscriptions to the ability to receive personals).
            names = [
                ("Zoe", "*****@*****.**"),
                ("Othello, the Moor of Venice", "*****@*****.**"),
                ("Iago", "*****@*****.**"),
                ("Prospero from The Tempest", "*****@*****.**"),
                ("Cordelia Lear", "*****@*****.**"),
                ("King Hamlet", "*****@*****.**"),
                ("aaron", "*****@*****.**"),
                ("Polonius", "*****@*****.**"),
            ]
            for i in range(options["extra_users"]):
                names.append(
                    ('Extra User %d' % (i, ), '*****@*****.**' % (i, )))
            create_users(zulip_realm, names)

            iago = get_user("*****@*****.**", zulip_realm)
            do_change_is_admin(iago, True)
            iago.is_staff = True
            iago.save(update_fields=['is_staff'])

            guest_user = get_user("*****@*****.**", zulip_realm)
            guest_user.is_guest = True
            guest_user.save(update_fields=['is_guest'])

            # These bots are directly referenced from code and thus
            # are needed for the test suite.
            all_realm_bots = [
                (bot['name'],
                 bot['email_template'] % (settings.INTERNAL_BOT_DOMAIN, ))
                for bot in settings.INTERNAL_BOTS
            ]
            zulip_realm_bots = [
                ("Zulip New User Bot", "*****@*****.**"),
                ("Zulip Error Bot", "*****@*****.**"),
                ("Zulip Default Bot", "*****@*****.**"),
                ("Welcome Bot", "*****@*****.**"),
            ]

            for i in range(options["extra_bots"]):
                zulip_realm_bots.append(
                    ('Extra Bot %d' % (i, ), '*****@*****.**' % (i, )))
            zulip_realm_bots.extend(all_realm_bots)
            create_users(zulip_realm,
                         zulip_realm_bots,
                         bot_type=UserProfile.DEFAULT_BOT)

            zoe = get_user("*****@*****.**", zulip_realm)
            zulip_webhook_bots = [
                ("Zulip Webhook Bot", "*****@*****.**"),
            ]
            # If a stream is not supplied in the webhook URL, the webhook
            # will (in some cases) send the notification as a PM to the
            # owner of the webhook bot, so bot_owner can't be None
            create_users(zulip_realm,
                         zulip_webhook_bots,
                         bot_type=UserProfile.INCOMING_WEBHOOK_BOT,
                         bot_owner=zoe)
            aaron = get_user("*****@*****.**", zulip_realm)
            zulip_outgoing_bots = [("Outgoing Webhook",
                                    "*****@*****.**")]
            create_users(zulip_realm,
                         zulip_outgoing_bots,
                         bot_type=UserProfile.OUTGOING_WEBHOOK_BOT,
                         bot_owner=aaron)
            # TODO: Clean up this initial bot creation code
            Service.objects.create(
                name="test",
                user_profile=get_user("*****@*****.**",
                                      zulip_realm),
                base_url="http://127.0.0.1:5002/bots/followup",
                token="abcd1234",
                interface=1)

            # Create public streams.
            stream_list = ["Verona", "Denmark", "Scotland", "Venice", "Rome"]
            stream_dict = {
                "Verona": {
                    "description": "A city in Italy",
                    "invite_only": False
                },
                "Denmark": {
                    "description": "A Scandinavian country",
                    "invite_only": False
                },
                "Scotland": {
                    "description": "Located in the United Kingdom",
                    "invite_only": False
                },
                "Venice": {
                    "description": "A northeastern Italian city",
                    "invite_only": False
                },
                "Rome": {
                    "description": "Yet another Italian city",
                    "invite_only": False
                }
            }  # type: Dict[Text, Dict[Text, Any]]

            bulk_create_streams(zulip_realm, stream_dict)
            recipient_streams = [
                Stream.objects.get(name=name, realm=zulip_realm).id
                for name in stream_list
            ]  # type: List[int]
            # Create subscriptions to streams.  The following
            # algorithm will give each of the users a different but
            # deterministic subset of the streams (given a fixed list
            # of users).
            subscriptions_to_add = []  # type: List[Subscription]
            event_time = timezone_now()
            all_subscription_logs = []  # type: (List[RealmAuditLog])
            profiles = UserProfile.objects.select_related().filter(
                is_bot=False, is_guest=False).order_by(
                    "email")  # type: Sequence[UserProfile]
            for i, profile in enumerate(profiles):
                # Subscribe to some streams.
                for type_id in recipient_streams[:int(
                        len(recipient_streams) * float(i) / len(profiles)) +
                                                 1]:
                    r = Recipient.objects.get(type=Recipient.STREAM,
                                              type_id=type_id)
                    s = Subscription(recipient=r,
                                     user_profile=profile,
                                     color=STREAM_ASSIGNMENT_COLORS[
                                         i % len(STREAM_ASSIGNMENT_COLORS)])

                    subscriptions_to_add.append(s)

                    log = RealmAuditLog(realm=profile.realm,
                                        modified_user=profile,
                                        modified_stream_id=type_id,
                                        event_last_message_id=0,
                                        event_type='subscription_created',
                                        event_time=event_time)
                    all_subscription_logs.append(log)

            Subscription.objects.bulk_create(subscriptions_to_add)
            RealmAuditLog.objects.bulk_create(all_subscription_logs)

            # Create custom profile field data
            phone_number = try_add_realm_custom_profile_field(
                zulip_realm,
                "Phone number",
                CustomProfileField.SHORT_TEXT,
                hint='')
            biography = try_add_realm_custom_profile_field(
                zulip_realm,
                "Biography",
                CustomProfileField.LONG_TEXT,
                hint='What are you known for?')
            favorite_food = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite food",
                CustomProfileField.SHORT_TEXT,
                hint="Or drink, if you'd prefer")

            # Fill in values for Iago and Hamlet
            hamlet = get_user("*****@*****.**", zulip_realm)
            do_update_user_custom_profile_data(iago, [
                {
                    "id": phone_number.id,
                    "value": "+1-234-567-8901"
                },
                {
                    "id": biography.id,
                    "value": "Betrayer of Othello."
                },
                {
                    "id": favorite_food.id,
                    "value": "Apples"
                },
            ])
            do_update_user_custom_profile_data(hamlet, [
                {
                    "id": phone_number.id,
                    "value": "+0-11-23-456-7890"
                },
                {
                    "id": biography.id,
                    "value": "Prince of Denmark, and other things!"
                },
                {
                    "id": favorite_food.id,
                    "value": "Dark chocolate"
                },
            ])
        else:
            zulip_realm = get_realm("zulip")
            recipient_streams = [
                klass.type_id
                for klass in Recipient.objects.filter(type=Recipient.STREAM)
            ]

        # Extract a list of all users
        user_profiles = list(UserProfile.objects.filter(
            is_bot=False))  # type: List[UserProfile]

        # Create a test realm emoji.
        IMAGE_FILE_PATH = os.path.join(settings.STATIC_ROOT, 'images',
                                       'test-images', 'checkbox.png')
        with open(IMAGE_FILE_PATH, 'rb') as fp:
            check_add_realm_emoji(zulip_realm, 'green_tick', iago, fp)

        if not options["test_suite"]:
            # Populate users with some bar data
            for user in user_profiles:
                status = UserPresence.ACTIVE  # type: int
                date = timezone_now()
                client = get_client("website")
                if user.full_name[0] <= 'H':
                    client = get_client("ZulipAndroid")
                UserPresence.objects.get_or_create(user_profile=user,
                                                   client=client,
                                                   timestamp=date,
                                                   status=status)

        user_profiles_ids = [user_profile.id for user_profile in user_profiles]

        # Create several initial huddles
        for i in range(options["num_huddles"]):
            get_huddle(random.sample(user_profiles_ids, random.randint(3, 4)))

        # Create several initial pairs for personals
        personals_pairs = [
            random.sample(user_profiles_ids, 2)
            for i in range(options["num_personals"])
        ]

        # Generate a new set of test data.
        create_test_data()

        # prepopulate the URL preview/embed data for the links present
        # in the config.generate_data.json data set.  This makes it
        # possible for populate_db to run happily without Internet
        # access.
        with open("zerver/tests/fixtures/docs_url_preview_data.json",
                  "r") as f:
            urls_with_preview_data = ujson.load(f)
            for url in urls_with_preview_data:
                cache_set(url, urls_with_preview_data[url], PREVIEW_CACHE_NAME)

        threads = options["threads"]
        jobs = [
        ]  # type: List[Tuple[int, List[List[int]], Dict[str, Any], Callable[[str], int], int]]
        for i in range(threads):
            count = options["num_messages"] // threads
            if i < options["num_messages"] % threads:
                count += 1
            jobs.append((count, personals_pairs, options, self.stdout.write,
                         random.randint(0, 10**10)))

        for job in jobs:
            send_messages(job)

        if options["delete"]:
            # Create the "website" and "API" clients; if we don't, the
            # default values in zerver/decorators.py will not work
            # with the Django test suite.
            get_client("website")
            get_client("API")

            if options["test_suite"]:
                # Create test users; the MIT ones are needed to test
                # the Zephyr mirroring codepaths.
                testsuite_mit_users = [
                    ("Fred Sipb (MIT)", "*****@*****.**"),
                    ("Athena Consulting Exchange User (MIT)",
                     "*****@*****.**"),
                    ("Esp Classroom (MIT)", "*****@*****.**"),
                ]
                create_users(mit_realm, testsuite_mit_users)

                testsuite_lear_users = [
                    ("King Lear", "*****@*****.**"),
                    ("Cordelia Lear", "*****@*****.**"),
                ]
                create_users(lear_realm, testsuite_lear_users)

            if not options["test_suite"]:
                # Initialize the email gateway bot as an API Super User
                email_gateway_bot = get_system_bot(settings.EMAIL_GATEWAY_BOT)
                email_gateway_bot.is_api_super_user = True
                email_gateway_bot.save()

                # To keep the messages.json fixtures file for the test
                # suite fast, don't add these users and subscriptions
                # when running populate_db for the test suite

                zulip_stream_dict = {
                    "devel": {
                        "description": "For developing",
                        "invite_only": False
                    },
                    "all": {
                        "description": "For everything",
                        "invite_only": False
                    },
                    "announce": {
                        "description": "For announcements",
                        "invite_only": False
                    },
                    "design": {
                        "description": "For design",
                        "invite_only": False
                    },
                    "support": {
                        "description": "For support",
                        "invite_only": False
                    },
                    "social": {
                        "description": "For socializing",
                        "invite_only": False
                    },
                    "test": {
                        "description": "For testing",
                        "invite_only": False
                    },
                    "errors": {
                        "description": "For errors",
                        "invite_only": False
                    },
                    "sales": {
                        "description": "For sales discussion",
                        "invite_only": False
                    }
                }  # type: Dict[Text, Dict[Text, Any]]

                # Calculate the maximum number of digits in any extra stream's
                # number, since a stream with name "Extra Stream 3" could show
                # up after "Extra Stream 29". (Used later to pad numbers with
                # 0s).
                maximum_digits = len(str(options['extra_streams'] - 1))

                for i in range(options['extra_streams']):
                    # Pad the number with 0s based on `maximum_digits`.
                    number_str = str(i).zfill(maximum_digits)

                    extra_stream_name = 'Extra Stream ' + number_str

                    zulip_stream_dict[extra_stream_name] = {
                        "description": "Auto-generated extra stream.",
                        "invite_only": False,
                    }

                bulk_create_streams(zulip_realm, zulip_stream_dict)
                # Now that we've created the notifications stream, configure it properly.
                zulip_realm.notifications_stream = get_stream(
                    "announce", zulip_realm)
                zulip_realm.save(update_fields=['notifications_stream'])

                # Add a few default streams
                for default_stream_name in [
                        "design", "devel", "social", "support"
                ]:
                    DefaultStream.objects.create(realm=zulip_realm,
                                                 stream=get_stream(
                                                     default_stream_name,
                                                     zulip_realm))

                # Now subscribe everyone to these streams
                subscriptions_to_add = []
                event_time = timezone_now()
                all_subscription_logs = []
                profiles = UserProfile.objects.select_related().filter(
                    realm=zulip_realm)
                for i, stream_name in enumerate(zulip_stream_dict):
                    stream = Stream.objects.get(name=stream_name,
                                                realm=zulip_realm)
                    recipient = Recipient.objects.get(type=Recipient.STREAM,
                                                      type_id=stream.id)
                    for profile in profiles:
                        # Subscribe to some streams.
                        s = Subscription(
                            recipient=recipient,
                            user_profile=profile,
                            color=STREAM_ASSIGNMENT_COLORS[
                                i % len(STREAM_ASSIGNMENT_COLORS)])
                        subscriptions_to_add.append(s)

                        log = RealmAuditLog(realm=profile.realm,
                                            modified_user=profile,
                                            modified_stream=stream,
                                            event_last_message_id=0,
                                            event_type='subscription_created',
                                            event_time=event_time)
                        all_subscription_logs.append(log)
                Subscription.objects.bulk_create(subscriptions_to_add)
                RealmAuditLog.objects.bulk_create(all_subscription_logs)

                # These bots are not needed by the test suite
                internal_zulip_users_nosubs = [
                    ("Zulip Commit Bot", "*****@*****.**"),
                    ("Zulip Trac Bot", "*****@*****.**"),
                    ("Zulip Nagios Bot", "*****@*****.**"),
                ]
                create_users(zulip_realm,
                             internal_zulip_users_nosubs,
                             bot_type=UserProfile.DEFAULT_BOT)

            zulip_cross_realm_bots = [
                ("Zulip Feedback Bot", "*****@*****.**"),
            ]
            create_users(zulip_realm,
                         zulip_cross_realm_bots,
                         bot_type=UserProfile.DEFAULT_BOT)

            # Mark all messages as read
            UserMessage.objects.all().update(flags=UserMessage.flags.read)

            if not options["test_suite"]:
                # Update pointer of each user to point to the last message in their
                # UserMessage rows with sender_id=user_profile_id.
                users = list(
                    UserMessage.objects.filter(message__sender_id=F(
                        'user_profile_id')).values('user_profile_id').annotate(
                            pointer=Max('message_id')))
                for user in users:
                    UserProfile.objects.filter(
                        id=user['user_profile_id']).update(
                            pointer=user['pointer'])

            create_user_groups()
            self.stdout.write("Successfully populated test database.\n")
Example #47
0
def generate_all_emails(request: HttpRequest) -> HttpResponse:
    if not settings.TEST_SUITE:  # nocoverage
        # It's really convenient to automatically inline the email CSS
        # here, since that saves a step when testing out changes to
        # the email CSS.  But we don't run this inside the test suite,
        # because by role, the tests shouldn't be doing a provision-like thing.
        subprocess.check_call(["./tools/inline-email-css"])

    # We import the Django test client inside the view function,
    # because it isn't needed in production elsewhere, and not
    # importing it saves ~50ms of unnecessary manage.py startup time.

    from django.test import Client
    client = Client()

    # write fake data for all variables
    registered_email = "*****@*****.**"
    unregistered_email_1 = "*****@*****.**"
    unregistered_email_2 = "*****@*****.**"
    realm = get_realm("zulip")
    host_kwargs = {'HTTP_HOST': realm.host}

    # Password reset email
    result = client.post('/accounts/password/reset/', {'email': registered_email}, **host_kwargs)
    assert result.status_code == 302

    # Confirm account email
    result = client.post('/accounts/home/', {'email': unregistered_email_1}, **host_kwargs)
    assert result.status_code == 302

    # Find account email
    result = client.post('/accounts/find/', {'emails': registered_email}, **host_kwargs)
    assert result.status_code == 302

    # New login email
    logged_in = client.login(dev_auth_username=registered_email, realm=realm)
    assert logged_in

    # New user invite and reminder emails
    result = client.post("/json/invites",
                         {"invitee_emails": unregistered_email_2, "stream": ["Denmark"]},
                         **host_kwargs)
    assert result.status_code == 200

    # Verification for new email
    result = client.patch('/json/settings',
                          urllib.parse.urlencode({'email': '*****@*****.**'}),
                          **host_kwargs)
    assert result.status_code == 200

    # Email change successful
    key = Confirmation.objects.filter(type=Confirmation.EMAIL_CHANGE).latest('id').confirmation_key
    url = confirmation_url(key, realm.host, Confirmation.EMAIL_CHANGE)
    user_profile = get_user(registered_email, realm)
    result = client.get(url)
    assert result.status_code == 200

    # Reset the email value so we can run this again
    user_profile.email = registered_email
    user_profile.save(update_fields=['email'])

    # Follow up day1 day2 emails for normal user
    enqueue_welcome_emails(user_profile)

    # Follow up day1 day2 emails for admin user
    enqueue_welcome_emails(get_user("*****@*****.**", realm), realm_creation=True)

    return redirect(email_page)
Example #48
0
    def test_do_convert_data(self) -> None:
        mattermost_data_dir = self.fixture_file_name("", "mattermost_fixtures")
        output_dir = self.make_import_output_dir("mattermost")

        do_convert_data(
            mattermost_data_dir=mattermost_data_dir,
            output_dir=output_dir,
            masking_content=False,
        )

        harry_team_output_dir = self.team_output_dir(output_dir, "gryffindor")
        self.assertEqual(
            os.path.exists(os.path.join(harry_team_output_dir, 'avatars')),
            True)
        self.assertEqual(
            os.path.exists(os.path.join(harry_team_output_dir, 'emoji')), True)
        self.assertEqual(
            os.path.exists(
                os.path.join(harry_team_output_dir, 'attachment.json')), True)

        realm = self.read_file(harry_team_output_dir, 'realm.json')

        self.assertEqual('Organization imported from Mattermost!',
                         realm['zerver_realm'][0]['description'])

        exported_user_ids = self.get_set(realm['zerver_userprofile'], 'id')
        exported_user_full_names = self.get_set(realm['zerver_userprofile'],
                                                'full_name')
        self.assertEqual({'Harry Potter', 'Ron Weasley', 'Severus Snape'},
                         exported_user_full_names)

        exported_user_emails = self.get_set(realm['zerver_userprofile'],
                                            'email')
        self.assertEqual(
            {'*****@*****.**', '*****@*****.**', '*****@*****.**'},
            exported_user_emails)

        self.assertEqual(len(realm['zerver_stream']), 3)
        exported_stream_names = self.get_set(realm['zerver_stream'], 'name')
        self.assertEqual(
            exported_stream_names, {
                'Gryffindor common room', 'Gryffindor quidditch team',
                'Dumbledores army'
            })
        self.assertEqual(self.get_set(realm['zerver_stream'], 'realm'),
                         {realm['zerver_realm'][0]['id']})
        self.assertEqual(self.get_set(realm['zerver_stream'], 'deactivated'),
                         {False})

        self.assertEqual(len(realm['zerver_defaultstream']), 0)

        exported_recipient_ids = self.get_set(realm['zerver_recipient'], 'id')
        self.assertEqual(len(exported_recipient_ids), 6)
        exported_recipient_types = self.get_set(realm['zerver_recipient'],
                                                'type')
        self.assertEqual(exported_recipient_types, {1, 2})
        exported_recipient_type_ids = self.get_set(realm['zerver_recipient'],
                                                   'type_id')
        self.assertEqual(len(exported_recipient_type_ids), 3)

        exported_subscription_userprofile = self.get_set(
            realm['zerver_subscription'], 'user_profile')
        self.assertEqual(len(exported_subscription_userprofile), 3)
        exported_subscription_recipients = self.get_set(
            realm['zerver_subscription'], 'recipient')
        self.assertEqual(len(exported_subscription_recipients), 6)

        messages = self.read_file(harry_team_output_dir,
                                  'messages-000001.json')

        exported_messages_id = self.get_set(messages['zerver_message'], 'id')
        self.assertIn(messages['zerver_message'][0]['sender'],
                      exported_user_ids)
        self.assertIn(messages['zerver_message'][0]['recipient'],
                      exported_recipient_ids)
        self.assertIn(messages['zerver_message'][0]['content'],
                      'harry joined the channel.\n\n')

        exported_usermessage_userprofiles = self.get_set(
            messages['zerver_usermessage'], 'user_profile')
        self.assertEqual(len(exported_usermessage_userprofiles), 3)
        exported_usermessage_messages = self.get_set(
            messages['zerver_usermessage'], 'message')
        self.assertEqual(exported_usermessage_messages, exported_messages_id)

        do_import_realm(
            import_dir=harry_team_output_dir,
            subdomain='gryffindor',
        )
        realm = get_realm('gryffindor')

        self.assertFalse(get_user("*****@*****.**", realm).is_mirror_dummy)
        self.assertFalse(get_user("*****@*****.**", realm).is_mirror_dummy)
        self.assertTrue(get_user("*****@*****.**", realm).is_mirror_dummy)

        messages = Message.objects.filter(sender__realm=realm)
        for message in messages:
            self.assertIsNotNone(message.rendered_content)
Example #49
0
    def test_people(self) -> None:
        hamlet = self.example_user('hamlet')
        realm = get_realm('zulip')
        self.login_user(hamlet)

        bots = {}
        for i in range(3):
            bots[i] = self.create_bot(
                owner=hamlet,
                bot_email='*****@*****.**' % (i, ),
                bot_name='Bot %d' % (i, ),
            )

        for i in range(3):
            defunct_user = self.create_non_active_user(
                realm=realm,
                email='*****@*****.**' % (i, ),
                name='Defunct User %d' % (i, ),
            )

        result = self._get_home_page()
        page_params = self._get_page_params(result)
        '''
        We send three lists of users.  The first two below are disjoint
        lists of users, and the records we send for them have identical
        structure.

        The realm_bots bucket is somewhat redundant, since all bots will
        be in one of the first two buckets.  They do include fields, however,
        that normal users don't care about, such as default_sending_stream.
        '''

        buckets = [
            'realm_users',
            'realm_non_active_users',
            'realm_bots',
        ]

        for field in buckets:
            users = page_params[field]
            self.assertTrue(len(users) >= 3, field)
            for rec in users:
                self.assertEqual(rec['user_id'],
                                 get_user(rec['email'], realm).id)
                if field == 'realm_bots':
                    self.assertNotIn('is_bot', rec)
                    self.assertIn('is_active', rec)
                    self.assertIn('owner_id', rec)
                else:
                    self.assertIn('is_bot', rec)
                    self.assertNotIn('is_active', rec)

        active_ids = {p['user_id'] for p in page_params['realm_users']}
        non_active_ids = {
            p['user_id']
            for p in page_params['realm_non_active_users']
        }
        bot_ids = {p['user_id'] for p in page_params['realm_bots']}

        self.assertIn(hamlet.id, active_ids)
        self.assertIn(defunct_user.id, non_active_ids)

        # Bots can show up in multiple buckets.
        self.assertIn(bots[2].id, bot_ids)
        self.assertIn(bots[2].id, active_ids)

        # Make sure nobody got mis-bucketed.
        self.assertNotIn(hamlet.id, non_active_ids)
        self.assertNotIn(defunct_user.id, active_ids)

        cross_bots = page_params['cross_realm_bots']
        self.assertEqual(len(cross_bots), 3)
        cross_bots.sort(key=lambda d: d['email'])
        for cross_bot in cross_bots:
            # These are either nondeterministic or boring
            del cross_bot['timezone']
            del cross_bot['avatar_url']
            del cross_bot['date_joined']

        notification_bot = self.notification_bot()
        email_gateway_bot = get_system_bot(settings.EMAIL_GATEWAY_BOT)
        welcome_bot = get_system_bot(settings.WELCOME_BOT)

        by_email = lambda d: d['email']

        self.assertEqual(
            sorted(cross_bots, key=by_email),
            sorted([
                dict(avatar_version=email_gateway_bot.avatar_version,
                     bot_owner_id=None,
                     bot_type=1,
                     email=email_gateway_bot.email,
                     user_id=email_gateway_bot.id,
                     full_name=email_gateway_bot.full_name,
                     is_active=True,
                     is_bot=True,
                     is_admin=False,
                     is_cross_realm_bot=True,
                     is_guest=False),
                dict(avatar_version=email_gateway_bot.avatar_version,
                     bot_owner_id=None,
                     bot_type=1,
                     email=notification_bot.email,
                     user_id=notification_bot.id,
                     full_name=notification_bot.full_name,
                     is_active=True,
                     is_bot=True,
                     is_admin=False,
                     is_cross_realm_bot=True,
                     is_guest=False),
                dict(avatar_version=email_gateway_bot.avatar_version,
                     bot_owner_id=None,
                     bot_type=1,
                     email=welcome_bot.email,
                     user_id=welcome_bot.id,
                     full_name=welcome_bot.full_name,
                     is_active=True,
                     is_bot=True,
                     is_admin=False,
                     is_cross_realm_bot=True,
                     is_guest=False),
            ],
                   key=by_email))
Example #50
0
 def notification_bot(self) -> UserProfile:
     return get_user('*****@*****.**', get_realm('zulip'))
Example #51
0
def accounts_register(request: HttpRequest) -> HttpResponse:
    key = request.POST['key']
    confirmation = Confirmation.objects.get(confirmation_key=key)
    prereg_user = confirmation.content_object
    email = prereg_user.email
    realm_creation = prereg_user.realm_creation
    password_required = prereg_user.password_required
    is_realm_admin = prereg_user.invited_as_admin or realm_creation

    validators.validate_email(email)
    if realm_creation:
        # For creating a new realm, there is no existing realm or domain
        realm = None
    else:
        realm = get_realm(get_subdomain(request))
        if realm is None or realm != prereg_user.realm:
            return render_confirmation_key_error(
                request,
                ConfirmationKeyException(
                    ConfirmationKeyException.DOES_NOT_EXIST))

        if not email_allowed_for_realm(email, realm):
            return render(request,
                          "zerver/closed_realm.html",
                          context={"closed_domain_name": realm.name})

        if realm.deactivated:
            # The user is trying to register for a deactivated realm. Advise them to
            # contact support.
            return redirect_to_deactivation_notice()

        try:
            validate_email_for_realm(realm, email)
        except ValidationError:  # nocoverage # We need to add a test for this.
            return HttpResponseRedirect(
                reverse('django.contrib.auth.views.login') + '?email=' +
                urllib.parse.quote_plus(email))

    name_validated = False
    full_name = None

    if request.POST.get('from_confirmation'):
        try:
            del request.session['authenticated_full_name']
        except KeyError:
            pass
        if realm is not None and realm.is_zephyr_mirror_realm:
            # For MIT users, we can get an authoritative name from Hesiod.
            # Technically we should check that this is actually an MIT
            # realm, but we can cross that bridge if we ever get a non-MIT
            # zephyr mirroring realm.
            hesiod_name = compute_mit_user_fullname(email)
            form = RegistrationForm(initial={
                'full_name':
                hesiod_name if "@" not in hesiod_name else ""
            },
                                    realm_creation=realm_creation)
            name_validated = True
        elif settings.POPULATE_PROFILE_VIA_LDAP:
            for backend in get_backends():
                if isinstance(backend, LDAPBackend):
                    ldap_attrs = _LDAPUser(
                        backend, backend.django_to_ldap_username(email)).attrs
                    try:
                        ldap_full_name = ldap_attrs[
                            settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0]
                        request.session[
                            'authenticated_full_name'] = ldap_full_name
                        name_validated = True
                        # We don't use initial= here, because if the form is
                        # complete (that is, no additional fields need to be
                        # filled out by the user) we want the form to validate,
                        # so they can be directly registered without having to
                        # go through this interstitial.
                        form = RegistrationForm({'full_name': ldap_full_name},
                                                realm_creation=realm_creation)
                        # FIXME: This will result in the user getting
                        # validation errors if they have to enter a password.
                        # Not relevant for ONLY_SSO, though.
                        break
                    except TypeError:
                        # Let the user fill out a name and/or try another backend
                        form = RegistrationForm(realm_creation=realm_creation)
        elif 'full_name' in request.POST:
            form = RegistrationForm(
                initial={'full_name': request.POST.get('full_name')},
                realm_creation=realm_creation)
        else:
            form = RegistrationForm(realm_creation=realm_creation)
    else:
        postdata = request.POST.copy()
        if name_changes_disabled(realm):
            # If we populate profile information via LDAP and we have a
            # verified name from you on file, use that. Otherwise, fall
            # back to the full name in the request.
            try:
                postdata.update(
                    {'full_name': request.session['authenticated_full_name']})
                name_validated = True
            except KeyError:
                pass
        form = RegistrationForm(postdata, realm_creation=realm_creation)
        if not (password_auth_enabled(realm) and password_required):
            form['password'].field.required = False

    if form.is_valid():
        if password_auth_enabled(realm):
            password = form.cleaned_data['password']
        else:
            # SSO users don't need no passwords
            password = None

        if realm_creation:
            string_id = form.cleaned_data['realm_subdomain']
            realm_name = form.cleaned_data['realm_name']
            realm = do_create_realm(string_id, realm_name)
            setup_initial_streams(realm)
        assert (realm is not None)

        full_name = form.cleaned_data['full_name']
        short_name = email_to_username(email)
        default_stream_group_names = request.POST.getlist(
            'default_stream_group')
        default_stream_groups = lookup_default_stream_groups(
            default_stream_group_names, realm)

        timezone = u""
        if 'timezone' in request.POST and request.POST[
                'timezone'] in get_all_timezones():
            timezone = request.POST['timezone']

        if not realm_creation:
            try:
                existing_user_profile = get_user(email, realm)
            except UserProfile.DoesNotExist:
                existing_user_profile = None
        else:
            existing_user_profile = None

        return_data = {}  # type: Dict[str, bool]
        if ldap_auth_enabled(realm):
            # If the user was authenticated using an external SSO
            # mechanism like Google or GitHub auth, then authentication
            # will have already been done before creating the
            # PreregistrationUser object with password_required=False, and
            # so we don't need to worry about passwords.
            #
            # If instead the realm is using EmailAuthBackend, we will
            # set their password above.
            #
            # But if the realm is using LDAPAuthBackend, we need to verify
            # their LDAP password (which will, as a side effect, create
            # the user account) here using authenticate.
            auth_result = authenticate(request,
                                       username=email,
                                       password=password,
                                       realm=realm,
                                       return_data=return_data)
            if auth_result is None:
                # TODO: This probably isn't going to give a
                # user-friendly error message, but it doesn't
                # particularly matter, because the registration form
                # is hidden for most users.
                return HttpResponseRedirect(
                    reverse('django.contrib.auth.views.login') + '?email=' +
                    urllib.parse.quote_plus(email))

            # Since we'll have created a user, we now just log them in.
            return login_and_go_to_home(request, auth_result)
        elif existing_user_profile is not None and existing_user_profile.is_mirror_dummy:
            user_profile = existing_user_profile
            do_activate_user(user_profile)
            do_change_password(user_profile, password)
            do_change_full_name(user_profile, full_name, user_profile)
            do_set_user_display_setting(user_profile, 'timezone', timezone)
            # TODO: When we clean up the `do_activate_user` code path,
            # make it respect invited_as_admin / is_realm_admin.
        else:
            user_profile = do_create_user(
                email,
                password,
                realm,
                full_name,
                short_name,
                prereg_user=prereg_user,
                is_realm_admin=is_realm_admin,
                tos_version=settings.TOS_VERSION,
                timezone=timezone,
                newsletter_data={"IP": request.META['REMOTE_ADDR']},
                default_stream_groups=default_stream_groups)

        if realm_creation:
            bulk_add_subscriptions([realm.signup_notifications_stream],
                                   [user_profile])
            send_initial_realm_messages(realm)

            # Because for realm creation, registration happens on the
            # root domain, we need to log them into the subdomain for
            # their new realm.
            return redirect_and_log_into_subdomain(realm, full_name, email)

        # This dummy_backend check below confirms the user is
        # authenticating to the correct subdomain.
        auth_result = authenticate(username=user_profile.email,
                                   realm=realm,
                                   return_data=return_data,
                                   use_dummy_backend=True)
        if return_data.get('invalid_subdomain'):
            # By construction, this should never happen.
            logging.error("Subdomain mismatch in registration %s: %s" % (
                realm.subdomain,
                user_profile.email,
            ))
            return redirect('/')

        return login_and_go_to_home(request, auth_result)

    return render(
        request,
        'zerver/register.html',
        context={
            'form': form,
            'email': email,
            'key': key,
            'full_name': request.session.get('authenticated_full_name', None),
            'lock_name': name_validated and name_changes_disabled(realm),
            # password_auth_enabled is normally set via our context processor,
            # but for the registration form, there is no logged in user yet, so
            # we have to set it here.
            'creating_new_team': realm_creation,
            'password_required': password_auth_enabled(realm)
            and password_required,
            'password_auth_enabled': password_auth_enabled(realm),
            'root_domain_available': is_root_domain_available(),
            'default_stream_groups': get_default_stream_groups(realm),
            'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH),
            'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH),
            'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH),
            'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH)
        })
Example #52
0
def add_bot_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    full_name_raw: Text = REQ("full_name"),
    short_name_raw: Text = REQ("short_name"),
    bot_type: int = REQ(validator=check_int, default=UserProfile.DEFAULT_BOT),
    payload_url: Optional[Text] = REQ(validator=check_url, default=""),
    service_name: Optional[Text] = REQ(default=None),
    config_data: Dict[Text, Text] = REQ(
        default={}, validator=check_dict(value_validator=check_string)),
    interface_type: int = REQ(validator=check_int, default=Service.GENERIC),
    default_sending_stream_name: Optional[Text] = REQ('default_sending_stream',
                                                      default=None),
    default_events_register_stream_name: Optional[Text] = REQ(
        'default_events_register_stream', default=None),
    default_all_public_streams: Optional[bool] = REQ(validator=check_bool,
                                                     default=None)
) -> HttpResponse:
    short_name = check_short_name(short_name_raw)
    service_name = service_name or short_name
    short_name += "-bot"
    full_name = check_full_name(full_name_raw)
    email = '%s@%s' % (short_name, user_profile.realm.get_bot_domain())
    form = CreateUserForm({'full_name': full_name, 'email': email})

    if bot_type == UserProfile.EMBEDDED_BOT:
        if not settings.EMBEDDED_BOTS_ENABLED:
            return json_error(_("Embedded bots are not enabled."))
        if service_name not in [bot.name for bot in EMBEDDED_BOTS]:
            return json_error(_("Invalid embedded bot name."))

    if not form.is_valid():
        # We validate client-side as well
        return json_error(_('Bad name or username'))
    try:
        get_user(email, user_profile.realm)
        return json_error(_("Username already in use"))
    except UserProfile.DoesNotExist:
        pass
    check_bot_creation_policy(user_profile, bot_type)
    check_valid_bot_type(user_profile, bot_type)
    check_valid_interface_type(interface_type)

    if len(request.FILES) == 0:
        avatar_source = UserProfile.AVATAR_FROM_GRAVATAR
    elif len(request.FILES) != 1:
        return json_error(_("You may only upload one file at a time"))
    else:
        avatar_source = UserProfile.AVATAR_FROM_USER

    default_sending_stream = None
    if default_sending_stream_name is not None:
        (default_sending_stream, ignored_rec,
         ignored_sub) = access_stream_by_name(user_profile,
                                              default_sending_stream_name)

    default_events_register_stream = None
    if default_events_register_stream_name is not None:
        (default_events_register_stream, ignored_rec,
         ignored_sub) = access_stream_by_name(
             user_profile, default_events_register_stream_name)

    if bot_type == UserProfile.EMBEDDED_BOT:
        check_valid_bot_config(service_name, config_data)

    bot_profile = do_create_user(
        email=email,
        password='',
        realm=user_profile.realm,
        full_name=full_name,
        short_name=short_name,
        bot_type=bot_type,
        bot_owner=user_profile,
        avatar_source=avatar_source,
        default_sending_stream=default_sending_stream,
        default_events_register_stream=default_events_register_stream,
        default_all_public_streams=default_all_public_streams)
    if len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        upload_avatar_image(user_file, user_profile, bot_profile)

    if bot_type in (UserProfile.OUTGOING_WEBHOOK_BOT,
                    UserProfile.EMBEDDED_BOT):
        add_service(name=service_name,
                    user_profile=bot_profile,
                    base_url=payload_url,
                    interface=interface_type,
                    token=random_api_key())

    if bot_type == UserProfile.EMBEDDED_BOT:
        for key, value in config_data.items():
            set_bot_config(bot_profile, key, value)

    notify_created_bot(bot_profile)

    json_result = dict(
        api_key=bot_profile.api_key,
        avatar_url=avatar_url(bot_profile),
        default_sending_stream=get_stream_name(
            bot_profile.default_sending_stream),
        default_events_register_stream=get_stream_name(
            bot_profile.default_events_register_stream),
        default_all_public_streams=bot_profile.default_all_public_streams,
    )
    return json_success(json_result)
Example #53
0
    def test_people(self) -> None:
        hamlet = self.example_user('hamlet')
        realm = get_realm('zulip')
        self.login(hamlet.email)

        for i in range(3):
            self.create_bot(
                owner=hamlet,
                bot_email='*****@*****.**' % (i, ),
                bot_name='Bot %d' % (i, ),
            )

        for i in range(3):
            self.create_non_active_user(
                realm=realm,
                email='*****@*****.**' % (i, ),
                name='Defunct User %d' % (i, ),
            )

        result = self._get_home_page()
        page_params = self._get_page_params(result)
        '''
        We send three lists of users.  The first two below are disjoint
        lists of users, and the records we send for them have identical
        structure.

        The realm_bots bucket is somewhat redundant, since all bots will
        be in one of the first two buckets.  They do include fields, however,
        that normal users don't care about, such as default_sending_stream.
        '''

        buckets = [
            'realm_users',
            'realm_non_active_users',
            'realm_bots',
        ]

        for field in buckets:
            users = page_params[field]
            self.assertTrue(len(users) >= 3, field)
            for rec in users:
                self.assertEqual(rec['user_id'],
                                 get_user(rec['email'], realm).id)
                if field == 'realm_bots':
                    self.assertNotIn('is_bot', rec)
                    self.assertIn('is_active', rec)
                    self.assertIn('owner', rec)
                else:
                    self.assertIn('is_bot', rec)
                    self.assertNotIn('is_active', rec)

        active_emails = {p['email'] for p in page_params['realm_users']}
        non_active_emails = {
            p['email']
            for p in page_params['realm_non_active_users']
        }
        bot_emails = {p['email'] for p in page_params['realm_bots']}

        self.assertIn(hamlet.email, active_emails)
        self.assertIn('*****@*****.**', non_active_emails)

        # Bots can show up in multiple buckets.
        self.assertIn('*****@*****.**', bot_emails)
        self.assertIn('*****@*****.**', active_emails)

        # Make sure nobody got mis-bucketed.
        self.assertNotIn(hamlet.email, non_active_emails)
        self.assertNotIn('*****@*****.**', active_emails)

        cross_bots = page_params['cross_realm_bots']
        self.assertEqual(len(cross_bots), 4)
        cross_bots.sort(key=lambda d: d['email'])
        for cross_bot in cross_bots:
            # These are either nondeterministic or boring
            del cross_bot['timezone']
            del cross_bot['avatar_url']
            del cross_bot['date_joined']

        notification_bot = self.notification_bot()

        by_email = lambda d: d['email']

        self.assertEqual(
            sorted(cross_bots, key=by_email),
            sorted([
                dict(user_id=get_system_bot('*****@*****.**').id,
                     is_admin=False,
                     email='*****@*****.**',
                     full_name='Email Gateway',
                     is_bot=True),
                dict(user_id=get_system_bot('*****@*****.**').id,
                     is_admin=False,
                     email='*****@*****.**',
                     full_name='Zulip Feedback Bot',
                     is_bot=True),
                dict(user_id=notification_bot.id,
                     is_admin=False,
                     email=notification_bot.email,
                     full_name='Notification Bot',
                     is_bot=True),
                dict(user_id=get_system_bot('*****@*****.**').id,
                     is_admin=False,
                     email='*****@*****.**',
                     full_name='Welcome Bot',
                     is_bot=True),
            ],
                   key=by_email))
Example #54
0
def add_bot_backend(request,
                    user_profile,
                    full_name_raw=REQ("full_name"),
                    short_name_raw=REQ("short_name"),
                    bot_type=REQ(validator=check_int,
                                 default=UserProfile.DEFAULT_BOT),
                    payload_url=REQ(validator=check_url, default=None),
                    interface_type=REQ(validator=check_int,
                                       default=Service.GENERIC),
                    default_sending_stream_name=REQ('default_sending_stream',
                                                    default=None),
                    default_events_register_stream_name=REQ(
                        'default_events_register_stream', default=None),
                    default_all_public_streams=REQ(validator=check_bool,
                                                   default=None)):
    # type: (HttpRequest, UserProfile, Text, Text, int, Optional[Text], int, Optional[Text], Optional[Text], Optional[bool]) -> HttpResponse
    short_name = check_short_name(short_name_raw)
    service_name = short_name
    short_name += "-bot"
    full_name = check_full_name(full_name_raw)
    email = '%s@%s' % (short_name, user_profile.realm.get_bot_domain())
    form = CreateUserForm({'full_name': full_name, 'email': email})
    if not form.is_valid():
        # We validate client-side as well
        return json_error(_('Bad name or username'))
    try:
        get_user(email, user_profile.realm)
        return json_error(_("Username already in use"))
    except UserProfile.DoesNotExist:
        pass
    check_valid_bot_type(bot_type)
    check_valid_interface_type(interface_type)

    if len(request.FILES) == 0:
        avatar_source = UserProfile.AVATAR_FROM_GRAVATAR
    elif len(request.FILES) != 1:
        return json_error(_("You may only upload one file at a time"))
    else:
        avatar_source = UserProfile.AVATAR_FROM_USER

    default_sending_stream = None
    if default_sending_stream_name is not None:
        (default_sending_stream, ignored_rec,
         ignored_sub) = access_stream_by_name(user_profile,
                                              default_sending_stream_name)

    default_events_register_stream = None
    if default_events_register_stream_name is not None:
        (default_events_register_stream, ignored_rec,
         ignored_sub) = access_stream_by_name(
             user_profile, default_events_register_stream_name)

    bot_profile = do_create_user(
        email=email,
        password='',
        realm=user_profile.realm,
        full_name=full_name,
        short_name=short_name,
        active=True,
        bot_type=bot_type,
        bot_owner=user_profile,
        avatar_source=avatar_source,
        default_sending_stream=default_sending_stream,
        default_events_register_stream=default_events_register_stream,
        default_all_public_streams=default_all_public_streams)
    if len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        upload_avatar_image(user_file, user_profile, bot_profile)

    if bot_type == UserProfile.OUTGOING_WEBHOOK_BOT:
        add_outgoing_webhook_service(name=service_name,
                                     user_profile=bot_profile,
                                     base_url=payload_url,
                                     interface=interface_type,
                                     token=random_api_key())

    json_result = dict(
        api_key=bot_profile.api_key,
        avatar_url=avatar_url(bot_profile),
        default_sending_stream=get_stream_name(
            bot_profile.default_sending_stream),
        default_events_register_stream=get_stream_name(
            bot_profile.default_events_register_stream),
        default_all_public_streams=bot_profile.default_all_public_streams,
    )
    return json_success(json_result)
Example #55
0
    def test_create_user_backend(self) -> None:

        # This test should give us complete coverage on
        # create_user_backend.  It mostly exercises error
        # conditions, and it also does a basic test of the success
        # path.

        admin = self.example_user('hamlet')
        admin_email = admin.email
        self.login(admin_email)
        do_change_is_admin(admin, True)

        result = self.client_post("/json/users", dict())
        self.assert_json_error(result, "Missing 'email' argument")

        result = self.client_post("/json/users",
                                  dict(email='*****@*****.**', ))
        self.assert_json_error(result, "Missing 'password' argument")

        result = self.client_post(
            "/json/users", dict(
                email='*****@*****.**',
                password='******',
            ))
        self.assert_json_error(result, "Missing 'full_name' argument")

        result = self.client_post(
            "/json/users",
            dict(
                email='*****@*****.**',
                password='******',
                full_name='Romeo Montague',
            ))
        self.assert_json_error(result, "Missing 'short_name' argument")

        result = self.client_post(
            "/json/users",
            dict(
                email='broken',
                password='******',
                full_name='Romeo Montague',
                short_name='Romeo',
            ))
        self.assert_json_error(result, "Bad name or username")

        result = self.client_post(
            "/json/users",
            dict(
                email='*****@*****.**',
                password='******',
                full_name='Romeo Montague',
                short_name='Romeo',
            ))
        self.assert_json_error(
            result,
            "Email '*****@*****.**' not allowed for realm 'zulip'")

        RealmDomain.objects.create(realm=get_realm('zulip'),
                                   domain='zulip.net')

        # HAPPY PATH STARTS HERE
        valid_params = dict(
            email='*****@*****.**',
            password='******',
            full_name='Romeo Montague',
            short_name='Romeo',
        )
        result = self.client_post("/json/users", valid_params)
        self.assert_json_success(result)

        # Romeo is a newly registered user
        new_user = get_user('*****@*****.**', get_realm('zulip'))
        self.assertEqual(new_user.full_name, 'Romeo Montague')
        self.assertEqual(new_user.short_name, 'Romeo')

        # One more error condition to test--we can't create
        # the same user twice.
        result = self.client_post("/json/users", valid_params)
        self.assert_json_error(result,
                               "Email '*****@*****.**' already in use")
    def handle(self, **options: Any) -> None:
        if options["percent_huddles"] + options["percent_personals"] > 100:
            self.stderr.write(
                "Error!  More than 100% of messages allocated.\n")
            return

        # Get consistent data for backend tests.
        if options["test_suite"]:
            random.seed(0)

        # If max_topics is not set, we set it proportional to the
        # number of messages.
        if options["max_topics"] is None:
            options["max_topics"] = 1 + options["num_messages"] // 100

        if options["delete"]:
            # Start by clearing all the data in our database
            clear_database()

            # Create our three default realms
            # Could in theory be done via zerver.lib.actions.do_create_realm, but
            # welcome-bot (needed for do_create_realm) hasn't been created yet
            create_internal_realm()
            zulip_realm = Realm.objects.create(
                string_id="zulip",
                name="Zulip Dev",
                emails_restricted_to_domains=False,
                email_address_visibility=Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS,
                description=
                "The Zulip development environment default organization."
                "  It's great for testing!",
                invite_required=False,
                org_type=Realm.CORPORATE)
            RealmDomain.objects.create(realm=zulip_realm, domain="zulip.com")
            if options["test_suite"]:
                mit_realm = Realm.objects.create(
                    string_id="zephyr",
                    name="MIT",
                    emails_restricted_to_domains=True,
                    invite_required=False,
                    org_type=Realm.CORPORATE)
                RealmDomain.objects.create(realm=mit_realm, domain="mit.edu")

                lear_realm = Realm.objects.create(
                    string_id="lear",
                    name="Lear & Co.",
                    emails_restricted_to_domains=False,
                    invite_required=False,
                    org_type=Realm.CORPORATE)

            # Create test Users (UserProfiles are automatically created,
            # as are subscriptions to the ability to receive personals).
            names = [
                ("Zoe", "*****@*****.**"),
                ("Othello, the Moor of Venice", "*****@*****.**"),
                ("Iago", "*****@*****.**"),
                ("Prospero from The Tempest", "*****@*****.**"),
                ("Cordelia Lear", "*****@*****.**"),
                ("King Hamlet", "*****@*****.**"),
                ("aaron", "*****@*****.**"),
                ("Polonius", "*****@*****.**"),
                ("Desdemona", "*****@*****.**"),
            ]

            # For testing really large batches:
            # Create extra users with semi realistic names to make search
            # functions somewhat realistic.  We'll still create 1000 users
            # like Extra222 User for some predicability.
            num_names = options['extra_users']
            num_boring_names = 300

            for i in range(min(num_names, num_boring_names)):
                full_name = f'Extra{i:03} User'
                names.append((full_name, f'extrauser{i}@zulip.com'))

            if num_names > num_boring_names:
                fnames = [
                    'Amber', 'Arpita', 'Bob', 'Cindy', 'Daniela', 'Dan',
                    'Dinesh', 'Faye', 'François', 'George', 'Hank', 'Irene',
                    'James', 'Janice', 'Jenny', 'Jill', 'John', 'Kate',
                    'Katelyn', 'Kobe', 'Lexi', 'Manish', 'Mark', 'Matt',
                    'Mayna', 'Michael', 'Pete', 'Peter', 'Phil', 'Phillipa',
                    'Preston', 'Sally', 'Scott', 'Sandra', 'Steve',
                    'Stephanie', 'Vera'
                ]
                mnames = ['de', 'van', 'von', 'Shaw', 'T.']
                lnames = [
                    'Adams', 'Agarwal', 'Beal', 'Benson', 'Bonita', 'Davis',
                    'George', 'Harden', 'James', 'Jones', 'Johnson', 'Jordan',
                    'Lee', 'Leonard', 'Singh', 'Smith', 'Patel', 'Towns',
                    'Wall'
                ]

            for i in range(num_boring_names, num_names):
                fname = random.choice(fnames) + str(i)
                full_name = fname
                if random.random() < 0.7:
                    if random.random() < 0.5:
                        full_name += ' ' + random.choice(mnames)
                    full_name += ' ' + random.choice(lnames)
                email = fname.lower() + '@zulip.com'
                names.append((full_name, email))

            create_users(zulip_realm, names, tos_version=settings.TOS_VERSION)

            iago = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            do_change_user_role(iago,
                                UserProfile.ROLE_REALM_ADMINISTRATOR,
                                acting_user=None)
            iago.is_staff = True
            iago.save(update_fields=['is_staff'])

            desdemona = get_user_by_delivery_email("*****@*****.**",
                                                   zulip_realm)
            do_change_user_role(desdemona,
                                UserProfile.ROLE_REALM_OWNER,
                                acting_user=None)

            guest_user = get_user_by_delivery_email("*****@*****.**",
                                                    zulip_realm)
            guest_user.role = UserProfile.ROLE_GUEST
            guest_user.save(update_fields=['role'])

            # These bots are directly referenced from code and thus
            # are needed for the test suite.
            zulip_realm_bots = [
                ("Zulip Error Bot", "*****@*****.**"),
                ("Zulip Default Bot", "*****@*****.**"),
            ]
            for i in range(options["extra_bots"]):
                zulip_realm_bots.append(
                    (f'Extra Bot {i}', f'extrabot{i}@zulip.com'))

            create_users(zulip_realm,
                         zulip_realm_bots,
                         bot_type=UserProfile.DEFAULT_BOT)

            zoe = get_user_by_delivery_email("*****@*****.**", zulip_realm)
            zulip_webhook_bots = [
                ("Zulip Webhook Bot", "*****@*****.**"),
            ]
            # If a stream is not supplied in the webhook URL, the webhook
            # will (in some cases) send the notification as a PM to the
            # owner of the webhook bot, so bot_owner can't be None
            create_users(zulip_realm,
                         zulip_webhook_bots,
                         bot_type=UserProfile.INCOMING_WEBHOOK_BOT,
                         bot_owner=zoe)
            aaron = get_user_by_delivery_email("*****@*****.**", zulip_realm)

            zulip_outgoing_bots = [
                ("Outgoing Webhook", "*****@*****.**"),
            ]
            create_users(zulip_realm,
                         zulip_outgoing_bots,
                         bot_type=UserProfile.OUTGOING_WEBHOOK_BOT,
                         bot_owner=aaron)
            outgoing_webhook = get_user("*****@*****.**",
                                        zulip_realm)
            add_service("outgoing-webhook",
                        user_profile=outgoing_webhook,
                        interface=Service.GENERIC,
                        base_url="http://127.0.0.1:5002",
                        token=generate_api_key())

            # Add the realm internal bots to each realm.
            create_if_missing_realm_internal_bots()

            # Create public streams.
            stream_list = ["Verona", "Denmark", "Scotland", "Venice", "Rome"]
            stream_dict: Dict[str, Dict[str, Any]] = {
                "Verona": {
                    "description": "A city in Italy"
                },
                "Denmark": {
                    "description": "A Scandinavian country"
                },
                "Scotland": {
                    "description": "Located in the United Kingdom"
                },
                "Venice": {
                    "description": "A northeastern Italian city"
                },
                "Rome": {
                    "description": "Yet another Italian city",
                    "is_web_public": True
                },
            }

            bulk_create_streams(zulip_realm, stream_dict)
            recipient_streams: List[int] = [
                Stream.objects.get(name=name, realm=zulip_realm).id
                for name in stream_list
            ]

            # Create subscriptions to streams.  The following
            # algorithm will give each of the users a different but
            # deterministic subset of the streams (given a fixed list
            # of users). For the test suite, we have a fixed list of
            # subscriptions to make sure test data is consistent
            # across platforms.

            subscriptions_list: List[Tuple[UserProfile, Recipient]] = []
            profiles: Sequence[
                UserProfile] = UserProfile.objects.select_related().filter(
                    is_bot=False).order_by("email")

            if options["test_suite"]:
                subscriptions_map = {
                    '*****@*****.**': ['Verona'],
                    '*****@*****.**': ['Verona'],
                    '*****@*****.**': ['Verona', 'Denmark'],
                    '*****@*****.**': ['Verona', 'Denmark', 'Scotland'],
                    '*****@*****.**': ['Verona', 'Denmark', 'Scotland'],
                    '*****@*****.**':
                    ['Verona', 'Denmark', 'Scotland', 'Venice'],
                    '*****@*****.**':
                    ['Verona', 'Denmark', 'Scotland', 'Venice', 'Rome'],
                    '*****@*****.**': ['Verona'],
                    '*****@*****.**': ['Verona', 'Denmark', 'Venice'],
                }

                for profile in profiles:
                    email = profile.delivery_email
                    if email not in subscriptions_map:
                        raise Exception(
                            f'Subscriptions not listed for user {email}')

                    for stream_name in subscriptions_map[email]:
                        stream = Stream.objects.get(name=stream_name)
                        r = Recipient.objects.get(type=Recipient.STREAM,
                                                  type_id=stream.id)
                        subscriptions_list.append((profile, r))
            else:
                num_streams = len(recipient_streams)
                num_users = len(profiles)
                for i, profile in enumerate(profiles):
                    # Subscribe to some streams.
                    fraction = float(i) / num_users
                    num_recips = int(num_streams * fraction) + 1

                    for type_id in recipient_streams[:num_recips]:
                        r = Recipient.objects.get(type=Recipient.STREAM,
                                                  type_id=type_id)
                        subscriptions_list.append((profile, r))

            subscriptions_to_add: List[Subscription] = []
            event_time = timezone_now()
            all_subscription_logs: (List[RealmAuditLog]) = []

            i = 0
            for profile, recipient in subscriptions_list:
                i += 1
                color = STREAM_ASSIGNMENT_COLORS[i %
                                                 len(STREAM_ASSIGNMENT_COLORS)]
                s = Subscription(recipient=recipient,
                                 user_profile=profile,
                                 color=color)

                subscriptions_to_add.append(s)

                log = RealmAuditLog(
                    realm=profile.realm,
                    modified_user=profile,
                    modified_stream_id=recipient.type_id,
                    event_last_message_id=0,
                    event_type=RealmAuditLog.SUBSCRIPTION_CREATED,
                    event_time=event_time)
                all_subscription_logs.append(log)

            Subscription.objects.bulk_create(subscriptions_to_add)
            RealmAuditLog.objects.bulk_create(all_subscription_logs)

            # Create custom profile field data
            phone_number = try_add_realm_custom_profile_field(
                zulip_realm,
                "Phone number",
                CustomProfileField.SHORT_TEXT,
                hint='')
            biography = try_add_realm_custom_profile_field(
                zulip_realm,
                "Biography",
                CustomProfileField.LONG_TEXT,
                hint='What are you known for?')
            favorite_food = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite food",
                CustomProfileField.SHORT_TEXT,
                hint="Or drink, if you'd prefer")
            field_data: ProfileFieldData = {
                'vim': {
                    'text': 'Vim',
                    'order': '1'
                },
                'emacs': {
                    'text': 'Emacs',
                    'order': '2'
                },
            }
            favorite_editor = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite editor",
                CustomProfileField.CHOICE,
                field_data=field_data)
            birthday = try_add_realm_custom_profile_field(
                zulip_realm, "Birthday", CustomProfileField.DATE)
            favorite_website = try_add_realm_custom_profile_field(
                zulip_realm,
                "Favorite website",
                CustomProfileField.URL,
                hint="Or your personal blog's URL")
            mentor = try_add_realm_custom_profile_field(
                zulip_realm, "Mentor", CustomProfileField.USER)
            github_profile = try_add_realm_default_custom_profile_field(
                zulip_realm, "github")

            # Fill in values for Iago and Hamlet
            hamlet = get_user_by_delivery_email("*****@*****.**",
                                                zulip_realm)
            do_update_user_custom_profile_data_if_changed(
                iago, [
                    {
                        "id": phone_number.id,
                        "value": "+1-234-567-8901"
                    },
                    {
                        "id": biography.id,
                        "value": "Betrayer of Othello."
                    },
                    {
                        "id": favorite_food.id,
                        "value": "Apples"
                    },
                    {
                        "id": favorite_editor.id,
                        "value": "emacs"
                    },
                    {
                        "id": birthday.id,
                        "value": "2000-1-1"
                    },
                    {
                        "id": favorite_website.id,
                        "value": "https://zulip.readthedocs.io/en/latest/"
                    },
                    {
                        "id": mentor.id,
                        "value": [hamlet.id]
                    },
                    {
                        "id": github_profile.id,
                        "value": 'zulip'
                    },
                ])
            do_update_user_custom_profile_data_if_changed(
                hamlet, [
                    {
                        "id": phone_number.id,
                        "value": "+0-11-23-456-7890"
                    },
                    {
                        "id":
                        biography.id,
                        "value":
                        "I am:\n* The prince of Denmark\n* Nephew to the usurping Claudius",
                    },
                    {
                        "id": favorite_food.id,
                        "value": "Dark chocolate"
                    },
                    {
                        "id": favorite_editor.id,
                        "value": "vim"
                    },
                    {
                        "id": birthday.id,
                        "value": "1900-1-1"
                    },
                    {
                        "id": favorite_website.id,
                        "value": "https://blog.zulig.org"
                    },
                    {
                        "id": mentor.id,
                        "value": [iago.id]
                    },
                    {
                        "id": github_profile.id,
                        "value": 'zulipbot'
                    },
                ])
        else:
            zulip_realm = get_realm("zulip")
            recipient_streams = [
                klass.type_id
                for klass in Recipient.objects.filter(type=Recipient.STREAM)
            ]

        # Extract a list of all users
        user_profiles: List[UserProfile] = list(
            UserProfile.objects.filter(is_bot=False))

        # Create a test realm emoji.
        IMAGE_FILE_PATH = static_path('images/test-images/checkbox.png')
        with open(IMAGE_FILE_PATH, 'rb') as fp:
            check_add_realm_emoji(zulip_realm, 'green_tick', iago, fp)

        if not options["test_suite"]:
            # Populate users with some bar data
            for user in user_profiles:
                status: int = UserPresence.ACTIVE
                date = timezone_now()
                client = get_client("website")
                if user.full_name[0] <= 'H':
                    client = get_client("ZulipAndroid")
                UserPresence.objects.get_or_create(user_profile=user,
                                                   realm_id=user.realm_id,
                                                   client=client,
                                                   timestamp=date,
                                                   status=status)

        user_profiles_ids = [user_profile.id for user_profile in user_profiles]

        # Create several initial huddles
        for i in range(options["num_huddles"]):
            get_huddle(random.sample(user_profiles_ids, random.randint(3, 4)))

        # Create several initial pairs for personals
        personals_pairs = [
            random.sample(user_profiles_ids, 2)
            for i in range(options["num_personals"])
        ]

        create_alert_words(zulip_realm.id)

        # Generate a new set of test data.
        create_test_data()

        # prepopulate the URL preview/embed data for the links present
        # in the config.generate_data.json data set.  This makes it
        # possible for populate_db to run happily without Internet
        # access.
        with open("zerver/tests/fixtures/docs_url_preview_data.json") as f:
            urls_with_preview_data = ujson.load(f)
            for url in urls_with_preview_data:
                cache_set(url, urls_with_preview_data[url], PREVIEW_CACHE_NAME)

        if options["delete"]:
            if options["test_suite"]:
                # Create test users; the MIT ones are needed to test
                # the Zephyr mirroring codepaths.
                testsuite_mit_users = [
                    ("Fred Sipb (MIT)", "*****@*****.**"),
                    ("Athena Consulting Exchange User (MIT)",
                     "*****@*****.**"),
                    ("Esp Classroom (MIT)", "*****@*****.**"),
                ]
                create_users(mit_realm,
                             testsuite_mit_users,
                             tos_version=settings.TOS_VERSION)

                testsuite_lear_users = [
                    ("King Lear", "*****@*****.**"),
                    ("Cordelia Lear", "*****@*****.**"),
                ]
                create_users(lear_realm,
                             testsuite_lear_users,
                             tos_version=settings.TOS_VERSION)

            if not options["test_suite"]:
                # To keep the messages.json fixtures file for the test
                # suite fast, don't add these users and subscriptions
                # when running populate_db for the test suite

                zulip_stream_dict: Dict[str, Dict[str, Any]] = {
                    "devel": {
                        "description": "For developing"
                    },
                    "all": {
                        "description": "For **everything**"
                    },
                    "announce": {
                        "description": "For announcements",
                        'stream_post_policy': Stream.STREAM_POST_POLICY_ADMINS
                    },
                    "design": {
                        "description": "For design"
                    },
                    "support": {
                        "description": "For support"
                    },
                    "social": {
                        "description": "For socializing"
                    },
                    "test": {
                        "description": "For testing `code`"
                    },
                    "errors": {
                        "description": "For errors"
                    },
                    "sales": {
                        "description": "For sales discussion"
                    },
                }

                # Calculate the maximum number of digits in any extra stream's
                # number, since a stream with name "Extra Stream 3" could show
                # up after "Extra Stream 29". (Used later to pad numbers with
                # 0s).
                maximum_digits = len(str(options['extra_streams'] - 1))

                for i in range(options['extra_streams']):
                    # Pad the number with 0s based on `maximum_digits`.
                    number_str = str(i).zfill(maximum_digits)

                    extra_stream_name = 'Extra Stream ' + number_str

                    zulip_stream_dict[extra_stream_name] = {
                        "description": "Auto-generated extra stream.",
                    }

                bulk_create_streams(zulip_realm, zulip_stream_dict)
                # Now that we've created the notifications stream, configure it properly.
                zulip_realm.notifications_stream = get_stream(
                    "announce", zulip_realm)
                zulip_realm.save(update_fields=['notifications_stream'])

                # Add a few default streams
                for default_stream_name in [
                        "design", "devel", "social", "support"
                ]:
                    DefaultStream.objects.create(realm=zulip_realm,
                                                 stream=get_stream(
                                                     default_stream_name,
                                                     zulip_realm))

                # Now subscribe everyone to these streams
                subscribe_users_to_streams(zulip_realm, zulip_stream_dict)

            if not options["test_suite"]:
                # Update pointer of each user to point to the last message in their
                # UserMessage rows with sender_id=user_profile_id.
                users = list(
                    UserMessage.objects.filter(message__sender_id=F(
                        'user_profile_id')).values('user_profile_id').annotate(
                            pointer=Max('message_id')))
                for user in users:
                    UserProfile.objects.filter(
                        id=user['user_profile_id']).update(
                            pointer=user['pointer'])

            create_user_groups()

            if not options["test_suite"]:
                # We populate the analytics database here for
                # development purpose only
                call_command('populate_analytics_db')

        threads = options["threads"]
        jobs: List[Tuple[int, List[List[int]], Dict[str, Any],
                         Callable[[str], int], int]] = []
        for i in range(threads):
            count = options["num_messages"] // threads
            if i < options["num_messages"] % threads:
                count += 1
            jobs.append((count, personals_pairs, options, self.stdout.write,
                         random.randint(0, 10**10)))

        for job in jobs:
            generate_and_send_messages(job)

        if options["delete"]:
            if not options['test_suite']:
                # These bots are not needed by the test suite
                # Also, we don't want interacting with each other
                # in dev setup.
                internal_zulip_users_nosubs = [
                    ("Zulip Commit Bot", "*****@*****.**"),
                    ("Zulip Trac Bot", "*****@*****.**"),
                    ("Zulip Nagios Bot", "*****@*****.**"),
                ]
                create_users(zulip_realm,
                             internal_zulip_users_nosubs,
                             bot_type=UserProfile.DEFAULT_BOT)

            mark_all_messages_as_read()
            self.stdout.write("Successfully populated test database.\n")
Example #57
0
 def mit_user(self, name: str) -> UserProfile:
     email = self.mit_user_map[name]
     return get_user(email, get_realm('zephyr'))
Example #58
0
    def test_people(self) -> None:
        hamlet = self.example_user("hamlet")
        realm = get_realm("zulip")
        self.login_user(hamlet)

        bots = {}
        for i in range(3):
            bots[i] = self.create_bot(
                owner=hamlet,
                bot_email=f"bot-{i}@zulip.com",
                bot_name=f"Bot {i}",
            )

        for i in range(3):
            defunct_user = self.create_non_active_user(
                realm=realm,
                email=f"defunct-{i}@zulip.com",
                name=f"Defunct User {i}",
            )

        result = self._get_home_page()
        page_params = self._get_page_params(result)
        """
        We send three lists of users.  The first two below are disjoint
        lists of users, and the records we send for them have identical
        structure.

        The realm_bots bucket is somewhat redundant, since all bots will
        be in one of the first two buckets.  They do include fields, however,
        that normal users don't care about, such as default_sending_stream.
        """

        buckets = [
            "realm_users",
            "realm_non_active_users",
            "realm_bots",
        ]

        for field in buckets:
            users = page_params[field]
            self.assertTrue(len(users) >= 3, field)
            for rec in users:
                self.assertEqual(rec["user_id"],
                                 get_user(rec["email"], realm).id)
                if field == "realm_bots":
                    self.assertNotIn("is_bot", rec)
                    self.assertIn("is_active", rec)
                    self.assertIn("owner_id", rec)
                else:
                    self.assertIn("is_bot", rec)
                    self.assertNotIn("is_active", rec)

        active_ids = {p["user_id"] for p in page_params["realm_users"]}
        non_active_ids = {
            p["user_id"]
            for p in page_params["realm_non_active_users"]
        }
        bot_ids = {p["user_id"] for p in page_params["realm_bots"]}

        self.assertIn(hamlet.id, active_ids)
        self.assertIn(defunct_user.id, non_active_ids)

        # Bots can show up in multiple buckets.
        self.assertIn(bots[2].id, bot_ids)
        self.assertIn(bots[2].id, active_ids)

        # Make sure nobody got mis-bucketed.
        self.assertNotIn(hamlet.id, non_active_ids)
        self.assertNotIn(defunct_user.id, active_ids)

        cross_bots = page_params["cross_realm_bots"]
        self.assertEqual(len(cross_bots), 3)
        cross_bots.sort(key=lambda d: d["email"])
        for cross_bot in cross_bots:
            # These are either nondeterministic or boring
            del cross_bot["timezone"]
            del cross_bot["avatar_url"]
            del cross_bot["date_joined"]

        notification_bot = self.notification_bot()
        email_gateway_bot = get_system_bot(settings.EMAIL_GATEWAY_BOT)
        welcome_bot = get_system_bot(settings.WELCOME_BOT)

        by_email = lambda d: d["email"]

        self.assertEqual(
            sorted(cross_bots, key=by_email),
            sorted(
                [
                    dict(
                        avatar_version=email_gateway_bot.avatar_version,
                        bot_owner_id=None,
                        bot_type=1,
                        email=email_gateway_bot.email,
                        user_id=email_gateway_bot.id,
                        full_name=email_gateway_bot.full_name,
                        is_active=True,
                        is_bot=True,
                        is_admin=False,
                        is_owner=False,
                        is_cross_realm_bot=True,
                        is_guest=False,
                    ),
                    dict(
                        avatar_version=notification_bot.avatar_version,
                        bot_owner_id=None,
                        bot_type=1,
                        email=notification_bot.email,
                        user_id=notification_bot.id,
                        full_name=notification_bot.full_name,
                        is_active=True,
                        is_bot=True,
                        is_admin=False,
                        is_owner=False,
                        is_cross_realm_bot=True,
                        is_guest=False,
                    ),
                    dict(
                        avatar_version=welcome_bot.avatar_version,
                        bot_owner_id=None,
                        bot_type=1,
                        email=welcome_bot.email,
                        user_id=welcome_bot.id,
                        full_name=welcome_bot.full_name,
                        is_active=True,
                        is_bot=True,
                        is_admin=False,
                        is_owner=False,
                        is_cross_realm_bot=True,
                        is_guest=False,
                    ),
                ],
                key=by_email,
            ),
        )
Example #59
0
 def lear_user(self, name: str) -> UserProfile:
     email = self.lear_user_map[name]
     return get_user(email, get_realm('lear'))
Example #60
0
    def save(self,
             domain_override: Optional[bool] = None,
             subject_template_name:
             Text = 'registration/password_reset_subject.txt',
             email_template_name:
             Text = 'registration/password_reset_email.html',
             use_https: bool = False,
             token_generator:
             PasswordResetTokenGenerator = default_token_generator,
             from_email: Optional[Text] = None,
             request: HttpRequest = None,
             html_email_template_name: Optional[Text] = None,
             extra_email_context: Optional[Dict[str, Any]] = None) -> None:
        """
        If the email address has an account in the target realm,
        generates a one-use only link for resetting password and sends
        to the user.

        We send a different email if an associated account does not exist in the
        database, or an account does exist, but not in the realm.

        Note: We ignore protocol and the various email template arguments (those
        are an artifact of using Django's password reset framework).
        """
        email = self.cleaned_data["email"]

        realm = get_realm(get_subdomain(request))

        if not email_auth_enabled(realm):
            logging.info(
                "Password reset attempted for %s even though password auth is disabled."
                % (email, ))
            return

        user = None
        try:
            user = get_user(email, realm)
        except UserProfile.DoesNotExist:
            pass

        context = {
            'email': email,
            'realm_uri': realm.uri,
        }

        if user is not None:
            token = token_generator.make_token(user)
            uid = urlsafe_base64_encode(force_bytes(user.id))
            endpoint = reverse(
                'django.contrib.auth.views.password_reset_confirm',
                kwargs=dict(uidb64=uid, token=token))

            context['no_account_in_realm'] = False
            context['reset_url'] = "{}{}".format(user.realm.uri, endpoint)
            send_email('zerver/emails/password_reset',
                       to_user_id=user.id,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.NOREPLY,
                       context=context)
        else:
            context['no_account_in_realm'] = True
            accounts = UserProfile.objects.filter(email__iexact=email)
            if accounts:
                context['accounts'] = accounts
                context['multiple_accounts'] = accounts.count() != 1
            send_email('zerver/emails/password_reset',
                       to_email=email,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.NOREPLY,
                       context=context)