mirror of https://github.com/zulip/zulip.git
api: Fix encoding of strings in realm endpoint.
* Don't require strings to be unnecessarily JSON-encoded. * Use check_capped_string rather than custom code for length checks. * Update frontend to pass the right parameters. With a much simplified populate_data_for_request design suggested by Anders; we only support a handful of data types, all of which are correctly encoded automatically by jQuery. Fixes part of #18035.
This commit is contained in:
parent
7947de5cd1
commit
cdbcb43706
|
@ -247,11 +247,11 @@ function test_submit_settings_form(override, submit_form) {
|
||||||
assert(patched);
|
assert(patched);
|
||||||
|
|
||||||
let expected_value = {
|
let expected_value = {
|
||||||
bot_creation_policy: "1",
|
bot_creation_policy: 1,
|
||||||
invite_to_stream_policy: "1",
|
invite_to_stream_policy: 1,
|
||||||
email_address_visibility: "1",
|
email_address_visibility: 1,
|
||||||
add_emoji_by_admins_only: false,
|
add_emoji_by_admins_only: false,
|
||||||
create_stream_policy: "2",
|
create_stream_policy: 2,
|
||||||
};
|
};
|
||||||
assert.deepEqual(data, expected_value);
|
assert.deepEqual(data, expected_value);
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ function test_submit_settings_form(override, submit_form) {
|
||||||
assert(patched);
|
assert(patched);
|
||||||
|
|
||||||
expected_value = {
|
expected_value = {
|
||||||
default_language: '"en"',
|
default_language: "en",
|
||||||
default_twenty_four_hour_time: "true",
|
default_twenty_four_hour_time: "true",
|
||||||
};
|
};
|
||||||
assert.deepEqual(data, expected_value);
|
assert.deepEqual(data, expected_value);
|
||||||
|
|
|
@ -795,7 +795,8 @@ export function build_page() {
|
||||||
}
|
}
|
||||||
} else if (subsection === "other_settings") {
|
} else if (subsection === "other_settings") {
|
||||||
const code_block_language_value = default_code_language_widget.value();
|
const code_block_language_value = default_code_language_widget.value();
|
||||||
data.default_code_block_language = JSON.stringify(code_block_language_value);
|
// No need to JSON-encode, since this value is already a string.
|
||||||
|
data.default_code_block_language = code_block_language_value;
|
||||||
} else if (subsection === "other_permissions") {
|
} else if (subsection === "other_permissions") {
|
||||||
const add_emoji_permission = $("#id_realm_add_emoji_by_admins_only").val();
|
const add_emoji_permission = $("#id_realm_add_emoji_by_admins_only").val();
|
||||||
|
|
||||||
|
@ -866,7 +867,7 @@ export function build_page() {
|
||||||
const input_value = get_input_element_value(input_elem);
|
const input_value = get_input_element_value(input_elem);
|
||||||
if (input_value !== undefined) {
|
if (input_value !== undefined) {
|
||||||
const property_name = input_elem.attr("id").replace("id_realm_", "");
|
const property_name = input_elem.attr("id").replace("id_realm_", "");
|
||||||
data[property_name] = JSON.stringify(input_value);
|
data[property_name] = input_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,12 @@ below features are supported.
|
||||||
|
|
||||||
## Changes in Zulip 4.0
|
## Changes in Zulip 4.0
|
||||||
|
|
||||||
|
**Feature level 52**
|
||||||
|
|
||||||
|
* `PATCH /realm`: Removed unnecessary JSON-encoding of string
|
||||||
|
parameters `name`, `description`, `default_language`, and
|
||||||
|
`default_code_block_language`.
|
||||||
|
|
||||||
**Feature level 51**
|
**Feature level 51**
|
||||||
|
|
||||||
* [`POST /register`](/api/register-queue): Added a new boolean field
|
* [`POST /register`](/api/register-queue): Added a new boolean field
|
||||||
|
|
|
@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
|
||||||
#
|
#
|
||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in templates/zerver/api/changelog.md.
|
# new level means in templates/zerver/api/changelog.md.
|
||||||
API_FEATURE_LEVEL = 51
|
API_FEATURE_LEVEL = 52
|
||||||
|
|
||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||||
# only when going from an old version of the code to a newer version. Bump
|
# only when going from an old version of the code to a newer version. Bump
|
||||||
|
|
|
@ -184,6 +184,7 @@ def clear_supported_auth_backends_cache() -> None:
|
||||||
|
|
||||||
class Realm(models.Model):
|
class Realm(models.Model):
|
||||||
MAX_REALM_NAME_LENGTH = 40
|
MAX_REALM_NAME_LENGTH = 40
|
||||||
|
MAX_REALM_DESCRIPTION_LENGTH = 1000
|
||||||
MAX_REALM_SUBDOMAIN_LENGTH = 40
|
MAX_REALM_SUBDOMAIN_LENGTH = 40
|
||||||
MAX_REALM_REDIRECT_URL_LENGTH = 128
|
MAX_REALM_REDIRECT_URL_LENGTH = 128
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List, Mapping
|
from typing import Any, Dict, List, Mapping, Union
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import orjson
|
import orjson
|
||||||
|
@ -103,7 +103,7 @@ class RealmTest(ZulipTestCase):
|
||||||
def test_update_realm_description(self) -> None:
|
def test_update_realm_description(self) -> None:
|
||||||
self.login("iago")
|
self.login("iago")
|
||||||
new_description = "zulip dev group"
|
new_description = "zulip dev group"
|
||||||
data = dict(description=orjson.dumps(new_description).decode())
|
data = dict(description=new_description)
|
||||||
events: List[Mapping[str, Any]] = []
|
events: List[Mapping[str, Any]] = []
|
||||||
with tornado_redirected_to_list(events):
|
with tornado_redirected_to_list(events):
|
||||||
result = self.client_patch("/json/realm", data)
|
result = self.client_patch("/json/realm", data)
|
||||||
|
@ -124,25 +124,25 @@ class RealmTest(ZulipTestCase):
|
||||||
|
|
||||||
def test_realm_description_length(self) -> None:
|
def test_realm_description_length(self) -> None:
|
||||||
new_description = "A" * 1001
|
new_description = "A" * 1001
|
||||||
data = dict(description=orjson.dumps(new_description).decode())
|
data = dict(description=new_description)
|
||||||
|
|
||||||
# create an admin user
|
# create an admin user
|
||||||
self.login("iago")
|
self.login("iago")
|
||||||
|
|
||||||
result = self.client_patch("/json/realm", data)
|
result = self.client_patch("/json/realm", data)
|
||||||
self.assert_json_error(result, "Organization description is too long.")
|
self.assert_json_error(result, "description is too long (limit: 1000 characters)")
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
self.assertNotEqual(realm.description, new_description)
|
self.assertNotEqual(realm.description, new_description)
|
||||||
|
|
||||||
def test_realm_name_length(self) -> None:
|
def test_realm_name_length(self) -> None:
|
||||||
new_name = "A" * (Realm.MAX_REALM_NAME_LENGTH + 1)
|
new_name = "A" * (Realm.MAX_REALM_NAME_LENGTH + 1)
|
||||||
data = dict(name=orjson.dumps(new_name).decode())
|
data = dict(name=new_name)
|
||||||
|
|
||||||
# create an admin user
|
# create an admin user
|
||||||
self.login("iago")
|
self.login("iago")
|
||||||
|
|
||||||
result = self.client_patch("/json/realm", data)
|
result = self.client_patch("/json/realm", data)
|
||||||
self.assert_json_error(result, "Organization name is too long.")
|
self.assert_json_error(result, "name is too long (limit: 40 characters)")
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
self.assertNotEqual(realm.name, new_name)
|
self.assertNotEqual(realm.name, new_name)
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ class RealmTest(ZulipTestCase):
|
||||||
|
|
||||||
self.login("othello")
|
self.login("othello")
|
||||||
|
|
||||||
req = dict(name=orjson.dumps(new_name).decode())
|
req = dict(name=new_name)
|
||||||
result = self.client_patch("/json/realm", req)
|
result = self.client_patch("/json/realm", req)
|
||||||
self.assert_json_error(result, "Must be an organization administrator")
|
self.assert_json_error(result, "Must be an organization administrator")
|
||||||
|
|
||||||
|
@ -419,7 +419,7 @@ class RealmTest(ZulipTestCase):
|
||||||
# we need an admin user.
|
# we need an admin user.
|
||||||
self.login("iago")
|
self.login("iago")
|
||||||
|
|
||||||
req = dict(default_language=orjson.dumps(new_lang).decode())
|
req = dict(default_language=new_lang)
|
||||||
result = self.client_patch("/json/realm", req)
|
result = self.client_patch("/json/realm", req)
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
|
@ -429,7 +429,7 @@ class RealmTest(ZulipTestCase):
|
||||||
# as the default realm language, correct validation error is
|
# as the default realm language, correct validation error is
|
||||||
# raised and the invalid language is not saved in db
|
# raised and the invalid language is not saved in db
|
||||||
invalid_lang = "invalid_lang"
|
invalid_lang = "invalid_lang"
|
||||||
req = dict(default_language=orjson.dumps(invalid_lang).decode())
|
req = dict(default_language=invalid_lang)
|
||||||
result = self.client_patch("/json/realm", req)
|
result = self.client_patch("/json/realm", req)
|
||||||
self.assert_json_error(result, f"Invalid language '{invalid_lang}'")
|
self.assert_json_error(result, f"Invalid language '{invalid_lang}'")
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
|
@ -820,8 +820,10 @@ class RealmAPITest(ZulipTestCase):
|
||||||
setattr(realm, attr, value)
|
setattr(realm, attr, value)
|
||||||
realm.save(update_fields=[attr])
|
realm.save(update_fields=[attr])
|
||||||
|
|
||||||
def update_with_api(self, name: str, value: int) -> Realm:
|
def update_with_api(self, name: str, value: Union[int, str]) -> Realm:
|
||||||
result = self.client_patch("/json/realm", {name: orjson.dumps(value).decode()})
|
if not isinstance(value, str):
|
||||||
|
value = orjson.dumps(value).decode()
|
||||||
|
result = self.client_patch("/json/realm", {name: value})
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
return get_realm("zulip") # refresh data
|
return get_realm("zulip") # refresh data
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ from zerver.lib.retention import parse_message_retention_days
|
||||||
from zerver.lib.streams import access_stream_by_id
|
from zerver.lib.streams import access_stream_by_id
|
||||||
from zerver.lib.validator import (
|
from zerver.lib.validator import (
|
||||||
check_bool,
|
check_bool,
|
||||||
|
check_capped_string,
|
||||||
check_dict,
|
check_dict,
|
||||||
check_int,
|
check_int,
|
||||||
check_int_in,
|
check_int_in,
|
||||||
check_string,
|
|
||||||
check_string_or_int,
|
check_string_or_int,
|
||||||
to_non_negative_int,
|
to_non_negative_int,
|
||||||
)
|
)
|
||||||
|
@ -41,8 +41,12 @@ from zerver.models import Realm, UserProfile
|
||||||
def update_realm(
|
def update_realm(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
name: Optional[str] = REQ(json_validator=check_string, default=None),
|
name: Optional[str] = REQ(
|
||||||
description: Optional[str] = REQ(json_validator=check_string, default=None),
|
str_validator=check_capped_string(Realm.MAX_REALM_NAME_LENGTH), default=None
|
||||||
|
),
|
||||||
|
description: Optional[str] = REQ(
|
||||||
|
str_validator=check_capped_string(Realm.MAX_REALM_DESCRIPTION_LENGTH), default=None
|
||||||
|
),
|
||||||
emails_restricted_to_domains: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
emails_restricted_to_domains: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
disallow_disposable_email_addresses: Optional[bool] = REQ(
|
disallow_disposable_email_addresses: Optional[bool] = REQ(
|
||||||
json_validator=check_bool, default=None
|
json_validator=check_bool, default=None
|
||||||
|
@ -68,7 +72,7 @@ def update_realm(
|
||||||
converter=to_non_negative_int, default=None
|
converter=to_non_negative_int, default=None
|
||||||
),
|
),
|
||||||
allow_edit_history: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
allow_edit_history: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
default_language: Optional[str] = REQ(json_validator=check_string, default=None),
|
default_language: Optional[str] = REQ(default=None),
|
||||||
waiting_period_threshold: Optional[int] = REQ(converter=to_non_negative_int, default=None),
|
waiting_period_threshold: Optional[int] = REQ(converter=to_non_negative_int, default=None),
|
||||||
authentication_methods: Optional[Dict[str, Any]] = REQ(
|
authentication_methods: Optional[Dict[str, Any]] = REQ(
|
||||||
json_validator=check_dict([]), default=None
|
json_validator=check_dict([]), default=None
|
||||||
|
@ -106,7 +110,7 @@ def update_realm(
|
||||||
),
|
),
|
||||||
default_twenty_four_hour_time: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
default_twenty_four_hour_time: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
video_chat_provider: Optional[int] = REQ(json_validator=check_int, default=None),
|
video_chat_provider: Optional[int] = REQ(json_validator=check_int, default=None),
|
||||||
default_code_block_language: Optional[str] = REQ(json_validator=check_string, default=None),
|
default_code_block_language: Optional[str] = REQ(default=None),
|
||||||
digest_weekday: Optional[int] = REQ(
|
digest_weekday: Optional[int] = REQ(
|
||||||
json_validator=check_int_in(Realm.DIGEST_WEEKDAY_VALUES), default=None
|
json_validator=check_int_in(Realm.DIGEST_WEEKDAY_VALUES), default=None
|
||||||
),
|
),
|
||||||
|
@ -117,10 +121,6 @@ def update_realm(
|
||||||
# the entire request can succeed or fail atomically.
|
# the entire request can succeed or fail atomically.
|
||||||
if default_language is not None and default_language not in get_available_language_codes():
|
if default_language is not None and default_language not in get_available_language_codes():
|
||||||
raise JsonableError(_("Invalid language '{}'").format(default_language))
|
raise JsonableError(_("Invalid language '{}'").format(default_language))
|
||||||
if description is not None and len(description) > 1000:
|
|
||||||
return json_error(_("Organization description is too long."))
|
|
||||||
if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH:
|
|
||||||
return json_error(_("Organization name is too long."))
|
|
||||||
if authentication_methods is not None:
|
if authentication_methods is not None:
|
||||||
if not user_profile.is_realm_owner:
|
if not user_profile.is_realm_owner:
|
||||||
raise OrganizationOwnerRequired()
|
raise OrganizationOwnerRequired()
|
||||||
|
|
Loading…
Reference in New Issue