diff --git a/docs/production/management-commands.md b/docs/production/management-commands.md index bc5288ace8..b961b8abca 100644 --- a/docs/production/management-commands.md +++ b/docs/production/management-commands.md @@ -117,11 +117,10 @@ There are dozens of useful management commands under * `./manage.py send_password_reset_email`: Sends password reset email(s) to one or more users. * `./manage.py change_user_email`: Change a user's email address. -* `./manage.py knight`: Can toggle whether a user is an administrator +* `./manage.py change_user_role`: Can change are user's role (easier done [via the UI](https://zulip.com/help/change-a-users-role)) or create an - API super user bot (with `--permission=api_super_user`), which are - needed for some content mirroring integrations. + `api_super_user`, which are needed for certain special API features. * `./manage.py export_single_user` does a limited version of the [main export tools](../production/export-and-import.md) containing just the messages accessible by a single user. diff --git a/docs/production/security-model.md b/docs/production/security-model.md index e031558939..e6165f1dd5 100644 --- a/docs/production/security-model.md +++ b/docs/production/security-model.md @@ -193,7 +193,7 @@ strength allowed is controlled by two settings in API super user bots cannot be created by Zulip users, including organization owners. They can only be created on the command - line (via `manage.py knight --permission=api_super_user`). + line (via `manage.py change_user_role api_super_user`). ## User-uploaded content diff --git a/zerver/management/commands/change_user_role.py b/zerver/management/commands/change_user_role.py new file mode 100644 index 0000000000..69e686021a --- /dev/null +++ b/zerver/management/commands/change_user_role.py @@ -0,0 +1,57 @@ +from argparse import ArgumentParser +from typing import Any + +from django.core.management.base import CommandError + +from zerver.lib.actions import do_change_is_api_super_user, do_change_user_role +from zerver.lib.management import ZulipBaseCommand +from zerver.models import UserProfile + + +class Command(ZulipBaseCommand): + help = """Change role of an existing user in their (own) Realm. + +ONLY perform this on customer request from an authorized person. +""" + + def add_arguments(self, parser: ArgumentParser) -> None: + parser.add_argument('email', metavar='', + help="email of user to change role") + parser.add_argument('new_role', metavar='', + choices=['owner', 'admin', 'member', 'guest', 'api_super_user'], + help="new role of the user") + parser.add_argument('--revoke', + dest='grant', + action="store_false", + help='Remove an api_super_user\'s rights.') + self.add_realm_args(parser, True) + + def handle(self, *args: Any, **options: Any) -> None: + email = options['email'] + realm = self.get_realm(options) + + user = self.get_user(email, realm) + + user_role_map = {'owner': UserProfile.ROLE_REALM_OWNER, + 'admin': UserProfile.ROLE_REALM_ADMINISTRATOR, + 'member': UserProfile.ROLE_MEMBER, + 'guest': UserProfile.ROLE_GUEST} + + if options['new_role'] != 'api_super_user': + new_role = user_role_map[options['new_role']] + if not options['grant']: + raise CommandError("Revoke not supported with this permission; please specify new role.") + if new_role == user.role: + raise CommandError("User already has this role.") + old_role_name = UserProfile.ROLE_ID_TO_NAME_MAP[user.role] + do_change_user_role(user, new_role, acting_user=None) + new_role_name = UserProfile.ROLE_ID_TO_NAME_MAP[user.role] + print(f"Role for {user.delivery_email} changed from {old_role_name} to {new_role_name}.") + else: + if user.is_api_super_user and options['grant']: + raise CommandError("User is already api super user for this realm.") + elif not user.is_api_super_user and not options['grant']: + raise CommandError("User is not api super user for this realm.") + do_change_is_api_super_user(user, options['grant']) + granted_text = "have" if options['grant'] else "not have" + print(f"{user.delivery_email} changed to {granted_text} {options['new_role']} permission.") diff --git a/zerver/management/commands/knight.py b/zerver/management/commands/knight.py deleted file mode 100644 index 77c13a0378..0000000000 --- a/zerver/management/commands/knight.py +++ /dev/null @@ -1,67 +0,0 @@ -from argparse import ArgumentParser -from typing import Any - -from django.core.management.base import CommandError - -from zerver.lib.actions import do_change_is_api_super_user, do_change_user_role -from zerver.lib.management import ZulipBaseCommand -from zerver.models import UserProfile - - -class Command(ZulipBaseCommand): - help = """Give an existing user administrative permissions over their (own) Realm. - -ONLY perform this on customer request from an authorized person. -""" - - def add_arguments(self, parser: ArgumentParser) -> None: - parser.add_argument('-f', '--for-real', - dest='ack', - action="store_true", - help='Acknowledgement that this is done according to policy.') - parser.add_argument('--revoke', - dest='grant', - action="store_false", - help='Remove an administrator\'s rights.') - parser.add_argument('--permission', - default='administer', - choices=['administer', 'api_super_user'], - help='Permission to grant/remove.') - parser.add_argument('email', metavar='', - help="email of user to knight") - self.add_realm_args(parser, True) - - def handle(self, *args: Any, **options: Any) -> None: - email = options['email'] - realm = self.get_realm(options) - - user = self.get_user(email, realm) - - if options['grant']: - if (user.is_realm_admin and options['permission'] == "administer" or - user.is_api_super_user and options['permission'] == "api_super_user"): - raise CommandError("User already has permission for this realm.") - else: - if options['ack']: - if options['permission'] == "api_super_user": - do_change_is_api_super_user(user, True) - elif options['permission'] == "administer": - do_change_user_role(user, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None) - print("Done!") - else: - print("Would have granted {} {} rights for {}".format( - email, options['permission'], user.realm.string_id)) - else: - if (user.is_realm_admin and options['permission'] == "administer" or - user.is_api_super_user and options['permission'] == "api_super_user"): - if options['ack']: - if options['permission'] == "api_super_user": - do_change_is_api_super_user(user, False) - elif options['permission'] == "administer": - do_change_user_role(user, UserProfile.ROLE_MEMBER, acting_user=None) - print("Done!") - else: - print("Would have removed {}'s {} rights on {}".format(email, options['permission'], - user.realm.string_id)) - else: - raise CommandError("User did not have permission for this realm!") diff --git a/zerver/management/commands/show_admins.py b/zerver/management/commands/show_admins.py index 21a06527b2..ede295a7d0 100644 --- a/zerver/management/commands/show_admins.py +++ b/zerver/management/commands/show_admins.py @@ -30,5 +30,4 @@ class Command(ZulipBaseCommand): else: raise CommandError('There are no admins for this realm!') - print('\nYou can use the "knight" management command to make more users admins.') - print('\nOr with the --revoke argument, remove admin status from users.') + print('\nYou can use the "change_user_role" management command to adjust roles.')