models: Add can_access_all_users_group setting.

This commit adds new setting for controlling who can access
all users in the realm which would have "Everyone" and
"Members only" option.

Fixes part of #10970.
This commit is contained in:
Sahil Batra 2023-03-23 20:12:00 +05:30 committed by Tim Abbott
parent f20d427a4d
commit c82bb3ec76
13 changed files with 165 additions and 3 deletions

View File

@ -20,6 +20,17 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 8.0 ## Changes in Zulip 8.0
**Feature level 225**
* `PATCH /realm`, [`POST /register`](/api/register-queue),
[`GET /events`](/api/get-events): Added `can_access_all_users_group_id`
realm setting, which is the ID of the user group whose members can
access all the users in the oragnization.
* [`POST /register`](/api/register-queue): Added `allowed_system_groups`
field to configuration data object of permission settings passed in
`server_supported_permission_settings`.
**Feature level 224** **Feature level 224**
* [`GET /events`](/api/get-events), [`GET /messages`](/api/get-messages), * [`GET /events`](/api/get-events), [`GET /messages`](/api/get-messages),

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in api_docs/changelog.md, as well as "**Changes**" # new level means in api_docs/changelog.md, as well as "**Changes**"
# entries in the endpoint's documentation in `zulip.yaml`. # entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 224 API_FEATURE_LEVEL = 225
# 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

View File

@ -991,7 +991,8 @@ night_logo_data = DictType(
) )
group_setting_update_data_type = DictType( group_setting_update_data_type = DictType(
required_keys=[], optional_keys=[("create_multiuse_invite_group", int)] required_keys=[],
optional_keys=[("create_multiuse_invite_group", int), ("can_access_all_users_group", int)],
) )
update_dict_data = UnionType( update_dict_data = UnionType(

View File

@ -1,5 +1,5 @@
import datetime import datetime
from dataclasses import dataclass from dataclasses import dataclass, field
from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, TypeVar, Union from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, TypeVar, Union
from django_stubs_ext import StrPromise from django_stubs_ext import StrPromise
@ -291,6 +291,7 @@ class GroupPermissionSetting:
default_group_name: str default_group_name: str
id_field_name: str id_field_name: str
default_for_system_groups: Optional[str] = None default_for_system_groups: Optional[str] = None
allowed_system_groups: List[str] = field(default_factory=list)
@dataclass @dataclass

View File

@ -215,6 +215,16 @@ def access_user_group_for_setting(
) )
) )
if (
permission_configuration.allowed_system_groups
and user_group.name not in permission_configuration.allowed_system_groups
):
raise JsonableError(
_("'{setting_name}' setting cannot be set to '{group_name}' group.").format(
setting_name=setting_name, group_name=user_group.name
)
)
return user_group return user_group

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.7 on 2023-03-23 14:40
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zerver", "0486_clear_old_data_for_unused_usermessage_flags"),
]
operations = [
migrations.AddField(
model_name="realm",
name="can_access_all_users_group",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.RESTRICT,
related_name="+",
to="zerver.usergroup",
),
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 4.2.5 on 2023-09-21 14:00
from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
from django.db.models import OuterRef
def set_default_value_for_can_access_all_users_group(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
Realm = apps.get_model("zerver", "Realm")
UserGroup = apps.get_model("zerver", "UserGroup")
EVERYONE_GROUP_NAME = "role:everyone"
Realm.objects.filter(can_access_all_users_group=None).update(
can_access_all_users_group=UserGroup.objects.filter(
name=EVERYONE_GROUP_NAME, realm=OuterRef("id"), is_system_group=True
).values("pk")
)
class Migration(migrations.Migration):
dependencies = [
("zerver", "0487_realm_can_access_all_users_group"),
]
operations = [
migrations.RunPython(
set_default_value_for_can_access_all_users_group,
elidable=True,
reverse_code=migrations.RunPython.noop,
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2023-09-21 14:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zerver", "0488_set_default_value_for_can_access_all_users_group"),
]
operations = [
migrations.AlterField(
model_name="realm",
name="can_access_all_users_group",
field=models.ForeignKey(
on_delete=django.db.models.deletion.RESTRICT,
related_name="+",
to="zerver.usergroup",
),
),
]

View File

@ -445,6 +445,14 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
"UserGroup", on_delete=models.RESTRICT, related_name="+" "UserGroup", on_delete=models.RESTRICT, related_name="+"
) )
# on_delete field here is set to RESTRICT because we don't want to allow
# deleting a user group in case it is referenced by this setting.
# We are not using PROTECT since we want to allow deletion of user groups
# when realm itself is deleted.
can_access_all_users_group = models.ForeignKey(
"UserGroup", on_delete=models.RESTRICT, related_name="+"
)
# Who in the organization is allowed to invite other users to streams. # Who in the organization is allowed to invite other users to streams.
invite_to_stream_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) invite_to_stream_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY)
@ -814,6 +822,16 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
default_group_name=SystemGroups.ADMINISTRATORS, default_group_name=SystemGroups.ADMINISTRATORS,
id_field_name="create_multiuse_invite_group_id", id_field_name="create_multiuse_invite_group_id",
), ),
can_access_all_users_group=GroupPermissionSetting(
require_system_group=True,
allow_internet_group=False,
allow_owners_group=False,
allow_nobody_group=False,
allow_everyone_group=True,
default_group_name=SystemGroups.EVERYONE,
id_field_name="can_access_all_users_group_id",
allowed_system_groups=[SystemGroups.EVERYONE],
),
) )
DIGEST_WEEKDAY_VALUES = [0, 1, 2, 3, 4, 5, 6] DIGEST_WEEKDAY_VALUES = [0, 1, 2, 3, 4, 5, 6]

View File

@ -4543,6 +4543,16 @@ paths:
guest users to prominently highlight their status. guest users to prominently highlight their status.
**Changes**: New in Zulip 8.0 (feature level 216). **Changes**: New in Zulip 8.0 (feature level 216).
can_access_all_users_group:
type: integer
description: |
The ID of the [user group](/api/get-user-groups) whose members
are allowed to access all users in the organization.
This setting can currently only be set to `"role:everyone"`
system group.
**Changes**: New in Zulip 8.0 (feature level 225).
additionalProperties: false additionalProperties: false
example: example:
{ {
@ -14356,6 +14366,16 @@ paths:
guest users to prominently highlight their status. guest users to prominently highlight their status.
**Changes**: New in Zulip 8.0 (feature level 216). **Changes**: New in Zulip 8.0 (feature level 216).
realm_can_access_all_users_group:
type: integer
description: |
The ID of the [user group](/api/get-user-groups) whose members
are allowed to access all users in the organization.
This setting can currently only be set to `"role:members"`
and `"role:everyone"` system groups.
**Changes**: New in Zulip 8.0 (feature level 225).
zulip_plan_is_not_limited: zulip_plan_is_not_limited:
type: boolean type: boolean
description: | description: |
@ -19422,6 +19442,18 @@ components:
Name of the default group for the setting for system groups. Name of the default group for the setting for system groups.
This is non-null only for group-level settings. This is non-null only for group-level settings.
allowed_system_groups:
type: array
description: |
An array of names of system groups to which the setting can
be set to.
If the list is empty, the setting can be set to system groups
based on the other boolean fields.
**Changes**: New in Zulip 8.0 (feature level 225).
items:
type: string
User: User:
allOf: allOf:
- $ref: "#/components/schemas/UserBase" - $ref: "#/components/schemas/UserBase"

View File

@ -115,6 +115,7 @@ class HomeTest(ZulipTestCase):
"realm_bot_creation_policy", "realm_bot_creation_policy",
"realm_bot_domain", "realm_bot_domain",
"realm_bots", "realm_bots",
"realm_can_access_all_users_group",
"realm_create_multiuse_invite_group", "realm_create_multiuse_invite_group",
"realm_create_private_stream_policy", "realm_create_private_stream_policy",
"realm_create_public_stream_policy", "realm_create_public_stream_policy",

View File

@ -1286,6 +1286,11 @@ class RealmAPITest(ZulipTestCase):
user_group.name == SystemGroups.OWNERS user_group.name == SystemGroups.OWNERS
and not setting_permission_configuration.allow_owners_group and not setting_permission_configuration.allow_owners_group
) )
or (
setting_permission_configuration.allowed_system_groups
and user_group.name
not in setting_permission_configuration.allowed_system_groups
)
): ):
value = orjson.dumps(user_group.id).decode() value = orjson.dumps(user_group.id).decode()

View File

@ -175,6 +175,9 @@ def update_realm(
default=None, default=None,
), ),
enable_guest_user_indicator: Optional[bool] = REQ(json_validator=check_bool, default=None), enable_guest_user_indicator: Optional[bool] = REQ(json_validator=check_bool, default=None),
can_access_all_users_group_id: Optional[int] = REQ(
"can_access_all_users_group", json_validator=check_int, default=None
),
) -> HttpResponse: ) -> HttpResponse:
realm = user_profile.realm realm = user_profile.realm