Skip to content

Commit

Permalink
Prevent vote weight of 0 (#1938)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsangmeister authored Nov 3, 2023
1 parent 7bbbcc6 commit d75a56b
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 31 deletions.
40 changes: 15 additions & 25 deletions cli/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,27 +163,17 @@ def check_field(
self.errors.append(
f"Default value for {collectionfield}' is not valid json."
)
if type == "number":
valid_attributes.append("minimum")
if not isinstance(field.get("minimum", 0), int):
self.errors.append(f"'minimum' for {collectionfield} is not a number.")
if type == "float":
valid_attributes.append("minimum")
if not isinstance(field.get("minimum", 0), (int, float)):
self.errors.append(f"'minimum' for {collectionfield} is not a number.")
if type == "decimal(6)":
if type in ("number", "float", "decimal(6)"):
valid_attributes.append("minimum")
if "minimum" in field:
self.validate_value_for_type(type, field["minimum"], collectionfield)
if type in ("string", "text"):
valid_attributes.append("maxLength")
if not isinstance(field.get("maxLength", 0), int):
self.errors.append(
f"'maxLength' for {collectionfield} is not a number."
)
valid_attributes.append("minLength")
if not isinstance(field.get("minLength", 0), int):
self.errors.append(
f"'minLength' for {collectionfield} is not a number."
)
for attr in ("minLength", "maxLength"):
valid_attributes.append(attr)
if not isinstance(field.get("maxLength", 0), int):
self.errors.append(
f"'maxLength' for {collectionfield} is not a number."
)
if type in DATA_TYPES:
valid_attributes.append("default")
if "default" in field:
Expand Down Expand Up @@ -232,34 +222,34 @@ def validate_value_for_type(
if type_str in basic_types:
if type(value) != basic_types[type_str]:
self.errors.append(
f"Value '{value}' for {collectionfield}' is not a {type_str}."
f"Value '{value}' for '{collectionfield}' is not a {type_str}."
)
elif type_str in ("string[]", "number[]"):
if not isinstance(value, list):
self.errors.append(
f"Value '{value}' for {collectionfield}' is not a {type_str}."
f"Value '{value}' for '{collectionfield}' is not a {type_str}."
)
for x in value:
if type(x) != basic_types[type_str[:-2]]:
self.errors.append(
f"Listentry '{x}' for {collectionfield}' is not a {type_str[:-2]}."
f"Listentry '{x}' for '{collectionfield}' is not a {type_str[:-2]}."
)
elif type_str == "JSON":
pass
elif type_str == "float":
if type(value) not in (int, float):
self.errors.append(
f"Value '{value}' for {collectionfield}' is not a float."
f"Value '{value}' for '{collectionfield}' is not a float."
)
elif type_str == "decimal(6)":
if not DECIMAL_PATTERN.match(value):
self.errors.append(
f"Value '{value}' for {collectionfield}' is not a decimal(6)."
f"Value '{value}' for '{collectionfield}' is not a decimal(6)."
)
elif type_str == "color":
if not COLOR_PATTERN.match(value):
self.errors.append(
f"Value '{value}' for {collectionfield}' is not a color."
f"Value '{value}' for '{collectionfield}' is not a color."
)
else:
raise NotImplementedError(type_str)
Expand Down
4 changes: 2 additions & 2 deletions global/meta/models.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ user:
default_vote_weight:
type: decimal(6)
default: "1.000000"
minimum: 0
minimum: "0.000001"
restriction_mode: A
last_email_sent:
type: timestamp
Expand Down Expand Up @@ -385,7 +385,7 @@ meeting_user:
restriction_mode: A
vote_weight:
type: decimal(6)
minimum: 0
minimum: "0.000001"
restriction_mode: A

user_id:
Expand Down
2 changes: 1 addition & 1 deletion openslides_backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def start_them_all(env: Environment) -> None: # pragma: no cover
for process in processes.values():
process.start()

def sigterm_handler(signalnum: int, current_stack_frame: Any) -> None:
def sigterm_handler(signalnum: int, _: Any) -> None:
strsignal = signal.strsignal # type: ignore
print(
f"Parent process {os.getpid()} received {strsignal(signalnum)} "
Expand Down
2 changes: 2 additions & 0 deletions openslides_backend/models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ def get_schema(self) -> Schema:
schema = self.extend_schema(super().get_schema(), **decimal_schema)
if not self.required:
schema["type"] = ["string", "null"]
# remove minimum since it is checked in the validate method
schema.pop("minimum", None)
return schema

def validate(self, value: Any, payload: Dict[str, Any] = {}) -> Any:
Expand Down
6 changes: 3 additions & 3 deletions openslides_backend/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .base import Model
from .mixins import AgendaItemModelMixin, MeetingModelMixin, PollModelMixin

MODELS_YML_CHECKSUM = "34d6dfe51212a0566f81e5c2f67675d6"
MODELS_YML_CHECKSUM = "832b7041a4dcc2876ea20c34fb5d7cf9"


class Organization(Model):
Expand Down Expand Up @@ -100,7 +100,7 @@ class User(Model):
default_number = fields.CharField()
default_structure_level = fields.CharField()
default_vote_weight = fields.DecimalField(
default="1.000000", constraints={"minimum": 0}
default="1.000000", constraints={"minimum": "0.000001"}
)
last_email_sent = fields.TimestampField()
is_demo_user = fields.BooleanField()
Expand Down Expand Up @@ -151,7 +151,7 @@ class MeetingUser(Model):
number = fields.CharField()
structure_level = fields.CharField()
about_me = fields.HTMLStrictField()
vote_weight = fields.DecimalField(constraints={"minimum": 0})
vote_weight = fields.DecimalField(constraints={"minimum": "0.000001"})
user_id = fields.RelationField(to={"user": "meeting_user_ids"}, required=True)
meeting_id = fields.RelationField(to={"meeting": "meeting_user_ids"}, required=True)
personal_note_ids = fields.RelationListField(
Expand Down
18 changes: 18 additions & 0 deletions tests/system/action/meeting_user/test_set_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,21 @@ def test_set_data_missing_identifiers(self) -> None:
"Identifier for meeting_user instance required, but neither id nor meeting_id/user_id is given."
== response.json["message"]
)

def test_prevent_zero_vote_weight(self) -> None:
self.set_models(
{
"meeting/10": {
"is_active_in_organization_id": 1,
"meeting_user_ids": [5],
},
"meeting_user/5": {
"user_id": 1,
"meeting_id": 10,
"vote_weight": "1.000000",
},
}
)
response = self.request("meeting_user.set_data", {"vote_weight": "0.000000"})
self.assert_status_code(response, 400)
self.assert_model_exists("meeting_user/5", {"vote_weight": "1.000000"})
20 changes: 20 additions & 0 deletions tests/system/action/user/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,26 @@ def test_update_vote_weight(self) -> None:
},
)

def test_update_prevent_zero_vote_weight(self) -> None:
self.set_models(
{
"user/111": {
"username": "username_srtgb123",
"default_vote_weight": "1.000000",
},
"meeting/1": {
"name": "test_meeting_1",
"is_active_in_organization_id": 1,
},
}
)
response = self.request(
"user.update",
{"id": 111, "default_vote_weight": "0.000000", "meeting_id": 1},
)
self.assert_status_code(response, 400)
self.assert_model_exists("user/111", {"default_vote_weight": "1.000000"})

def test_update_self_vote_delegation(self) -> None:
self.set_models(
{
Expand Down

0 comments on commit d75a56b

Please sign in to comment.