2017-03-08 12:07:51 +01:00
|
|
|
import filecmp
|
|
|
|
import os
|
2024-07-12 02:30:23 +02:00
|
|
|
from typing import Any
|
2020-06-11 00:54:34 +02:00
|
|
|
from unittest.mock import MagicMock, patch
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2017-03-08 12:07:51 +01:00
|
|
|
from django.core import mail
|
2019-08-30 00:21:36 +02:00
|
|
|
from django.test import override_settings
|
2020-06-11 00:54:34 +02:00
|
|
|
from zulip_bots.custom_exceptions import ConfigValidationError
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2024-08-20 23:15:19 +02:00
|
|
|
from zerver.actions.bots import do_change_bot_owner, do_change_default_sending_stream
|
2021-10-26 09:15:16 +02:00
|
|
|
from zerver.actions.realm_settings import do_set_realm_user_default_setting
|
2022-04-14 23:51:16 +02:00
|
|
|
from zerver.actions.streams import do_change_stream_permission
|
2022-05-07 09:03:02 +02:00
|
|
|
from zerver.actions.users import do_change_can_create_users, do_change_user_role, do_deactivate_user
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.bot_config import ConfigError, get_bot_config
|
|
|
|
from zerver.lib.bot_lib import get_bot_handler
|
|
|
|
from zerver.lib.integrations import EMBEDDED_BOTS, WebhookIntegration
|
|
|
|
from zerver.lib.test_classes import UploadSerializeMixin, ZulipTestCase
|
2022-10-15 22:47:40 +02:00
|
|
|
from zerver.lib.test_helpers import avatar_disk_path, get_test_image_file
|
2024-08-20 23:15:19 +02:00
|
|
|
from zerver.lib.utils import assert_is_not_none
|
2024-09-20 11:41:30 +02:00
|
|
|
from zerver.lib.webhooks.common import WebhookConfigOption
|
2024-05-22 11:43:10 +02:00
|
|
|
from zerver.models import RealmUserDefault, Service, Subscription, UserProfile
|
2023-12-15 21:00:29 +01:00
|
|
|
from zerver.models.bots import get_bot_services
|
2024-05-22 11:43:10 +02:00
|
|
|
from zerver.models.realms import BotCreationPolicyEnum, get_realm
|
2023-12-15 03:57:04 +01:00
|
|
|
from zerver.models.streams import get_stream
|
2024-08-20 23:15:19 +02:00
|
|
|
from zerver.models.users import bot_owner_user_ids, get_user, is_cross_realm_bot_email
|
2018-02-13 11:47:40 +01:00
|
|
|
|
2019-08-18 12:56:21 +02:00
|
|
|
|
|
|
|
# A test validator
|
2024-07-12 02:30:23 +02:00
|
|
|
def _check_string(var_name: str, val: str) -> str | None:
|
2022-06-01 00:34:34 +02:00
|
|
|
if val.startswith("_"):
|
2020-06-14 02:57:50 +02:00
|
|
|
return f'{var_name} starts with a "_" and is hence invalid.'
|
2019-08-18 12:56:21 +02:00
|
|
|
return None
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2019-08-18 12:56:21 +02:00
|
|
|
stripe_sample_config_options = [
|
2021-02-12 08:19:30 +01:00
|
|
|
WebhookIntegration(
|
2021-02-12 08:20:45 +01:00
|
|
|
"stripe",
|
|
|
|
["financial"],
|
|
|
|
display_name="Stripe",
|
2024-09-20 11:41:30 +02:00
|
|
|
config_options=[
|
|
|
|
WebhookConfigOption(
|
|
|
|
name="stripe_api_key", description="Stripe API key", validator=_check_string
|
|
|
|
)
|
|
|
|
],
|
2021-02-12 08:19:30 +01:00
|
|
|
),
|
2019-08-18 12:56:21 +02:00
|
|
|
]
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-03-22 05:58:54 +01:00
|
|
|
class BotTest(ZulipTestCase, UploadSerializeMixin):
|
2018-05-15 15:26:04 +02:00
|
|
|
def get_bot_user(self, email: str) -> UserProfile:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
bot = get_user(email, realm)
|
|
|
|
return bot
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def assert_num_bots_equal(self, count: int) -> None:
|
2017-03-08 12:07:51 +01:00
|
|
|
result = self.client_get("/json/bots")
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
|
|
|
self.assert_length(response_dict["bots"], count)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def create_bot(self, **extras: Any) -> dict[str, Any]:
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": "1",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
bot_info.update(extras)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
|
|
|
return response_dict
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bot_domain(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-05 04:17:12 +01:00
|
|
|
self.create_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(UserProfile.objects.filter(email="hambot-bot@zulip.testserver").exists())
|
2017-03-05 04:17:12 +01:00
|
|
|
# The other cases are hard to test directly, since we don't allow creating bots from
|
|
|
|
# the wrong subdomain, and because 'testserver.example.com' is not a valid domain for the bot's email.
|
2022-02-08 00:13:33 +01:00
|
|
|
# So we just test the Realm.get_bot_domain function.
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.assertEqual(realm.get_bot_domain(), "zulip.testserver")
|
2017-03-05 04:17:12 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def deactivate_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_delete(f"/json/bots/{self.get_bot_user(email).id}")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_bad_username(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2017-06-21 13:46:58 +02:00
|
|
|
|
|
|
|
# Invalid username
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="My bot name",
|
2022-07-27 23:33:49 +02:00
|
|
|
short_name="my\nbot",
|
2017-03-08 12:07:51 +01:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Bad name or username")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2017-06-21 13:46:58 +02:00
|
|
|
# Empty username
|
|
|
|
bot_info = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="My bot name",
|
|
|
|
short_name="",
|
2017-06-21 13:46:58 +02:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Bad name or username")
|
2017-06-21 13:46:58 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2020-08-01 11:23:28 +02:00
|
|
|
@override_settings(FAKE_EMAIL_DOMAIN="invaliddomain", REALM_HOSTS={"zulip": "127.0.0.1"})
|
2019-08-30 00:21:36 +02:00
|
|
|
def test_add_bot_with_invalid_fake_email_domain(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-30 00:21:36 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": "1",
|
2019-08-30 00:21:36 +02:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
error_message = (
|
|
|
|
"Can't create bots until FAKE_EMAIL_DOMAIN is correctly configured.\n"
|
2023-01-03 02:16:53 +01:00
|
|
|
"Please contact your server administrator."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-08-30 00:21:36 +02:00
|
|
|
self.assert_json_error(result, error_message)
|
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_no_name(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-05-12 04:21:49 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
bot_info = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="a",
|
|
|
|
short_name="bot",
|
2017-05-12 04:21:49 +02:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Name too short!")
|
2017-05-12 04:21:49 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_json_users_with_bots(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(hamlet)
|
2017-10-09 20:56:09 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2019-02-22 23:32:08 +01:00
|
|
|
num_bots = 3
|
2017-10-09 20:56:09 +02:00
|
|
|
for i in range(num_bots):
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name = f"Bot {i}"
|
|
|
|
short_name = f"bot-{i}"
|
2017-10-09 20:56:09 +02:00
|
|
|
bot_info = dict(
|
|
|
|
full_name=full_name,
|
|
|
|
short_name=short_name,
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
bot_type=1,
|
2017-10-09 20:56:09 +02:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assert_num_bots_equal(num_bots)
|
|
|
|
|
2023-09-27 02:10:49 +02:00
|
|
|
with self.assert_database_query_count(4):
|
2021-02-12 08:20:45 +01:00
|
|
|
users_result = self.client_get("/json/users")
|
2017-10-09 20:56:09 +02:00
|
|
|
|
|
|
|
self.assert_json_success(users_result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=4) as events:
|
2017-03-08 12:07:51 +01:00
|
|
|
result = self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2018-05-15 15:26:04 +02:00
|
|
|
bot = self.get_bot_user(email)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-08-02 23:16:44 +02:00
|
|
|
(event,) = (e for e in events if e["event"]["type"] == "realm_bot")
|
2020-03-19 17:13:09 +01:00
|
|
|
|
2020-08-11 23:01:01 +02:00
|
|
|
self.assertEqual(result["user_id"], bot.id)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(
|
|
|
|
dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
type="realm_bot",
|
|
|
|
op="add",
|
2020-03-12 14:17:25 +01:00
|
|
|
bot=dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="hambot-bot@zulip.testserver",
|
2020-03-12 14:17:25 +01:00
|
|
|
user_id=bot.id,
|
|
|
|
bot_type=bot.bot_type,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="The Bot of Hamlet",
|
2020-03-12 14:17:25 +01:00
|
|
|
is_active=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
api_key=result["api_key"],
|
|
|
|
avatar_url=result["avatar_url"],
|
2020-03-12 14:17:25 +01:00
|
|
|
default_sending_stream=None,
|
|
|
|
default_events_register_stream=None,
|
|
|
|
default_all_public_streams=False,
|
|
|
|
services=[],
|
2020-05-10 19:21:08 +02:00
|
|
|
owner_id=hamlet.id,
|
2020-03-12 14:17:25 +01:00
|
|
|
),
|
2017-03-08 12:07:51 +01:00
|
|
|
),
|
2021-02-12 08:20:45 +01:00
|
|
|
event["event"],
|
2017-03-08 12:07:51 +01:00
|
|
|
)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
users_result = self.client_get("/json/users")
|
|
|
|
members = orjson.loads(users_result.content)["members"]
|
2022-06-15 04:24:28 +02:00
|
|
|
[bot_dict] = [m for m in members if m["email"] == "hambot-bot@zulip.testserver"]
|
|
|
|
self.assertEqual(bot_dict["bot_owner_id"], self.example_user("hamlet").id)
|
|
|
|
self.assertEqual(bot_dict["user_id"], self.get_bot_user(email).id)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2020-08-01 11:23:28 +02:00
|
|
|
@override_settings(FAKE_EMAIL_DOMAIN="fakedomain.com", REALM_HOSTS={"zulip": "127.0.0.1"})
|
|
|
|
def test_add_bot_with_fake_email_domain(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-08-01 11:23:28 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@fakedomain.com"
|
2020-08-01 11:23:28 +02:00
|
|
|
self.get_bot_user(email)
|
|
|
|
|
2021-01-18 14:34:54 +01:00
|
|
|
@override_settings(EXTERNAL_HOST="example.com")
|
|
|
|
def test_add_bot_verify_subdomain_in_email_address(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-01-18 14:34:54 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.example.com"
|
2021-01-18 14:34:54 +01:00
|
|
|
self.get_bot_user(email)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
FAKE_EMAIL_DOMAIN="fakedomain.com", REALM_HOSTS={"zulip": "zulip.example.com"}
|
|
|
|
)
|
2020-08-01 11:23:28 +02:00
|
|
|
def test_add_bot_host_used_as_domain_if_valid(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-08-01 11:23:28 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.example.com"
|
2020-08-01 11:23:28 +02:00
|
|
|
self.get_bot_user(email)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_username_in_use(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-08-10 17:18:43 +02:00
|
|
|
self.create_bot()
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
# The short_name is used in the email, which we call
|
|
|
|
# "Username" for legacy reasons.
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="whatever",
|
|
|
|
short_name="hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Username already in use")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
dup_full_name = "The Bot of Hamlet"
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
|
|
|
bot_info = dict(
|
|
|
|
full_name=dup_full_name,
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever",
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Name is already in use!")
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_user_avatar(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
with get_test_image_file("img.png") as fp:
|
2017-03-08 12:07:51 +01:00
|
|
|
self.create_bot(file=fp)
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(email, realm)
|
2017-03-08 12:07:51 +01:00
|
|
|
# Make sure that avatar image that we've uploaded is same with avatar image in the server
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
filecmp.cmp(fp.name, os.path.splitext(avatar_disk_path(profile))[0] + ".original")
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER)
|
|
|
|
self.assertTrue(os.path.exists(avatar_disk_path(profile)))
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_too_many_files(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
with get_test_image_file("img.png") as fp1, get_test_image_file("img.gif") as fp2:
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="whatever",
|
|
|
|
short_name="whatever",
|
2017-03-08 12:07:51 +01:00
|
|
|
file1=fp1,
|
|
|
|
file2=fp2,
|
|
|
|
)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "You may only upload one file at a time")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_sending_stream(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.create_bot(default_sending_stream="Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["default_sending_stream"], "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(email, realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert profile.default_sending_stream is not None
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(profile.default_sending_stream.name, "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_sending_stream_not_subscribed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.create_bot(default_sending_stream="Rome")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["default_sending_stream"], "Rome")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(email, realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert profile.default_sending_stream is not None
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(profile.default_sending_stream.name, "Rome")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2019-09-23 22:51:31 +02:00
|
|
|
def test_add_bot_email_address_visibility(self) -> None:
|
|
|
|
# Test that we don't mangle the email field with
|
2023-01-02 20:50:23 +01:00
|
|
|
# email_address_visibility limited to admins
|
2019-09-23 22:51:31 +02:00
|
|
|
user = self.example_user("hamlet")
|
2021-10-26 09:15:16 +02:00
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=user.realm)
|
|
|
|
do_set_realm_user_default_setting(
|
|
|
|
realm_user_default,
|
2021-03-01 11:33:24 +01:00
|
|
|
"email_address_visibility",
|
2021-10-26 09:15:16 +02:00
|
|
|
RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_ADMINS,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-09-23 22:51:31 +02:00
|
|
|
user.refresh_from_db()
|
|
|
|
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2019-09-23 22:51:31 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=4) as events:
|
2019-09-23 22:51:31 +02:00
|
|
|
result = self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2019-09-23 22:51:31 +02:00
|
|
|
bot = self.get_bot_user(email)
|
|
|
|
|
2021-08-02 23:16:44 +02:00
|
|
|
(event,) = (e for e in events if e["event"]["type"] == "realm_bot")
|
2019-09-23 22:51:31 +02:00
|
|
|
self.assertEqual(
|
|
|
|
dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
type="realm_bot",
|
|
|
|
op="add",
|
2021-02-12 08:19:30 +01:00
|
|
|
bot=dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="hambot-bot@zulip.testserver",
|
2021-02-12 08:19:30 +01:00
|
|
|
user_id=bot.id,
|
|
|
|
bot_type=bot.bot_type,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="The Bot of Hamlet",
|
2021-02-12 08:19:30 +01:00
|
|
|
is_active=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
api_key=result["api_key"],
|
|
|
|
avatar_url=result["avatar_url"],
|
2021-02-12 08:19:30 +01:00
|
|
|
default_sending_stream=None,
|
|
|
|
default_events_register_stream=None,
|
|
|
|
default_all_public_streams=False,
|
|
|
|
services=[],
|
|
|
|
owner_id=user.id,
|
|
|
|
),
|
2019-09-23 22:51:31 +02:00
|
|
|
),
|
2021-02-12 08:20:45 +01:00
|
|
|
event["event"],
|
2019-09-23 22:51:31 +02:00
|
|
|
)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
users_result = self.client_get("/json/users")
|
|
|
|
members = orjson.loads(users_result.content)["members"]
|
2022-06-15 04:24:28 +02:00
|
|
|
[bot_dict] = [m for m in members if m["email"] == "hambot-bot@zulip.testserver"]
|
|
|
|
self.assertEqual(bot_dict["bot_owner_id"], user.id)
|
|
|
|
self.assertEqual(bot_dict["user_id"], self.get_bot_user(email).id)
|
2019-09-23 22:51:31 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bot_add_subscription(self) -> None:
|
2017-03-08 12:07:51 +01:00
|
|
|
"""
|
|
|
|
Calling POST /json/users/me/subscriptions should successfully add
|
|
|
|
streams, and a stream to the
|
|
|
|
list of subscriptions and confirm the right number of events
|
|
|
|
are generated.
|
|
|
|
When 'principals' has a bot, no notification message event or invitation email
|
2020-10-23 02:43:28 +02:00
|
|
|
is sent when add_subscriptions_backend is called in the above API call.
|
2017-03-08 12:07:51 +01:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
iago = self.example_user("iago")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(hamlet)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
# Normal user i.e. not a bot.
|
|
|
|
request_data = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"principals": '["' + iago.email + '"]',
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=3) as events:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.common_subscribe_to_streams(hamlet, ["Rome"], request_data)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_event = [e for e in events if e["event"]["type"] == "message"]
|
2017-05-07 17:17:56 +02:00
|
|
|
self.assert_length(msg_event, 1) # Notification message event is sent.
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
# Create a bot.
|
|
|
|
self.assert_num_bots_equal(0)
|
2021-08-10 17:18:43 +02:00
|
|
|
self.create_bot()
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
# A bot
|
|
|
|
bot_request_data = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"principals": '["hambot-bot@zulip.testserver"]',
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=2) as events_bot:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.common_subscribe_to_streams(hamlet, ["Rome"], bot_request_data)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# No notification message event or invitation email is sent because of bot.
|
2021-02-12 08:20:45 +01:00
|
|
|
msg_event = [e for e in events_bot if e["event"]["type"] == "message"]
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_length(msg_event, 0)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(events_bot, len(events) - 1)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
# Test runner automatically redirects all sent email to a dummy 'outbox'.
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(mail.outbox, 0)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_sending_stream_private_allowed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
user_profile = self.example_user("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
stream = get_stream("Denmark", user_profile.realm)
|
2017-08-25 06:01:29 +02:00
|
|
|
self.subscribe(user_profile, stream.name)
|
2022-08-01 12:54:46 +02:00
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=user_profile,
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
self.assert_num_bots_equal(0)
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=4) as events:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.create_bot(default_sending_stream="Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["default_sending_stream"], "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(email, realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert profile.default_sending_stream is not None
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(profile.default_sending_stream.name, "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-08-02 23:16:44 +02:00
|
|
|
(event,) = (e for e in events if e["event"]["type"] == "realm_bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(
|
|
|
|
dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
type="realm_bot",
|
|
|
|
op="add",
|
2020-03-12 14:17:25 +01:00
|
|
|
bot=dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="hambot-bot@zulip.testserver",
|
2020-03-12 14:17:25 +01:00
|
|
|
user_id=profile.id,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="The Bot of Hamlet",
|
2020-03-12 14:17:25 +01:00
|
|
|
bot_type=profile.bot_type,
|
|
|
|
is_active=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
api_key=result["api_key"],
|
|
|
|
avatar_url=result["avatar_url"],
|
|
|
|
default_sending_stream="Denmark",
|
2020-03-12 14:17:25 +01:00
|
|
|
default_events_register_stream=None,
|
|
|
|
default_all_public_streams=False,
|
|
|
|
services=[],
|
2020-05-10 19:21:08 +02:00
|
|
|
owner_id=user_profile.id,
|
2020-03-12 14:17:25 +01:00
|
|
|
),
|
2017-03-08 12:07:51 +01:00
|
|
|
),
|
2021-02-12 08:20:45 +01:00
|
|
|
event["event"],
|
2017-03-08 12:07:51 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["users"], [user_profile.id])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_sending_stream_private_denied(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
realm = self.example_user("hamlet").realm
|
2017-07-12 21:10:18 +02:00
|
|
|
stream = get_stream("Denmark", realm)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.unsubscribe(self.example_user("hamlet"), "Denmark")
|
2021-12-11 00:41:25 +01:00
|
|
|
do_change_stream_permission(
|
2022-08-01 12:54:46 +02:00
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=self.example_user("hamlet"),
|
2021-12-11 00:41:25 +01:00
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"default_sending_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'Denmark'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_events_register_stream(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.create_bot(default_events_register_stream="Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["default_events_register_stream"], "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(bot_email, bot_realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert profile.default_events_register_stream is not None
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(profile.default_events_register_stream.name, "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_events_register_stream_private_allowed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
stream = self.subscribe(user_profile, "Denmark")
|
2022-08-01 12:54:46 +02:00
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=user_profile,
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
self.assert_num_bots_equal(0)
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=4) as events:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.create_bot(default_events_register_stream="Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["default_events_register_stream"], "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
bot_profile = get_user(bot_email, bot_realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert bot_profile.default_events_register_stream is not None
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(bot_profile.default_events_register_stream.name, "Denmark")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-08-02 23:16:44 +02:00
|
|
|
(event,) = (e for e in events if e["event"]["type"] == "realm_bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(
|
|
|
|
dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
type="realm_bot",
|
|
|
|
op="add",
|
2020-03-12 14:17:25 +01:00
|
|
|
bot=dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="hambot-bot@zulip.testserver",
|
|
|
|
full_name="The Bot of Hamlet",
|
2020-03-12 14:17:25 +01:00
|
|
|
user_id=bot_profile.id,
|
|
|
|
bot_type=bot_profile.bot_type,
|
|
|
|
is_active=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
api_key=result["api_key"],
|
|
|
|
avatar_url=result["avatar_url"],
|
2020-03-12 14:17:25 +01:00
|
|
|
default_sending_stream=None,
|
2021-02-12 08:20:45 +01:00
|
|
|
default_events_register_stream="Denmark",
|
2020-03-12 14:17:25 +01:00
|
|
|
default_all_public_streams=False,
|
|
|
|
services=[],
|
2020-05-10 19:21:08 +02:00
|
|
|
owner_id=user_profile.id,
|
2020-03-12 14:17:25 +01:00
|
|
|
),
|
2017-03-08 12:07:51 +01:00
|
|
|
),
|
2021-02-12 08:20:45 +01:00
|
|
|
event["event"],
|
2017-03-08 12:07:51 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["users"], [user_profile.id])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_events_register_stream_private_denied(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
realm = self.example_user("hamlet").realm
|
2017-07-12 21:10:18 +02:00
|
|
|
stream = get_stream("Denmark", realm)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.unsubscribe(self.example_user("hamlet"), "Denmark")
|
2021-12-11 00:41:25 +01:00
|
|
|
do_change_stream_permission(
|
2022-08-01 12:54:46 +02:00
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=self.example_user("hamlet"),
|
2021-12-11 00:41:25 +01:00
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"default_events_register_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'Denmark'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_default_all_public_streams(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
2020-08-07 01:09:47 +02:00
|
|
|
result = self.create_bot(default_all_public_streams=orjson.dumps(True).decode())
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(result["default_all_public_streams"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(bot_email, bot_realm)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(profile.default_all_public_streams, True)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_deactivate_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
self.deactivate_bot()
|
|
|
|
# You can deactivate the same bot twice.
|
|
|
|
self.deactivate_bot()
|
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2024-09-07 23:01:34 +02:00
|
|
|
def test_deactivate_bot_invalid_id(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
2024-09-07 23:01:34 +02:00
|
|
|
invalid_bot_id = 1000
|
|
|
|
result = self.client_delete(f"/json/bots/{invalid_bot_id}")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "No such bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2024-08-20 23:15:19 +02:00
|
|
|
def test_deactivate_bot_with_no_owners(self) -> None:
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
self.login("iago")
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
# Set up the bot to be a private bot, as otherwise realm admins
|
|
|
|
# are used as default owners in the absence of .bot_owner_id.
|
|
|
|
stream = get_stream("Denmark", get_realm("zulip"))
|
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=iago,
|
|
|
|
)
|
|
|
|
|
|
|
|
new_bot = assert_is_not_none(UserProfile.objects.last())
|
|
|
|
do_change_default_sending_stream(new_bot, stream, acting_user=iago)
|
|
|
|
new_bot.bot_owner_id = None
|
|
|
|
new_bot.save()
|
|
|
|
|
|
|
|
result = self.client_delete(f"/json/bots/{new_bot.id}")
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2018-02-04 19:47:12 +01:00
|
|
|
def test_deactivate_bot_with_owner_deactivation(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2018-02-04 19:47:12 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2018-02-04 19:47:12 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Another Bot of Hamlet",
|
|
|
|
"short_name": "hambot-another",
|
2018-02-04 19:47:12 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
UserProfile.objects.filter(is_bot=True, bot_owner=user, is_active=True).count(), 2
|
|
|
|
)
|
2018-02-04 19:47:12 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_delete("/json/users/me")
|
2018-02-04 19:47:12 +01:00
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2018-02-04 19:47:12 +01:00
|
|
|
self.assertFalse(user.is_active)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertFalse(
|
|
|
|
UserProfile.objects.filter(is_bot=True, bot_owner=user, is_active=True).exists()
|
|
|
|
)
|
2018-02-04 19:47:12 +01:00
|
|
|
|
2018-05-15 15:26:04 +02:00
|
|
|
def test_cannot_deactivate_other_realm_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.mit_user("starnine")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2018-05-15 15:26:04 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot in zephyr",
|
|
|
|
"short_name": "starn-bot",
|
|
|
|
"bot_type": "1",
|
2018-05-15 15:26:04 +02:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info, subdomain="zephyr")
|
|
|
|
self.assert_json_success(result)
|
|
|
|
result = self.client_get("/json/bots", subdomain="zephyr")
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
|
|
|
bot_email = response_dict["bots"][0]["username"]
|
2020-03-06 18:40:46 +01:00
|
|
|
bot = get_user(bot_email, user.realm)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_delete(f"/json/bots/{bot.id}")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "No such bot")
|
2018-05-15 15:26:04 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bot_deactivation_attacks(self) -> None:
|
2017-03-08 12:07:51 +01:00
|
|
|
"""You cannot deactivate somebody else's bot."""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
# Have Othello try to deactivate both Hamlet and
|
|
|
|
# Hamlet's bot.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("othello")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2018-04-03 00:36:31 +02:00
|
|
|
# Cannot deactivate a user as a bot
|
2018-05-15 15:26:04 +02:00
|
|
|
result = self.client_delete("/json/bots/{}".format(self.example_user("hamlet").id))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "No such bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_delete(f"/json/bots/{self.get_bot_user(email).id}")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Insufficient permission")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
# But we don't actually deactivate the other person's bot.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2018-04-03 00:36:31 +02:00
|
|
|
# Cannot deactivate a bot as a user
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_delete(f"/json/users/{self.get_bot_user(email).id}")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "No such user")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2023-03-29 19:01:19 +02:00
|
|
|
def test_activate_bot_with_duplicate_name(self) -> None:
|
|
|
|
self.login("iago")
|
|
|
|
# Create a bot and then deactivate it
|
|
|
|
original_name = "Hamlet"
|
|
|
|
bot_info = {
|
|
|
|
"full_name": original_name,
|
|
|
|
"short_name": "hambot",
|
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot = self.get_bot_user(bot_email)
|
|
|
|
do_deactivate_user(bot, False, acting_user=None)
|
|
|
|
self.assertFalse(
|
|
|
|
UserProfile.objects.filter(is_bot=True, id=bot.id, is_active=True).exists()
|
|
|
|
)
|
|
|
|
|
|
|
|
# Create another bot with the same name
|
|
|
|
bot_info2 = {
|
|
|
|
"full_name": original_name,
|
|
|
|
"short_name": "hambot2",
|
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info2)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
result = self.client_post(f"/json/users/{bot.id}/reactivate")
|
|
|
|
self.assert_json_error(
|
|
|
|
result,
|
|
|
|
'There is already an active bot named "Hamlet" in this organization. To reactivate this bot, you must rename or deactivate the other one first.',
|
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bot_permissions(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
# Have Othello try to mess with Hamlet's bots.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("othello")
|
|
|
|
email = "hambot-bot@zulip.testserver"
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_post(f"/json/bots/{self.get_bot_user(email).id}/api_key/regenerate")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Insufficient permission")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Fred",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Insufficient permission")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def get_bot(self) -> dict[str, Any]:
|
2017-03-08 12:07:51 +01:00
|
|
|
result = self.client_get("/json/bots")
|
2022-06-07 01:37:01 +02:00
|
|
|
return self.assert_json_success(result)["bots"][0]
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_update_api_key(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.create_bot()
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
old_api_key = bot["api_key"]
|
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
result = self.client_post(f"/json/bots/{self.get_bot_user(email).id}/api_key/regenerate")
|
2022-06-07 01:37:01 +02:00
|
|
|
new_api_key = self.assert_json_success(result)["api_key"]
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertNotEqual(old_api_key, new_api_key)
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(new_api_key, bot["api_key"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_update_api_key_for_invalid_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-05-15 18:13:07 +02:00
|
|
|
invalid_user_id = 1000
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post(f"/json/bots/{invalid_user_id}/api_key/regenerate")
|
|
|
|
self.assert_json_error(result, "No such bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_bot_type_default(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-30 19:19:48 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-05-30 19:19:48 +02:00
|
|
|
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)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_bot_type_incoming_webhook(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-30 19:19:48 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-05-30 19:19:48 +02:00
|
|
|
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)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_bot_with_bot_type_invalid(self) -> None:
|
2017-06-07 06:16:18 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": 7,
|
2017-06-07 06:16:18 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-06-07 06:16:18 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid bot type")
|
2017-06-07 06:16:18 +02:00
|
|
|
|
2018-01-29 16:10:54 +01:00
|
|
|
def test_no_generic_bots_allowed_for_non_admins(self) -> None:
|
2017-11-24 16:24:24 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": 1,
|
2017-11-24 16:24:24 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2024-05-22 11:43:10 +02:00
|
|
|
bot_realm.bot_creation_policy = BotCreationPolicyEnum.LIMIT_GENERIC_BOTS
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm.save(update_fields=["bot_creation_policy"])
|
2017-11-24 16:24:24 +01:00
|
|
|
|
|
|
|
# A regular user cannot create a generic bot
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-11-24 16:24:24 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
2017-11-24 16:24:24 +01:00
|
|
|
|
|
|
|
# But can create an incoming webhook
|
|
|
|
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)
|
|
|
|
|
2018-07-27 18:18:33 +02:00
|
|
|
def test_no_generic_bot_reactivation_allowed_for_non_admins(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-07-27 18:18:33 +02:00
|
|
|
self.create_bot(bot_type=UserProfile.DEFAULT_BOT)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2024-05-22 11:43:10 +02:00
|
|
|
bot_realm.bot_creation_policy = BotCreationPolicyEnum.LIMIT_GENERIC_BOTS
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm.save(update_fields=["bot_creation_policy"])
|
2018-07-27 18:18:33 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
2018-07-27 18:18:33 +02:00
|
|
|
bot_user = get_user(bot_email, bot_realm)
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(bot_user, acting_user=None)
|
2018-07-27 18:18:33 +02:00
|
|
|
|
|
|
|
# A regular user cannot reactivate a generic bot
|
|
|
|
self.assert_num_bots_equal(0)
|
2020-06-10 06:41:04 +02:00
|
|
|
result = self.client_post(f"/json/users/{bot_user.id}/reactivate")
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
2018-07-27 18:18:33 +02:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
|
2018-01-29 16:10:54 +01:00
|
|
|
def test_no_generic_bots_allowed_for_admins(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2024-05-22 11:43:10 +02:00
|
|
|
bot_realm.bot_creation_policy = BotCreationPolicyEnum.LIMIT_GENERIC_BOTS
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm.save(update_fields=["bot_creation_policy"])
|
2018-01-29 16:10:54 +01:00
|
|
|
|
|
|
|
# An administrator can create any type of bot
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2018-01-29 16:10:54 +01:00
|
|
|
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)
|
|
|
|
|
|
|
|
def test_no_bots_allowed_for_non_admins(self) -> None:
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": 1,
|
2018-01-29 16:10:54 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2024-05-22 11:43:10 +02:00
|
|
|
bot_realm.bot_creation_policy = BotCreationPolicyEnum.ADMINS_ONLY
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm.save(update_fields=["bot_creation_policy"])
|
2018-01-29 16:10:54 +01:00
|
|
|
|
|
|
|
# A regular user cannot create a generic bot
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-01-29 16:10:54 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
2018-01-29 16:10:54 +01:00
|
|
|
|
|
|
|
# Also, a regular user cannot create a incoming bot
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_info["bot_type"] = 2
|
|
|
|
self.login("hamlet")
|
2018-01-29 16:10:54 +01:00
|
|
|
self.assert_num_bots_equal(0)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_num_bots_equal(0)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
2018-01-29 16:10:54 +01:00
|
|
|
|
|
|
|
def test_no_bots_allowed_for_admins(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2024-05-22 11:43:10 +02:00
|
|
|
bot_realm.bot_creation_policy = BotCreationPolicyEnum.ADMINS_ONLY
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm.save(update_fields=["bot_creation_policy"])
|
2017-11-24 16:24:24 +01:00
|
|
|
|
|
|
|
# An administrator can create any type of bot
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2017-11-24 16:24:24 +01:00
|
|
|
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)
|
|
|
|
|
2022-04-21 16:04:52 +02:00
|
|
|
def test_reactivating_bot_with_deactivated_owner(self) -> None:
|
|
|
|
self.login("hamlet")
|
|
|
|
bot_info = {
|
|
|
|
"full_name": "Test bot",
|
|
|
|
"short_name": "testbot",
|
|
|
|
"bot_type": "1",
|
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
bot_id = result.json()["user_id"]
|
|
|
|
|
|
|
|
test_bot = UserProfile.objects.get(id=bot_id, is_bot=True)
|
2022-05-16 09:13:41 +02:00
|
|
|
private_stream = self.make_stream("private_stream", invite_only=True)
|
|
|
|
public_stream = self.make_stream("public_stream")
|
|
|
|
self.subscribe(test_bot, "private_stream")
|
|
|
|
self.subscribe(self.example_user("hamlet"), "private_stream")
|
|
|
|
self.subscribe(test_bot, "public_stream")
|
|
|
|
self.subscribe(self.example_user("hamlet"), "public_stream")
|
|
|
|
|
|
|
|
private_stream_test = self.make_stream("private_stream_test", invite_only=True)
|
|
|
|
self.subscribe(self.example_user("iago"), "private_stream_test")
|
|
|
|
self.subscribe(test_bot, "private_stream_test")
|
|
|
|
|
2022-04-21 16:04:52 +02:00
|
|
|
do_deactivate_user(test_bot, acting_user=None)
|
|
|
|
|
|
|
|
# Deactivate the bot owner
|
|
|
|
do_deactivate_user(self.example_user("hamlet"), acting_user=None)
|
|
|
|
|
|
|
|
self.login("iago")
|
|
|
|
result = self.client_post(f"/json/users/{bot_id}/reactivate")
|
|
|
|
self.assert_json_success(result)
|
|
|
|
test_bot = UserProfile.objects.get(id=bot_id, is_bot=True)
|
|
|
|
assert test_bot.bot_owner is not None
|
|
|
|
self.assertEqual(test_bot.bot_owner.id, self.example_user("iago").id)
|
|
|
|
|
2022-05-16 09:13:41 +02:00
|
|
|
self.assertFalse(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=test_bot, recipient__type_id=private_stream.id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=test_bot, recipient__type_id=private_stream_test.id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=test_bot, recipient__type_id=public_stream.id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_full_name(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Fred",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Fred", response_dict["full_name"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Fred", bot["full_name"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
def test_patch_bot_full_name_in_use(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
original_name = "The Bot of Hamlet"
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": original_name,
|
|
|
|
"short_name": "hambot",
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
bot = self.get_bot_user(bot_email)
|
2020-06-09 00:25:09 +02:00
|
|
|
url = f"/json/bots/{bot.id}"
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
|
|
|
# It doesn't matter whether a name is taken by a human
|
|
|
|
# or a bot, we can't use it.
|
2021-02-12 08:20:45 +01:00
|
|
|
already_taken_name = self.example_user("cordelia").full_name
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": already_taken_name,
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
}
|
|
|
|
result = self.client_patch(url, bot_info)
|
|
|
|
self.assert_json_error(result, "Name is already in use!")
|
|
|
|
|
|
|
|
# We can use our own name (with extra whitespace), and the
|
|
|
|
# server should silently do nothing.
|
2021-02-12 08:20:45 +01:00
|
|
|
original_name_with_padding = " " + original_name + " "
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": original_name_with_padding,
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
}
|
|
|
|
result = self.client_patch(url, bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot = self.get_bot_user(bot_email)
|
|
|
|
self.assertEqual(bot.full_name, original_name)
|
|
|
|
|
|
|
|
# And let's do a sanity check with an actual name change
|
|
|
|
# after our various attempts that either failed or did
|
|
|
|
# nothing.
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Hal",
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
}
|
|
|
|
result = self.client_patch(url, bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot = self.get_bot_user(bot_email)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(bot.full_name, "Hal")
|
bots: Prevent bots from having duplicate full names.
Bots are not allowed to use the same name as
other users in the realm (either bot or human).
This is kind of a big commit, but I wanted to
combine the post/patch (aka add/edit) checks
into one commit, since it's a change in policy
that affects both codepaths.
A lot of the noise is in tests. We had good
coverage on the previous code, including some places
like event testing where we were expediently
not bothering to use different names for
different bots in some longer tests. And then
of course I test some new scenarios that are relevant
with the new policy.
There are two new functions:
check_bot_name_available:
very simple Django query
check_change_bot_full_name:
this diverges from the 3-line
check_change_full_name, where the latter
is still used for the "humans" use case
And then we just call those in appropriate places.
Note that there is still a loophole here
where you can get two bots with the same
name if you reactivate a bot named Fred
that was inactive when the second bot named
Fred was created. Also, we don't attempt
to fix historical data. So this commit
shouldn't be considered any kind of lockdown,
it's just meant to help people from
inadvertently creating two bots of the same
name where they don't intend to. For more
context, we are continuing to allow two
human users in the same realm to have the
same full name, and our code should generally
be tolerant of that possibility. (A good
example is our new mention syntax, which disambiguates
same-named people using ids.)
It's also worth noting that our web app client
doesn't try to scrub full_name from its payload in
situations where the user has actually only modified other
fields in the "Edit bot" UI. Starting here
we just handle this on the server, since it's
easy to fix there, and even if we fixed it in the web
app, there's no guarantee that other clients won't be
just as brute force. It wasn't exactly broken before,
but we'd needlessly write rows to audit tables.
Fixes #10509
2018-09-27 19:25:18 +02:00
|
|
|
|
2018-04-03 03:55:51 +02:00
|
|
|
def test_patch_bot_full_name_non_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2018-04-03 03:55:51 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Fred",
|
2018-04-03 03:55:51 +02:00
|
|
|
}
|
2018-05-15 15:26:04 +02:00
|
|
|
result = self.client_patch("/json/bots/{}".format(self.example_user("hamlet").id), bot_info)
|
2018-04-03 03:55:51 +02:00
|
|
|
self.assert_json_error(result, "No such bot")
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_owner(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
othello = self.example_user("othello")
|
2020-03-12 14:17:25 +01:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
bot_info: dict[str, object] = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": othello.id,
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
# Test bot's owner has been changed successfully.
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual(response_dict["bot_owner"], othello.email)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("othello")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("The Bot of Hamlet", bot["full_name"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2018-06-19 07:42:04 +02:00
|
|
|
def test_patch_bot_owner_bad_user_id(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-02-10 14:17:04 +01:00
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
|
|
|
profile = get_user("hambot-bot@zulip.testserver", get_realm("zulip"))
|
2018-02-10 14:17:04 +01:00
|
|
|
|
2018-06-26 22:27:10 +02:00
|
|
|
bad_bot_owner_id = 999999
|
2018-02-10 14:17:04 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": bad_bot_owner_id,
|
2018-02-10 14:17:04 +01:00
|
|
|
}
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-02-10 14:17:04 +01:00
|
|
|
self.assert_json_error(result, "Failed to change owner, no such user")
|
2021-02-12 08:20:45 +01:00
|
|
|
profile = get_user("hambot-bot@zulip.testserver", get_realm("zulip"))
|
2018-02-10 14:17:04 +01:00
|
|
|
self.assertEqual(profile.bot_owner, self.example_user("hamlet"))
|
|
|
|
|
2018-02-13 11:54:16 +01:00
|
|
|
def test_patch_bot_owner_deactivated(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-02-13 11:54:16 +01:00
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
target_user_profile = self.example_user("othello")
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(target_user_profile, acting_user=None)
|
2021-02-12 08:20:45 +01:00
|
|
|
target_user_profile = self.example_user("othello")
|
2018-02-13 11:54:16 +01:00
|
|
|
self.assertFalse(target_user_profile.is_active)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": self.example_user("othello").id,
|
2018-02-13 11:54:16 +01:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-02-13 11:54:16 +01:00
|
|
|
self.assert_json_error(result, "Failed to change owner, user is deactivated")
|
2018-05-15 15:26:04 +02:00
|
|
|
profile = self.get_bot_user(email)
|
2018-02-13 11:54:16 +01:00
|
|
|
self.assertEqual(profile.bot_owner, self.example_user("hamlet"))
|
|
|
|
|
2018-06-19 07:42:04 +02:00
|
|
|
def test_patch_bot_owner_must_be_in_same_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-06-19 07:42:04 +02:00
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": self.mit_user("starnine").id,
|
2018-06-19 07:42:04 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-06-19 07:42:04 +02:00
|
|
|
self.assert_json_error(result, "Failed to change owner, no such user")
|
|
|
|
profile = self.get_bot_user(email)
|
|
|
|
self.assertEqual(profile.bot_owner, self.example_user("hamlet"))
|
|
|
|
|
2018-05-23 00:03:31 +02:00
|
|
|
def test_patch_bot_owner_noop(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-05-23 00:03:31 +02:00
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": self.example_user("hamlet").id,
|
2018-05-23 00:03:31 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-05-23 00:03:31 +02:00
|
|
|
|
|
|
|
# Check that we're still the owner
|
|
|
|
self.assert_json_success(result)
|
|
|
|
profile = self.get_bot_user(email)
|
|
|
|
self.assertEqual(profile.bot_owner, self.example_user("hamlet"))
|
|
|
|
|
2018-02-13 11:54:16 +01:00
|
|
|
def test_patch_bot_owner_a_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-02-13 11:54:16 +01:00
|
|
|
self.create_bot()
|
|
|
|
self.assert_num_bots_equal(1)
|
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
bot_info: dict[str, object] = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Another Bot of Hamlet",
|
|
|
|
"short_name": "hamelbot",
|
2018-02-13 11:54:16 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"bot_owner_id": self.get_bot_user("hamelbot-bot@zulip.testserver").id,
|
2018-02-13 11:54:16 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-02-13 11:54:16 +01:00
|
|
|
self.assert_json_error(result, "Failed to change owner, bots can't own other bots")
|
2021-02-12 08:20:45 +01:00
|
|
|
profile = get_user(email, get_realm("zulip"))
|
2018-02-13 11:54:16 +01:00
|
|
|
self.assertEqual(profile.bot_owner, self.example_user("hamlet"))
|
|
|
|
|
2022-01-07 21:47:11 +01:00
|
|
|
def test_patch_bot_owner_of_bot_with_can_create_users(self) -> None:
|
|
|
|
"""
|
|
|
|
can_create_users is granted to organizations upon approval, and thus
|
|
|
|
should be thought of as something that only organization owners should
|
|
|
|
have control over.
|
|
|
|
"""
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
|
|
|
|
self.login("hamlet")
|
|
|
|
self.create_bot()
|
|
|
|
|
|
|
|
bot_realm = get_realm("zulip")
|
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_user = get_user(bot_email, bot_realm)
|
|
|
|
|
|
|
|
do_change_can_create_users(bot_user, True)
|
|
|
|
|
|
|
|
self.logout()
|
|
|
|
# iago is an ordinary organization administrator, and thus doesn't have
|
|
|
|
# sufficient permissions to change ownership of this bot.
|
|
|
|
self.login("iago")
|
|
|
|
bot_info = {
|
|
|
|
"bot_owner_id": cordelia.id,
|
|
|
|
}
|
|
|
|
result = self.client_patch(f"/json/bots/{bot_user.id}", bot_info)
|
|
|
|
self.assert_json_error(
|
|
|
|
result,
|
|
|
|
"Must be an organization owner",
|
|
|
|
)
|
|
|
|
|
|
|
|
self.logout()
|
|
|
|
# desdemona is the organization owner and should be allowed to change the bot's ownership.
|
|
|
|
self.login("desdemona")
|
|
|
|
result = self.client_patch(f"/json/bots/{bot_user.id}", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_user.refresh_from_db()
|
|
|
|
self.assertEqual(bot_user.bot_owner, cordelia)
|
|
|
|
|
2022-05-07 08:56:33 +02:00
|
|
|
def test_patch_bot_owner_with_private_streams(self) -> None:
|
|
|
|
self.login("iago")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.create_bot()
|
|
|
|
|
|
|
|
bot_realm = get_realm("zulip")
|
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_user = get_user(bot_email, bot_realm)
|
|
|
|
|
|
|
|
private_stream = self.make_stream("private_stream", invite_only=True)
|
|
|
|
public_stream = self.make_stream("public_stream")
|
|
|
|
self.subscribe(bot_user, "private_stream")
|
|
|
|
self.subscribe(self.example_user("iago"), "private_stream")
|
|
|
|
self.subscribe(bot_user, "public_stream")
|
|
|
|
self.subscribe(self.example_user("iago"), "public_stream")
|
|
|
|
|
|
|
|
private_stream_test = self.make_stream("private_stream_test", invite_only=True)
|
|
|
|
self.subscribe(self.example_user("hamlet"), "private_stream_test")
|
|
|
|
self.subscribe(bot_user, "private_stream_test")
|
|
|
|
|
|
|
|
bot_info = {
|
|
|
|
"bot_owner_id": hamlet.id,
|
|
|
|
}
|
|
|
|
result = self.client_patch(f"/json/bots/{bot_user.id}", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_user = get_user(bot_email, bot_realm)
|
|
|
|
assert bot_user.bot_owner is not None
|
|
|
|
self.assertEqual(bot_user.bot_owner.id, hamlet.id)
|
|
|
|
|
|
|
|
assert private_stream.recipient_id is not None
|
|
|
|
self.assertFalse(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=bot_user, recipient_id=private_stream.recipient_id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
assert private_stream_test.recipient_id is not None
|
|
|
|
self.assertTrue(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=bot_user, recipient_id=private_stream_test.recipient_id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
assert public_stream.recipient_id is not None
|
|
|
|
self.assertTrue(
|
|
|
|
Subscription.objects.filter(
|
|
|
|
user_profile=bot_user, recipient_id=public_stream.recipient_id, active=True
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_avatar(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(bot_email, bot_realm)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2017-03-08 12:07:51 +01:00
|
|
|
# Try error case first (too many files):
|
2021-02-12 08:20:45 +01:00
|
|
|
with get_test_image_file("img.png") as fp1, get_test_image_file("img.gif") as fp2:
|
2017-03-08 12:07:51 +01:00
|
|
|
result = self.client_patch_multipart(
|
2021-02-12 08:20:45 +01:00
|
|
|
f"/json/bots/{self.get_bot_user(email).id}", dict(file1=fp1, file2=fp2)
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "You may only upload one file at a time")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(bot_email, bot_realm)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(profile.avatar_version, 1)
|
|
|
|
|
|
|
|
# HAPPY PATH
|
2021-02-12 08:20:45 +01:00
|
|
|
with get_test_image_file("img.png") as fp:
|
2017-03-08 12:07:51 +01:00
|
|
|
result = self.client_patch_multipart(
|
2021-02-12 08:20:45 +01:00
|
|
|
f"/json/bots/{self.get_bot_user(email).id}", dict(file=fp)
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-05-23 20:57:59 +02:00
|
|
|
profile = get_user(bot_email, bot_realm)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(profile.avatar_version, 2)
|
|
|
|
# Make sure that avatar image that we've uploaded is same with avatar image in the server
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
filecmp.cmp(fp.name, os.path.splitext(avatar_disk_path(profile))[0] + ".original")
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER)
|
|
|
|
self.assertTrue(os.path.exists(avatar_disk_path(profile)))
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Denmark", response_dict["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Denmark", bot["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream_not_subscribed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "Rome",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Rome", response_dict["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Rome", bot["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream_none(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2017-05-23 20:57:59 +02:00
|
|
|
default_sending_stream = get_user(bot_email, bot_realm).default_sending_stream
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(None, default_sending_stream)
|
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(None, bot["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-05-07 09:03:02 +02:00
|
|
|
def test_patch_bot_role(self) -> None:
|
|
|
|
self.login("desdemona")
|
|
|
|
|
|
|
|
email = "default-bot@zulip.com"
|
|
|
|
user_profile = self.get_bot_user(email)
|
|
|
|
|
|
|
|
do_change_user_role(user_profile, UserProfile.ROLE_MEMBER, acting_user=user_profile)
|
|
|
|
|
|
|
|
req = dict(role=UserProfile.ROLE_GUEST)
|
|
|
|
|
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", req)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
user_profile = self.get_bot_user(email)
|
|
|
|
self.assertEqual(user_profile.role, UserProfile.ROLE_GUEST)
|
|
|
|
|
|
|
|
# Test for not allowing a non-owner user to make assign a bot an owner role
|
|
|
|
desdemona = self.example_user("desdemona")
|
|
|
|
do_change_user_role(desdemona, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
|
|
|
|
|
|
|
|
req = dict(role=UserProfile.ROLE_REALM_OWNER)
|
|
|
|
|
2022-07-19 04:00:51 +02:00
|
|
|
result = self.client_patch(f"/json/users/{user_profile.id}", req)
|
|
|
|
self.assert_json_error(result, "Must be an organization owner")
|
|
|
|
|
|
|
|
result = self.client_patch(f"/json/bots/{user_profile.id}", req)
|
2022-05-07 09:03:02 +02:00
|
|
|
self.assert_json_error(result, "Must be an organization owner")
|
|
|
|
|
2022-07-19 04:00:51 +02:00
|
|
|
# Test for not allowing a non-administrator user to assign a bot an administrator role
|
|
|
|
shiva = self.example_user("shiva")
|
|
|
|
self.assertEqual(shiva.role, UserProfile.ROLE_MODERATOR)
|
|
|
|
self.login_user(shiva)
|
|
|
|
do_change_bot_owner(user_profile, shiva, acting_user=None)
|
|
|
|
|
|
|
|
req = dict(role=UserProfile.ROLE_REALM_ADMINISTRATOR)
|
|
|
|
|
|
|
|
result = self.client_patch(f"/json/users/{user_profile.id}", req)
|
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
|
|
|
|
|
|
|
result = self.client_patch(f"/json/bots/{user_profile.id}", req)
|
|
|
|
self.assert_json_error(result, "Must be an organization administrator")
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream_private_allowed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
user_profile = self.example_user("hamlet")
|
2017-08-25 06:01:29 +02:00
|
|
|
stream = self.subscribe(user_profile, "Denmark")
|
2022-08-01 12:54:46 +02:00
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=user_profile,
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Denmark", response_dict["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Denmark", bot["default_sending_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream_private_denied(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
realm = self.example_user("hamlet").realm
|
2017-07-12 21:10:18 +02:00
|
|
|
stream = get_stream("Denmark", realm)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.unsubscribe(self.example_user("hamlet"), "Denmark")
|
2021-12-11 00:41:25 +01:00
|
|
|
do_change_stream_permission(
|
2022-08-01 12:54:46 +02:00
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=self.example_user("hamlet"),
|
2021-12-11 00:41:25 +01:00
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'Denmark'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_to_stream_not_found(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_sending_stream": "missing",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'missing'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_events_register_stream(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(hamlet)
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
2018-08-15 18:06:53 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2018-08-15 18:06:53 +02:00
|
|
|
bot_user = self.get_bot_user(email)
|
2020-06-09 00:25:09 +02:00
|
|
|
url = f"/json/bots/{bot_user.id}"
|
2018-08-15 18:06:53 +02:00
|
|
|
|
|
|
|
# Successfully give the bot a default stream.
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "Denmark"
|
2018-08-15 18:06:53 +02:00
|
|
|
bot_info = dict(default_events_register_stream=stream_name)
|
|
|
|
result = self.client_patch(url, bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual(stream_name, response_dict["default_events_register_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(stream_name, bot["default_events_register_stream"])
|
2018-08-15 18:06:53 +02:00
|
|
|
|
|
|
|
# Make sure we are locked out of an unsubscribed private stream.
|
|
|
|
# We'll subscribe the bot but not the owner (since the check is
|
|
|
|
# on owner).
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "private_stream"
|
2018-08-15 18:06:53 +02:00
|
|
|
self.make_stream(stream_name, hamlet.realm, invite_only=True)
|
|
|
|
self.subscribe(bot_user, stream_name)
|
|
|
|
bot_info = dict(default_events_register_stream=stream_name)
|
|
|
|
result = self.client_patch(url, bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error_contains(result, "Invalid channel name")
|
2018-08-15 18:06:53 +02:00
|
|
|
|
|
|
|
# Subscribing the owner allows us to patch the stream.
|
|
|
|
self.subscribe(hamlet, stream_name)
|
|
|
|
bot_info = dict(default_events_register_stream=stream_name)
|
|
|
|
result = self.client_patch(url, bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Make sure the bot cannot create their own default stream.
|
2020-06-09 00:25:09 +02:00
|
|
|
url = f"/api/v1/bots/{bot_user.id}"
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(bot_user, url, bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error_contains(result, "endpoint does not accept")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_events_register_stream_allowed(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
user_profile = self.example_user("hamlet")
|
2017-08-25 06:01:29 +02:00
|
|
|
stream = self.subscribe(user_profile, "Denmark")
|
2022-08-01 12:54:46 +02:00
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=user_profile,
|
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_events_register_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Denmark", response_dict["default_events_register_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Denmark", bot["default_events_register_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_events_register_stream_denied(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
realm = self.example_user("hamlet").realm
|
2017-07-12 21:10:18 +02:00
|
|
|
stream = get_stream("Denmark", realm)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.unsubscribe(self.example_user("hamlet"), "Denmark")
|
2021-12-11 00:41:25 +01:00
|
|
|
do_change_stream_permission(
|
2022-08-01 12:54:46 +02:00
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=self.example_user("hamlet"),
|
2021-12-11 00:41:25 +01:00
|
|
|
)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_events_register_stream": "Denmark",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'Denmark'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_events_register_stream_none(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_events_register_stream": "",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-05-23 20:57:59 +02:00
|
|
|
bot_email = "hambot-bot@zulip.testserver"
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2021-02-12 08:19:30 +01:00
|
|
|
default_events_register_stream = get_user(
|
|
|
|
bot_email, bot_realm
|
|
|
|
).default_events_register_stream
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assertEqual(None, default_events_register_stream)
|
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(None, bot["default_events_register_stream"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_events_register_stream_not_found(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_events_register_stream": "missing",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Invalid channel name 'missing'")
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_default_all_public_streams_true(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_all_public_streams": orjson.dumps(True).decode(),
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual(response_dict["default_all_public_streams"], True)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(bot["default_all_public_streams"], True)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_default_all_public_streams_false(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"default_all_public_streams": orjson.dumps(False).decode(),
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-06-07 01:37:01 +02:00
|
|
|
response_dict = self.assert_json_success(result)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual(response_dict["default_all_public_streams"], False)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(bot["default_all_public_streams"], False)
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bot_via_post(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Fred",
|
|
|
|
"method": "PATCH",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2018-05-15 15:26:04 +02:00
|
|
|
# Important: We intentionally use the wrong method, post, here.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2022-08-25 18:41:46 +02:00
|
|
|
|
|
|
|
# TODO: The "method" parameter is not currently tracked as a processed parameter
|
2024-09-03 18:08:36 +02:00
|
|
|
# by typed_endpoint. Assert it is returned as an ignored parameter.
|
2022-08-25 18:41:46 +02:00
|
|
|
response_dict = self.assert_json_success(result, ignored_parameters=["method"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2022-06-07 01:37:01 +02:00
|
|
|
self.assertEqual("Fred", response_dict["full_name"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
|
|
|
bot = self.get_bot()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual("Fred", bot["full_name"])
|
2017-03-08 12:07:51 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_patch_bogus_bot(self) -> None:
|
2017-03-08 12:07:51 +01:00
|
|
|
"""Deleting a bogus bot will succeed silently."""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.create_bot()
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Fred",
|
2017-03-08 12:07:51 +01:00
|
|
|
}
|
2018-05-15 15:26:04 +02:00
|
|
|
invalid_user_id = 1000
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{invalid_user_id}", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "No such bot")
|
2017-03-08 12:07:51 +01:00
|
|
|
self.assert_num_bots_equal(1)
|
2017-06-10 18:43:31 +02:00
|
|
|
|
2018-01-16 20:34:12 +01:00
|
|
|
def test_patch_outgoing_webhook_bot(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-01-16 20:34:12 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": UserProfile.OUTGOING_WEBHOOK_BOT,
|
|
|
|
"payload_url": orjson.dumps("http://foo.bar.com").decode(),
|
2022-04-07 18:09:00 +02:00
|
|
|
"interface_type": Service.GENERIC,
|
2018-01-16 20:34:12 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"service_payload_url": orjson.dumps("http://foo.bar2.com").decode(),
|
|
|
|
"service_interface": Service.SLACK,
|
2018-01-16 20:34:12 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "hambot-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-01-16 20:34:12 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
service_interface = orjson.loads(result.content)["service_interface"]
|
2018-01-16 20:34:12 +01:00
|
|
|
self.assertEqual(service_interface, Service.SLACK)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
service_payload_url = orjson.loads(result.content)["service_payload_url"]
|
2018-01-16 20:34:12 +01:00
|
|
|
self.assertEqual(service_payload_url, "http://foo.bar2.com")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@patch("zulip_bots.bots.giphy.giphy.GiphyHandler.validate_config")
|
2018-02-13 11:47:40 +01:00
|
|
|
def test_patch_bot_config_data(self, mock_validate_config: MagicMock) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
"test",
|
2021-02-12 08:19:30 +01:00
|
|
|
self.example_user("hamlet"),
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="Bot with config data",
|
2021-02-12 08:19:30 +01:00
|
|
|
bot_type=UserProfile.EMBEDDED_BOT,
|
2021-02-12 08:20:45 +01:00
|
|
|
service_name="giphy",
|
|
|
|
config_data=orjson.dumps({"key": "12345678"}).decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_info = {"config_data": orjson.dumps({"key": "87654321"}).decode()}
|
|
|
|
email = "test-bot@zulip.testserver"
|
2020-06-09 00:25:09 +02:00
|
|
|
result = self.client_patch(f"/json/bots/{self.get_bot_user(email).id}", bot_info)
|
2018-01-30 19:21:13 +01:00
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
config_data = orjson.loads(result.content)["config_data"]
|
|
|
|
self.assertEqual(config_data, orjson.loads(bot_info["config_data"]))
|
2018-01-30 19:21:13 +01:00
|
|
|
|
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
|
|
|
def test_outgoing_webhook_invalid_interface(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2018-01-16 20:34:12 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Outgoing Webhook test bot",
|
|
|
|
"short_name": "outgoingservicebot",
|
|
|
|
"bot_type": UserProfile.OUTGOING_WEBHOOK_BOT,
|
|
|
|
"payload_url": orjson.dumps("http://127.0.0.1:5002").decode(),
|
|
|
|
"interface_type": -1,
|
2018-01-16 20:34:12 +01:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid interface type")
|
2018-01-16 20:34:12 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_info["interface_type"] = Service.GENERIC
|
2018-01-16 20:34:12 +01:00
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_create_outgoing_webhook_bot(self, **extras: Any) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-06-10 18:43:31 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Outgoing Webhook test bot",
|
|
|
|
"short_name": "outgoingservicebot",
|
|
|
|
"bot_type": UserProfile.OUTGOING_WEBHOOK_BOT,
|
|
|
|
"payload_url": orjson.dumps("http://127.0.0.1:5002").decode(),
|
2017-06-10 18:43:31 +02:00
|
|
|
}
|
|
|
|
bot_info.update(extras)
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
bot_email = "outgoingservicebot-bot@zulip.testserver"
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2017-06-10 18:43:31 +02:00
|
|
|
bot = get_user(bot_email, bot_realm)
|
2020-09-02 08:13:11 +02:00
|
|
|
[service] = get_bot_services(bot.id)
|
2017-06-10 18:43:31 +02:00
|
|
|
|
|
|
|
self.assertEqual(service.name, "outgoingservicebot")
|
2018-05-28 18:24:32 +02:00
|
|
|
self.assertEqual(service.base_url, "http://127.0.0.1:5002")
|
2017-06-10 18:43:31 +02:00
|
|
|
self.assertEqual(service.user_profile, bot)
|
|
|
|
|
|
|
|
# invalid URL test case.
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_info["payload_url"] = orjson.dumps("http://127.0.0.:5002").decode()
|
2017-06-10 18:43:31 +02:00
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2024-05-11 17:04:34 +02:00
|
|
|
self.assert_json_error(result, "Invalid payload_url: Value error, Not a URL")
|
2017-07-21 17:54:34 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_get_bot_handler(self) -> None:
|
2017-07-25 19:03:09 +02:00
|
|
|
# Test for valid service.
|
2021-02-12 08:20:45 +01:00
|
|
|
test_service_name = "converter"
|
2017-07-25 19:03:09 +02:00
|
|
|
test_bot_handler = get_bot_handler(test_service_name)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
str(type(test_bot_handler)),
|
|
|
|
"<class 'zulip_bots.bots.converter.converter.ConverterHandler'>",
|
|
|
|
)
|
2017-07-25 19:03:09 +02:00
|
|
|
|
|
|
|
# Test for invalid service.
|
|
|
|
test_service_name = "incorrect_bot_service_foo"
|
|
|
|
test_bot_handler = get_bot_handler(test_service_name)
|
|
|
|
self.assertEqual(test_bot_handler, None)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_if_each_embedded_bot_service_exists(self) -> None:
|
2017-07-21 17:54:34 +02:00
|
|
|
for embedded_bot in EMBEDDED_BOTS:
|
2017-10-12 16:52:53 +02:00
|
|
|
self.assertIsNotNone(get_bot_handler(embedded_bot.name))
|
2017-07-03 18:35:12 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_outgoing_webhook_interface_type(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2017-07-03 18:35:12 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Outgoing Webhook test bot",
|
|
|
|
"short_name": "outgoingservicebot",
|
|
|
|
"bot_type": UserProfile.OUTGOING_WEBHOOK_BOT,
|
|
|
|
"payload_url": orjson.dumps("http://127.0.0.1:5002").decode(),
|
|
|
|
"interface_type": -1,
|
2017-07-03 18:35:12 +02:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid interface type")
|
2017-07-03 18:35:12 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_info["interface_type"] = Service.GENERIC
|
2017-07-03 18:35:12 +02:00
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
2017-07-14 16:44:07 +02:00
|
|
|
|
2018-02-25 19:09:44 +01:00
|
|
|
def test_create_embedded_bot_with_disabled_embedded_bots(self, **extras: Any) -> None:
|
2017-07-14 16:44:07 +02:00
|
|
|
with self.settings(EMBEDDED_BOTS_ENABLED=False):
|
2020-07-05 00:50:18 +02:00
|
|
|
self.fail_to_create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="embeddedservicebot",
|
2020-07-05 00:50:18 +02:00
|
|
|
user_profile=self.example_user("hamlet"),
|
|
|
|
bot_type=UserProfile.EMBEDDED_BOT,
|
2021-02-12 08:20:45 +01:00
|
|
|
service_name="followup",
|
|
|
|
config_data=orjson.dumps({"key": "value"}).decode(),
|
|
|
|
assert_json_error_msg="Embedded bots are not enabled.",
|
2020-07-05 00:50:18 +02:00
|
|
|
**extras,
|
|
|
|
)
|
2017-07-14 16:44:07 +02:00
|
|
|
|
2018-02-25 19:09:44 +01:00
|
|
|
def test_create_embedded_bot(self, **extras: Any) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_config_info = {"key": "value"}
|
2021-02-12 08:19:30 +01:00
|
|
|
self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="embeddedservicebot",
|
2021-02-12 08:19:30 +01:00
|
|
|
user_profile=self.example_user("hamlet"),
|
|
|
|
bot_type=UserProfile.EMBEDDED_BOT,
|
2021-02-12 08:20:45 +01:00
|
|
|
service_name="followup",
|
2021-02-12 08:19:30 +01:00
|
|
|
config_data=orjson.dumps(bot_config_info).decode(),
|
|
|
|
**extras,
|
|
|
|
)
|
2017-07-14 16:44:07 +02:00
|
|
|
bot_email = "embeddedservicebot-bot@zulip.testserver"
|
2021-02-12 08:20:45 +01:00
|
|
|
bot_realm = get_realm("zulip")
|
2017-07-14 16:44:07 +02:00
|
|
|
bot = get_user(bot_email, bot_realm)
|
2020-09-02 08:13:11 +02:00
|
|
|
[service] = get_bot_services(bot.id)
|
2018-01-07 19:24:14 +01:00
|
|
|
bot_config = get_bot_config(bot)
|
|
|
|
self.assertEqual(bot_config, bot_config_info)
|
|
|
|
self.assertEqual(service.name, "followup")
|
2017-07-14 16:44:07 +02:00
|
|
|
self.assertEqual(service.user_profile, bot)
|
|
|
|
|
2018-02-25 19:09:44 +01:00
|
|
|
def test_create_embedded_bot_with_incorrect_service_name(self, **extras: Any) -> None:
|
2020-07-05 00:50:18 +02:00
|
|
|
self.fail_to_create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="embeddedservicebot",
|
2020-07-05 00:50:18 +02:00
|
|
|
user_profile=self.example_user("hamlet"),
|
|
|
|
bot_type=UserProfile.EMBEDDED_BOT,
|
2021-02-12 08:20:45 +01:00
|
|
|
service_name="not_existing_service",
|
|
|
|
assert_json_error_msg="Invalid embedded bot name.",
|
2020-07-05 00:50:18 +02:00
|
|
|
**extras,
|
|
|
|
)
|
2017-12-07 20:47:10 +01:00
|
|
|
|
2018-02-25 19:09:44 +01:00
|
|
|
def test_create_embedded_bot_with_invalid_config_value(self, **extras: Any) -> None:
|
2020-07-05 00:50:18 +02:00
|
|
|
self.fail_to_create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="embeddedservicebot",
|
2020-07-05 00:50:18 +02:00
|
|
|
user_profile=self.example_user("hamlet"),
|
2021-02-12 08:20:45 +01:00
|
|
|
service_name="followup",
|
|
|
|
config_data=orjson.dumps({"invalid": ["config", "value"]}).decode(),
|
2024-05-11 17:04:34 +02:00
|
|
|
assert_json_error_msg='config_data["invalid"] is not a string',
|
2020-07-05 00:50:18 +02:00
|
|
|
**extras,
|
|
|
|
)
|
2018-01-07 19:24:14 +01:00
|
|
|
|
2018-02-13 11:47:40 +01:00
|
|
|
# Test to create embedded bot with an incorrect config value
|
2021-02-12 08:20:45 +01:00
|
|
|
incorrect_bot_config_info = {"key": "incorrect key"}
|
2018-02-13 11:47:40 +01:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Embedded test bot",
|
|
|
|
"short_name": "embeddedservicebot3",
|
|
|
|
"bot_type": UserProfile.EMBEDDED_BOT,
|
|
|
|
"service_name": "giphy",
|
|
|
|
"config_data": orjson.dumps(incorrect_bot_config_info).decode(),
|
2018-02-13 11:47:40 +01:00
|
|
|
}
|
|
|
|
bot_info.update(extras)
|
2021-02-12 08:19:30 +01:00
|
|
|
with patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zulip_bots.bots.giphy.giphy.GiphyHandler.validate_config",
|
2021-02-12 08:19:30 +01:00
|
|
|
side_effect=ConfigValidationError,
|
|
|
|
):
|
2018-02-13 11:47:40 +01:00
|
|
|
result = self.client_post("/json/bots", bot_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid configuration data!")
|
2018-02-13 11:47:40 +01:00
|
|
|
|
2017-12-07 20:47:10 +01:00
|
|
|
def test_is_cross_realm_bot_email(self) -> None:
|
|
|
|
self.assertTrue(is_cross_realm_bot_email("notification-bot@zulip.com"))
|
|
|
|
self.assertTrue(is_cross_realm_bot_email("notification-BOT@zulip.com"))
|
|
|
|
self.assertFalse(is_cross_realm_bot_email("random-bot@zulip.com"))
|
|
|
|
|
|
|
|
with self.settings(CROSS_REALM_BOT_EMAILS={"random-bot@zulip.com"}):
|
|
|
|
self.assertTrue(is_cross_realm_bot_email("random-bot@zulip.com"))
|
|
|
|
self.assertFalse(is_cross_realm_bot_email("notification-bot@zulip.com"))
|
2019-08-18 12:56:21 +02:00
|
|
|
|
2024-08-20 23:15:19 +02:00
|
|
|
def test_private_bot_empty_bot_owner_user_ids(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login("hamlet")
|
|
|
|
self.create_bot()
|
|
|
|
stream = get_stream("Denmark", get_realm("zulip"))
|
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=True,
|
|
|
|
history_public_to_subscribers=False,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=hamlet,
|
|
|
|
)
|
|
|
|
|
|
|
|
new_bot = assert_is_not_none(UserProfile.objects.last())
|
|
|
|
do_change_default_sending_stream(new_bot, stream, acting_user=hamlet)
|
|
|
|
new_bot.bot_owner_id = None
|
|
|
|
new_bot.save()
|
|
|
|
|
|
|
|
self.assertEqual(bot_owner_user_ids(new_bot), set())
|
|
|
|
|
2019-08-18 12:56:21 +02:00
|
|
|
@patch("zerver.lib.integrations.WEBHOOK_INTEGRATIONS", stripe_sample_config_options)
|
|
|
|
def test_create_incoming_webhook_bot_with_service_name_and_with_keys(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-18 12:56:21 +02:00
|
|
|
bot_metadata = {
|
|
|
|
"full_name": "My Stripe Bot",
|
|
|
|
"short_name": "my-stripe",
|
|
|
|
"bot_type": UserProfile.INCOMING_WEBHOOK_BOT,
|
|
|
|
"service_name": "stripe",
|
2020-08-07 01:09:47 +02:00
|
|
|
"config_data": orjson.dumps({"stripe_api_key": "sample-api-key"}).decode(),
|
2019-08-18 12:56:21 +02:00
|
|
|
}
|
|
|
|
self.create_bot(**bot_metadata)
|
|
|
|
new_bot = UserProfile.objects.get(full_name="My Stripe Bot")
|
|
|
|
config_data = get_bot_config(new_bot)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
config_data, {"integration_id": "stripe", "stripe_api_key": "sample-api-key"}
|
|
|
|
)
|
2019-08-18 12:56:21 +02:00
|
|
|
|
|
|
|
@patch("zerver.lib.integrations.WEBHOOK_INTEGRATIONS", stripe_sample_config_options)
|
|
|
|
def test_create_incoming_webhook_bot_with_service_name_incorrect_keys(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-18 12:56:21 +02:00
|
|
|
bot_metadata = {
|
|
|
|
"full_name": "My Stripe Bot",
|
|
|
|
"short_name": "my-stripe",
|
|
|
|
"bot_type": UserProfile.INCOMING_WEBHOOK_BOT,
|
|
|
|
"service_name": "stripe",
|
2020-08-07 01:09:47 +02:00
|
|
|
"config_data": orjson.dumps({"stripe_api_key": "_invalid_key"}).decode(),
|
2019-08-18 12:56:21 +02:00
|
|
|
}
|
|
|
|
response = self.client_post("/json/bots", bot_metadata)
|
|
|
|
self.assertEqual(response.status_code, 400)
|
|
|
|
expected_error_message = 'Invalid stripe_api_key value _invalid_key (stripe_api_key starts with a "_" and is hence invalid.)'
|
2020-10-30 01:18:43 +01:00
|
|
|
self.assertEqual(orjson.loads(response.content)["msg"], expected_error_message)
|
2019-08-18 12:56:21 +02:00
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
UserProfile.objects.get(full_name="My Stripe Bot")
|
|
|
|
|
|
|
|
@patch("zerver.lib.integrations.WEBHOOK_INTEGRATIONS", stripe_sample_config_options)
|
|
|
|
def test_create_incoming_webhook_bot_with_service_name_without_keys(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-18 12:56:21 +02:00
|
|
|
bot_metadata = {
|
|
|
|
"full_name": "My Stripe Bot",
|
|
|
|
"short_name": "my-stripe",
|
|
|
|
"bot_type": UserProfile.INCOMING_WEBHOOK_BOT,
|
|
|
|
"service_name": "stripe",
|
|
|
|
}
|
|
|
|
response = self.client_post("/json/bots", bot_metadata)
|
|
|
|
self.assertEqual(response.status_code, 400)
|
|
|
|
expected_error_message = "Missing configuration parameters: {'stripe_api_key'}"
|
2020-10-30 01:18:43 +01:00
|
|
|
self.assertEqual(orjson.loads(response.content)["msg"], expected_error_message)
|
2019-08-18 12:56:21 +02:00
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
UserProfile.objects.get(full_name="My Stripe Bot")
|
|
|
|
|
|
|
|
@patch("zerver.lib.integrations.WEBHOOK_INTEGRATIONS", stripe_sample_config_options)
|
|
|
|
def test_create_incoming_webhook_bot_without_service_name(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-18 12:56:21 +02:00
|
|
|
bot_metadata = {
|
|
|
|
"full_name": "My Stripe Bot",
|
|
|
|
"short_name": "my-stripe",
|
|
|
|
"bot_type": UserProfile.INCOMING_WEBHOOK_BOT,
|
|
|
|
}
|
|
|
|
self.create_bot(**bot_metadata)
|
|
|
|
new_bot = UserProfile.objects.get(full_name="My Stripe Bot")
|
|
|
|
with self.assertRaises(ConfigError):
|
|
|
|
get_bot_config(new_bot)
|
|
|
|
|
|
|
|
@patch("zerver.lib.integrations.WEBHOOK_INTEGRATIONS", stripe_sample_config_options)
|
|
|
|
def test_create_incoming_webhook_bot_with_incorrect_service_name(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2019-08-18 12:56:21 +02:00
|
|
|
bot_metadata = {
|
|
|
|
"full_name": "My Stripe Bot",
|
|
|
|
"short_name": "my-stripe",
|
|
|
|
"bot_type": UserProfile.INCOMING_WEBHOOK_BOT,
|
|
|
|
"service_name": "stripes",
|
|
|
|
}
|
|
|
|
response = self.client_post("/json/bots", bot_metadata)
|
|
|
|
self.assertEqual(response.status_code, 400)
|
|
|
|
expected_error_message = "Invalid integration 'stripes'."
|
2020-10-30 01:18:43 +01:00
|
|
|
self.assertEqual(orjson.loads(response.content)["msg"], expected_error_message)
|
2019-08-18 12:56:21 +02:00
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
UserProfile.objects.get(full_name="My Stripe Bot")
|