mirror of
https://github.com/zulip/zulip.git
synced 2025-11-17 04:12:02 +00:00
user-groups: Add create API endpoint.
Significantly modified by tabbott for better security structure.
This commit is contained in:
@@ -4,6 +4,7 @@ from typing import (
|
|||||||
)
|
)
|
||||||
from mypy_extensions import TypedDict
|
from mypy_extensions import TypedDict
|
||||||
|
|
||||||
|
import django.db.utils
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
@@ -48,6 +49,7 @@ from zerver.lib.topic_mutes import (
|
|||||||
add_topic_mute,
|
add_topic_mute,
|
||||||
remove_topic_mute,
|
remove_topic_mute,
|
||||||
)
|
)
|
||||||
|
from zerver.lib.user_groups import create_user_group
|
||||||
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \
|
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \
|
||||||
RealmDomain, \
|
RealmDomain, \
|
||||||
Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \
|
Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \
|
||||||
@@ -4183,3 +4185,10 @@ def do_update_user_custom_profile_data(user_profile, data):
|
|||||||
update_or_create(user_profile=user_profile,
|
update_or_create(user_profile=user_profile,
|
||||||
field_id=field['id'],
|
field_id=field['id'],
|
||||||
defaults={'value': field['value']})
|
defaults={'value': field['value']})
|
||||||
|
|
||||||
|
def check_add_user_group(realm, name, initial_members, description):
|
||||||
|
# type: (Realm, Text, List[UserProfile], Text) -> None
|
||||||
|
try:
|
||||||
|
create_user_group(name, initial_members, realm, description=description)
|
||||||
|
except django.db.utils.IntegrityError:
|
||||||
|
raise JsonableError(_("User group '%s' already exists." % (name,)))
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.models import UserProfile, Realm, UserGroupMembership, UserGroup
|
from zerver.models import UserProfile, Realm, UserGroupMembership, UserGroup
|
||||||
from typing import Dict, Iterable, List, Text
|
from typing import Dict, Iterable, List, Text
|
||||||
|
|
||||||
|
def access_user_group_by_id(user_group_id: int, realm: Realm) -> UserGroup:
|
||||||
|
try:
|
||||||
|
user_group = UserGroup.objects.get(id=user_group_id, realm=realm)
|
||||||
|
except UserGroup.DoesNotExist:
|
||||||
|
raise JsonableError(_("Invalid user group"))
|
||||||
|
return user_group
|
||||||
|
|
||||||
def user_groups_in_realm(realm):
|
def user_groups_in_realm(realm):
|
||||||
# type: (Realm) -> List[UserGroup]
|
# type: (Realm) -> List[UserGroup]
|
||||||
user_groups = UserGroup.objects.filter(realm=realm)
|
user_groups = UserGroup.objects.filter(realm=realm)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from typing import Text
|
from typing import List, Text
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_full_name
|
from zerver.lib.actions import do_change_full_name
|
||||||
from zerver.lib.request import JsonableError
|
from zerver.lib.request import JsonableError
|
||||||
from zerver.models import UserProfile, Service
|
from zerver.models import UserProfile, Service, Realm, \
|
||||||
|
get_user_profile_by_id
|
||||||
|
|
||||||
def check_full_name(full_name_raw):
|
def check_full_name(full_name_raw):
|
||||||
# type: (Text) -> Text
|
# type: (Text) -> Text
|
||||||
@@ -43,3 +44,20 @@ def check_valid_interface_type(interface_type):
|
|||||||
# type: (int) -> None
|
# type: (int) -> None
|
||||||
if interface_type not in Service.ALLOWED_INTERFACE_TYPES:
|
if interface_type not in Service.ALLOWED_INTERFACE_TYPES:
|
||||||
raise JsonableError(_('Invalid interface type'))
|
raise JsonableError(_('Invalid interface type'))
|
||||||
|
|
||||||
|
def user_ids_to_users(user_ids: List[int], realm: Realm) -> List[UserProfile]:
|
||||||
|
# TODO: Change this to do a single bulk query with
|
||||||
|
# generic_bulk_cached_fetch; it'll be faster.
|
||||||
|
#
|
||||||
|
# TODO: Consider adding a flag to control whether deactivated
|
||||||
|
# users should be included.
|
||||||
|
user_profiles = []
|
||||||
|
for user_id in user_ids:
|
||||||
|
try:
|
||||||
|
user_profile = get_user_profile_by_id(user_id)
|
||||||
|
except UserProfile.DoesNotExist:
|
||||||
|
raise JsonableError(_("Invalid user ID: %s" % (user_id,)))
|
||||||
|
if user_profile.realm != realm:
|
||||||
|
raise JsonableError(_("Invalid user ID: %s" % (user_id,)))
|
||||||
|
user_profiles.append(user_profile)
|
||||||
|
return user_profiles
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from typing import Any, List, Optional, Text
|
from typing import Any, List, Optional, Text
|
||||||
|
|
||||||
|
import ujson
|
||||||
import django
|
import django
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
@@ -55,3 +56,39 @@ class UserGroupTestCase(ZulipTestCase):
|
|||||||
with mock.patch('zerver.lib.user_groups.remove_user_from_user_group',
|
with mock.patch('zerver.lib.user_groups.remove_user_from_user_group',
|
||||||
side_effect=Exception):
|
side_effect=Exception):
|
||||||
self.assertFalse(check_remove_user_from_user_group(othello, user_group))
|
self.assertFalse(check_remove_user_from_user_group(othello, user_group))
|
||||||
|
|
||||||
|
class UserGroupAPITestCase(ZulipTestCase):
|
||||||
|
def test_user_group_create(self):
|
||||||
|
# type: () -> None
|
||||||
|
hamlet = self.example_user('hamlet')
|
||||||
|
|
||||||
|
# Test success
|
||||||
|
self.login(self.example_email("hamlet"))
|
||||||
|
params = {
|
||||||
|
'name': 'support',
|
||||||
|
'members': ujson.dumps([hamlet.id]),
|
||||||
|
'description': 'Support team',
|
||||||
|
}
|
||||||
|
result = self.client_post('/json/user_groups/create', info=params)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
self.assert_length(UserGroup.objects.all(), 1)
|
||||||
|
|
||||||
|
# Test invalid member error
|
||||||
|
params = {
|
||||||
|
'name': 'backend',
|
||||||
|
'members': ujson.dumps([1111]),
|
||||||
|
'description': 'Backend team',
|
||||||
|
}
|
||||||
|
result = self.client_post('/json/user_groups/create', info=params)
|
||||||
|
self.assert_json_error(result, "Invalid user ID: 1111")
|
||||||
|
self.assert_length(UserGroup.objects.all(), 1)
|
||||||
|
|
||||||
|
# Test we cannot add hamlet again
|
||||||
|
params = {
|
||||||
|
'name': 'support',
|
||||||
|
'members': ujson.dumps([hamlet.id]),
|
||||||
|
'description': 'Support team',
|
||||||
|
}
|
||||||
|
result = self.client_post('/json/user_groups/create', info=params)
|
||||||
|
self.assert_json_error(result, "User group 'support' already exists.")
|
||||||
|
self.assert_length(UserGroup.objects.all(), 1)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from zerver.models import UserProfile, Recipient, \
|
|||||||
|
|
||||||
from zerver.lib.avatar import avatar_url
|
from zerver.lib.avatar import avatar_url
|
||||||
from zerver.lib.email_mirror import create_missed_message_address
|
from zerver.lib.email_mirror import create_missed_message_address
|
||||||
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.send_email import send_future_email
|
from zerver.lib.send_email import send_future_email
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
get_emails_from_user_ids,
|
get_emails_from_user_ids,
|
||||||
@@ -35,6 +36,7 @@ from zerver.lib.actions import (
|
|||||||
)
|
)
|
||||||
from zerver.lib.topic_mutes import add_topic_mute
|
from zerver.lib.topic_mutes import add_topic_mute
|
||||||
from zerver.lib.stream_topic import StreamTopicTarget
|
from zerver.lib.stream_topic import StreamTopicTarget
|
||||||
|
from zerver.lib.users import user_ids_to_users
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
@@ -265,6 +267,18 @@ class UserProfileTest(ZulipTestCase):
|
|||||||
|
|
||||||
self.assertFalse(m.called)
|
self.assertFalse(m.called)
|
||||||
|
|
||||||
|
def test_user_ids_to_users(self) -> None:
|
||||||
|
real_user_ids = [
|
||||||
|
self.example_user('hamlet').id,
|
||||||
|
self.example_user('cordelia').id,
|
||||||
|
]
|
||||||
|
|
||||||
|
user_ids_to_users(real_user_ids, get_realm("zulip"))
|
||||||
|
with self.assertRaises(JsonableError):
|
||||||
|
user_ids_to_users([1234], get_realm("zephyr"))
|
||||||
|
with self.assertRaises(JsonableError):
|
||||||
|
user_ids_to_users(real_user_ids, get_realm("zephyr"))
|
||||||
|
|
||||||
class ActivateTest(ZulipTestCase):
|
class ActivateTest(ZulipTestCase):
|
||||||
def test_basics(self) -> None:
|
def test_basics(self) -> None:
|
||||||
user = self.example_user('hamlet')
|
user = self.example_user('hamlet')
|
||||||
|
|||||||
22
zerver/views/user_groups.py
Normal file
22
zerver/views/user_groups.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from django.http import HttpResponse, HttpRequest
|
||||||
|
|
||||||
|
from typing import List, Text
|
||||||
|
|
||||||
|
from zerver.context_processors import get_realm_from_request
|
||||||
|
from zerver.lib.actions import check_add_user_group
|
||||||
|
from zerver.lib.request import has_request_variables, REQ
|
||||||
|
from zerver.lib.response import json_success, json_error
|
||||||
|
from zerver.lib.users import user_ids_to_users
|
||||||
|
from zerver.lib.validator import check_list, check_string, check_int, \
|
||||||
|
check_short_string
|
||||||
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
@has_request_variables
|
||||||
|
def add_user_group(request, user_profile,
|
||||||
|
name=REQ(),
|
||||||
|
members=REQ(validator=check_list(check_int), default=[]),
|
||||||
|
description=REQ()):
|
||||||
|
# type: (HttpRequest, UserProfile, Text, List[int], Text) -> HttpResponse
|
||||||
|
user_profiles = user_ids_to_users(members, user_profile.realm)
|
||||||
|
check_add_user_group(user_profile.realm, name, user_profiles, description)
|
||||||
|
return json_success()
|
||||||
@@ -27,6 +27,7 @@ import zerver.views.zephyr
|
|||||||
import zerver.views.users
|
import zerver.views.users
|
||||||
import zerver.views.unsubscribe
|
import zerver.views.unsubscribe
|
||||||
import zerver.views.integrations
|
import zerver.views.integrations
|
||||||
|
import zerver.views.user_groups
|
||||||
import zerver.views.user_settings
|
import zerver.views.user_settings
|
||||||
import zerver.views.muting
|
import zerver.views.muting
|
||||||
import zerver.views.streams
|
import zerver.views.streams
|
||||||
@@ -214,6 +215,10 @@ v1_api_and_json_patterns = [
|
|||||||
{'POST': 'zerver.views.push_notifications.add_android_reg_id',
|
{'POST': 'zerver.views.push_notifications.add_android_reg_id',
|
||||||
'DELETE': 'zerver.views.push_notifications.remove_android_reg_id'}),
|
'DELETE': 'zerver.views.push_notifications.remove_android_reg_id'}),
|
||||||
|
|
||||||
|
# user_groups -> zerver.views.user_groups
|
||||||
|
url(r'^user_groups/create$', rest_dispatch,
|
||||||
|
{'POST': 'zerver.views.user_groups.add_user_group'}),
|
||||||
|
|
||||||
# users/me -> zerver.views.user_settings
|
# users/me -> zerver.views.user_settings
|
||||||
url(r'^users/me/api_key/regenerate$', rest_dispatch,
|
url(r'^users/me/api_key/regenerate$', rest_dispatch,
|
||||||
{'POST': 'zerver.views.user_settings.regenerate_api_key'}),
|
{'POST': 'zerver.views.user_settings.regenerate_api_key'}),
|
||||||
|
|||||||
Reference in New Issue
Block a user