From e1446646e9d8333fbb32abebad25f6dd19124b66 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Wed, 2 Jun 2021 23:29:18 +0200 Subject: [PATCH 1/6] Add admin-cli --- README.md | 4 +- .../admin-cli/management/__init__.py | 0 .../admin-cli/management/commands/__init__.py | 0 .../admin-cli/management/commands/_utils.py | 14 ++ .../management/commands/group-create.py | 30 ++++ .../management/commands/group-delete.py | 16 ++ .../management/commands/group-modify.py | 48 ++++++ .../management/commands/groups-delete.py | 27 ++++ .../management/commands/groups-list.py | 8 + .../management/commands/permissions-list.py | 8 + .../management/commands/user-create.py | 111 ++++++++++++++ .../management/commands/user-delete.py | 16 ++ .../management/commands/user-modify.py | 137 ++++++++++++++++++ .../management/commands/users-delete.py | 25 ++++ .../management/commands/users-list.py | 8 + etebase_server/settings.py | 1 + 16 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 django_etebase/admin-cli/management/__init__.py create mode 100644 django_etebase/admin-cli/management/commands/__init__.py create mode 100644 django_etebase/admin-cli/management/commands/_utils.py create mode 100755 django_etebase/admin-cli/management/commands/group-create.py create mode 100755 django_etebase/admin-cli/management/commands/group-delete.py create mode 100755 django_etebase/admin-cli/management/commands/group-modify.py create mode 100755 django_etebase/admin-cli/management/commands/groups-delete.py create mode 100755 django_etebase/admin-cli/management/commands/groups-list.py create mode 100755 django_etebase/admin-cli/management/commands/permissions-list.py create mode 100755 django_etebase/admin-cli/management/commands/user-create.py create mode 100755 django_etebase/admin-cli/management/commands/user-delete.py create mode 100755 django_etebase/admin-cli/management/commands/user-modify.py create mode 100755 django_etebase/admin-cli/management/commands/users-delete.py create mode 100755 django_etebase/admin-cli/management/commands/users-list.py diff --git a/README.md b/README.md index 7a7efac..d2d1361 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Create yourself an admin user: ``` At this stage you need to create accounts to be used with the EteSync apps. To do that, please go to: -`www.your-etesync-install.com/admin` and create a new user to be used with the service. No need to set +`www.your-etesync-install.com/admin` or use CLI `./manage.py createuser ` and create a new user to be used with the service. No need to set a password, as Etebase uses a zero-knowledge proof for authentication, so the user will just create a password when creating the account from the apps. @@ -152,7 +152,7 @@ A quick summary can be found [on tldrlegal](https://tldrlegal.com/license/gnu-af ## Commercial licensing -For commercial licensing options, contact license@etebase.com +For commercial licensing options, contact license@etebase.com # Financially Supporting Etebase diff --git a/django_etebase/admin-cli/management/__init__.py b/django_etebase/admin-cli/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_etebase/admin-cli/management/commands/__init__.py b/django_etebase/admin-cli/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_etebase/admin-cli/management/commands/_utils.py b/django_etebase/admin-cli/management/commands/_utils.py new file mode 100644 index 0000000..2371c78 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/_utils.py @@ -0,0 +1,14 @@ +from distutils.util import strtobool +from datetime import datetime +import pytz + +def argbool(arg): + if arg == None: return None + return bool(strtobool(arg)) + +def argdate(arg): + if arg == None: return None + try: + return pytz.utc.localize(datetime.strptime(arg, '%Y-%m-%d %H:%M:%S')) + except ValueError: + return pytz.utc.localize(datetime.strptime(arg, '%Y-%m-%d %H:%M:%S.%f')) diff --git a/django_etebase/admin-cli/management/commands/group-create.py b/django_etebase/admin-cli/management/commands/group-create.py new file mode 100755 index 0000000..3f87570 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/group-create.py @@ -0,0 +1,30 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Group, Permission +from django.db.utils import IntegrityError + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'name' + , type=str + , help="New group's name." ) + parser.add_argument( '--permissions' + , type=str + , nargs='*' + , default=[] + , help="New group's permissions." ) + + def handle(self, *args, **options): + try: + for index,permission in enumerate(options["permissions"]): + options["permissions"][index] = Permission.objects.get(name=permission) + + group = Group.objects.create(name=options["name"]) + group.permissions.set(options["permissions"]) + group.save() + except (IntegrityError,Permission.DoesNotExist) as exception: + self.stdout.write(self.style.ERROR(f'Unable to create group "{options["name"]}": ' + str(exception))) + exit(1) + + self.stdout.write(self.style.SUCCESS(f'Group "{options["name"]}" has been created.')) + exit(0) diff --git a/django_etebase/admin-cli/management/commands/group-delete.py b/django_etebase/admin-cli/management/commands/group-delete.py new file mode 100755 index 0000000..5fd4195 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/group-delete.py @@ -0,0 +1,16 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Group + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'name' + , type=str + , help="Name of the group to be deleted." ) + + def handle(self, *args, **options): + try: + Group.objects.get(name = options["name"]).delete() + self.stdout.write(self.style.SUCCESS(f'Grop "{options["name"]}" has been deleted.')) + except Group.DoesNotExist as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete group "{options["name"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/group-modify.py b/django_etebase/admin-cli/management/commands/group-modify.py new file mode 100755 index 0000000..0ac9347 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/group-modify.py @@ -0,0 +1,48 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Group, Permission +from django.db.utils import IntegrityError + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'name' + , type=str + , help="Group's name." ) + parser.add_argument( '-n' + , '--new_name' + , '--new-name' + , type=str + , default=None + , help="Group's new name." ) + parser.add_argument( '-m' + , '--mode' + , type=str + , choices=['set', 'add', 'remove'] + , default='set' + , help="Set modification mode. Affects --permissions." ) + parser.add_argument( '--permissions' + , type=str + , nargs='*' + , default=None + , help="Group's new permissions." ) + + def handle(self, *args, **options): + try: + if options["permissions"] != None: + for index,permission in enumerate(options["permissions"]): + options["permissions"][index] = Permission.objects.get(name=permission) + + group = Group.objects.get(name=options["name"]) + + if options["new_name"] != None: + group.name = options["new_name"] + if options["permissions"] != None: + if options["mode"] == "set" : group.permissions.set ( options["permissions"]) + if options["mode"] == "add" : group.permissions.add (*options["permissions"]) + if options["mode"] == "remove" : group.permissions.remove(*options["permissions"]) + + group.save() + self.stdout.write(self.style.SUCCESS(f'Group "{options["name"]}" has been modified.')) + + except (User.DoesNotExist, ValueError) as exception: + self.stdout.write(self.style.ERROR(f'Unable to modify group "{options["name"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/groups-delete.py b/django_etebase/admin-cli/management/commands/groups-delete.py new file mode 100755 index 0000000..c89e61f --- /dev/null +++ b/django_etebase/admin-cli/management/commands/groups-delete.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Group + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( '-y' + , '--yes' + , action='store_true' + , default=False + , help="Allow deletion of all groups!" ) + + def handle(self, *args, **options): + if options["yes"] != True: + print('Do you really want to delete all groups? [y/N]: ', end='') + if input() not in ('y', 'Y', 'yes', 'YES', 'Yes'): + self.stdout.write(self.style.SUCCESS(f'No groups have been deleted.')) + exit(0) + + try: + for group in Group.objects.all(): + group.delete() + self.stdout.write(self.style.SUCCESS(f'All groups have been deleted.')) + exit(0) + except Group.DoesNotExist as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete all groups: ' + str(exception))) + exit(1) diff --git a/django_etebase/admin-cli/management/commands/groups-list.py b/django_etebase/admin-cli/management/commands/groups-list.py new file mode 100755 index 0000000..181c3f0 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/groups-list.py @@ -0,0 +1,8 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Group + +class Command(BaseCommand): + + def handle(self, *args, **options): + for group in Group.objects.all(): + print(group.name) diff --git a/django_etebase/admin-cli/management/commands/permissions-list.py b/django_etebase/admin-cli/management/commands/permissions-list.py new file mode 100755 index 0000000..9697e5d --- /dev/null +++ b/django_etebase/admin-cli/management/commands/permissions-list.py @@ -0,0 +1,8 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Permission + +class Command(BaseCommand): + + def handle(self, *args, **options): + for permission in Permission.objects.all(): + print(permission.name) diff --git a/django_etebase/admin-cli/management/commands/user-create.py b/django_etebase/admin-cli/management/commands/user-create.py new file mode 100755 index 0000000..8e0a730 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/user-create.py @@ -0,0 +1,111 @@ +from django.core.management.base import BaseCommand +from django_etebase.users.management.commands._utils import argbool, argdate +from myauth.models import User +from django.contrib.auth.models import Group, Permission +from django.db.utils import IntegrityError + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'username' + , type=str + , help="New user's login username." ) + parser.add_argument( '-p' + , '--password' + , type=str + , help="New user's plain text login password." ) + parser.add_argument( '-f' + , '--first_name' + , '--first' + , type=str + , default='' + , help="New user's first name." ) + parser.add_argument( '-l' + , '--last_name' + , '--last' + , type=str + , default='' + , help="New user's last name." ) + parser.add_argument( '-e' + , '--email' + , type=str + , default='' + , help="New user's email address." ) + parser.add_argument( '-a' + , '--is_active' + , '--active' + , nargs='?' + , type=argbool + , const=True + , default=False + , help="Enable login. [YES]" ) + parser.add_argument( '-s' + , '--is_staff' + , '--staff' + , nargs='?' + , type=argbool + , const=True + , default=False + , help="Mark user as staff. [NO]" ) + parser.add_argument( '-S' + , '--is_superuser' + , '--superuser' + , nargs='?' + , type=argbool + , const=True + , default=False + , help="Mark user as superuser. [NO]" ) + parser.add_argument( '-g' + , '--groups' + , type=str + , nargs='*' + , default=[] + , help="New user's groups." ) + parser.add_argument( '--user_permissions' + , '--user-permissions' + , '--permissions' + , type=str + , nargs='*' + , default=[] + , help="New user's user permissions." ) + parser.add_argument( '-j' + , '--date_joined' + , '--date-joined' + , type=str + , default=None + , help="New user's date joined, formated as '%Y-%m-%d %H:%M:%S.%f'." ) + parser.add_argument( '--last_login' + , '--last-login' + , type=str + , default=None + , help="New user's last login date, formated as '%Y-%m-%d %H:%M:%S.%f'." ) + + def handle(self, *args, **options): + try: + for index,group in enumerate(options["groups"]): + options["groups"][index] = Group.objects.get(name=group) + for index,permission in enumerate(options["user_permissions"]): + options["user_permissions"][index] = Permission.objects.get(name=permission) + options["date_joined"] = argdate(options["date_joined"]) + options["last_login" ] = argdate(options["last_login" ]) + + user = User.objects.create_user( username = options["username" ] + , password = options["password" ] + , email = options["email" ] + , first_name = options["first_name" ] + , last_name = options["last_name" ] + , is_superuser = options["is_superuser" ] + , is_staff = options["is_staff" ] + , is_active = options["is_active" ] + , last_login = options["last_login" ] ) + user.groups.set(options["groups"]) + user.user_permissions.set(options["user_permissions"]) + if options["date_joined"] != None: + user.date_joined = options["date_joined"] + user.save() + except (IntegrityError,Group.DoesNotExist,Permission.DoesNotExist) as exception: + self.stdout.write(self.style.ERROR(f'Unable to create user "{options["username"]}": ' + str(exception))) + exit(1) + + self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been created.')) + exit(0) diff --git a/django_etebase/admin-cli/management/commands/user-delete.py b/django_etebase/admin-cli/management/commands/user-delete.py new file mode 100755 index 0000000..9a35d8d --- /dev/null +++ b/django_etebase/admin-cli/management/commands/user-delete.py @@ -0,0 +1,16 @@ +from django.core.management.base import BaseCommand +from myauth.models import User + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'username' + , type=str + , help="Login username of the user to be deleted." ) + + def handle(self, *args, **options): + try: + User.objects.get(username = options["username"]).delete() + self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been deleted.')) + except User.DoesNotExist as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete user "{options["username"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/user-modify.py b/django_etebase/admin-cli/management/commands/user-modify.py new file mode 100755 index 0000000..bf39e68 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/user-modify.py @@ -0,0 +1,137 @@ +from django.core.management.base import BaseCommand +from django_etebase.users.management.commands._utils import argbool, argdate +from myauth.models import User +from django.contrib.auth.models import Group, Permission +from django.db.utils import IntegrityError + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'username' + , type=str + , help="User's login username." ) + parser.add_argument( '-u' + , '--new_username' + , '--new-username' + , type=str + , default=None + , help="User's new login username." ) + parser.add_argument( '-p' + , '--password' + , type=str + , help="User's new plain text login password." ) + parser.add_argument( '-f' + , '--first_name' + , '--first-name' + , '--first' + , type=str + , default=None + , help="User's new first name." ) + parser.add_argument( '-l' + , '--last_name' + , '--last-name' + , '--last' + , type=str + , default=None + , help="User's new last name." ) + parser.add_argument( '-e' + , '--email' + , type=str + , default=None + , help="User's new email address." ) + parser.add_argument( '-a' + , '--is_active' + , '--is-active' + , '--active' + , nargs='?' + , type=argbool + , const=True + , default=None + , help="Enable/Disable login." ) + parser.add_argument( '-s' + , '--is_staff' + , '--is-staff' + , '--staff' + , nargs='?' + , type=argbool + , const=True + , default=None + , help="Mark/Unmark user as staff." ) + parser.add_argument( '-S' + , '--is_superuser' + , '--is-superuser' + , '--superuser' + , nargs='?' + , type=argbool + , const=True + , default=None + , help="Mark/Unmark user as superuser." ) + parser.add_argument( '-m' + , '--mode' + , type=str + , choices=['set', 'add', 'remove'] + , default='set' + , help="Set modification mode. Affects --groups and --user_permissions." ) + parser.add_argument( '-g' + , '--groups' + , type=str + , nargs='*' + , default=None + , help="User's new groups." ) + parser.add_argument( '--user_permissions' + , '--user-permissions' + , '--permissions' + , type=str + , nargs='*' + , default=None + , help="User's new user permissions." ) + parser.add_argument( '-j' + , '--date_joined' + , '--date-joined' + , type=str + , default=None + , help="User's new date joined, formated as '%Y-%m-%d %H:%M:%S.%f'." ) + parser.add_argument( '--last_login' + , '--last-login' + , type=str + , default=None + , help="User's new last login date, formated as '%Y-%m-%d %H:%M:%S.%f'." ) + + def handle(self, *args, **options): + try: + if options["groups"] != None: + for index,group in enumerate(options["groups"]): + options["groups"][index] = Group.objects.get(name=group) + if options["user_permissions"] != None: + for index,permission in enumerate(options["user_permissions"]): + options["user_permissions"][index] = Permission.objects.get(name=permission) + options["date_joined"] = argdate(options["date_joined"]) + options["last_login" ] = argdate(options["last_login" ]) + + user = User.objects.get(username = options["username"]) + + if options["new_username"] != None: user.username = options["new_username"] + if options["password" ] != None: user.password = options["password" ] + if options["email" ] != None: user.email = options["email" ] + if options["first_name" ] != None: user.first_name = options["first_name" ] + if options["last_name" ] != None: user.last_name = options["last_name" ] + if options["is_active" ] != None: user.is_active = options["is_active" ] + if options["is_staff" ] != None: user.is_staff = options["is_staff" ] + if options["is_superuser"] != None: user.is_superuser = options["is_superuser"] + if options["date_joined" ] != None: user.date_joined = options["date_joined" ] + if options["last_login" ] != None: user.last_login = options["last_login" ] + + if options["groups"] != None: + if options["mode"] == "set" : user.groups.set ( options["groups"]) + if options["mode"] == "add" : user.groups.add (*options["groups"]) + if options["mode"] == "remove" : user.groups.remove(*options["groups"]) + if options["user_permissions"] != None: + if options["mode"] == "set" : user.user_permissions.set ( options["user_permissions"]) + if options["mode"] == "add" : user.user_permissions.add (*options["user_permissions"]) + if options["mode"] == "remove" : user.user_permissions.remove(*options["user_permissions"]) + + user.save() + self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been modified.')) + + except (User.DoesNotExist, ValueError) as exception: + self.stdout.write(self.style.ERROR(f'Unable to modify user "{options["username"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/users-delete.py b/django_etebase/admin-cli/management/commands/users-delete.py new file mode 100755 index 0000000..619e245 --- /dev/null +++ b/django_etebase/admin-cli/management/commands/users-delete.py @@ -0,0 +1,25 @@ +from django.core.management.base import BaseCommand +from myauth.models import User + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( '-y' + , '--yes' + , action='store_true' + , default=False + , help="Allow deletion of all users!" ) + + def handle(self, *args, **options): + if options["yes"] != True: + print('Do you really want to delete all users? [y/N]: ', end='') + if input()[0] not in ('y', 'Y', 'yes', 'YES', 'Yes'): + self.stdout.write(self.style.SUCCESS(f'No users have been deleted.')) + exit(0) + + try: + for user in User.objects.all(): + user.delete() + self.stdout.write(self.style.SUCCESS(f'All users have been deleted.')) + except User.DoesNotExist as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete all users: ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/users-list.py b/django_etebase/admin-cli/management/commands/users-list.py new file mode 100755 index 0000000..860385e --- /dev/null +++ b/django_etebase/admin-cli/management/commands/users-list.py @@ -0,0 +1,8 @@ +from django.core.management.base import BaseCommand +from myauth.models import User + +class Command(BaseCommand): + + def handle(self, *args, **options): + for user in User.objects.all(): + print(user.username) diff --git a/etebase_server/settings.py b/etebase_server/settings.py index aa77461..8f7e0ea 100644 --- a/etebase_server/settings.py +++ b/etebase_server/settings.py @@ -56,6 +56,7 @@ INSTALLED_APPS = [ "myauth.apps.MyauthConfig", "django_etebase.apps.DjangoEtebaseConfig", "django_etebase.token_auth.apps.TokenAuthConfig", + "django_etebase.admin-cli", ] MIDDLEWARE = [ From a940f0a753bea65b35fed6396a1929f1b0ab0154 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Thu, 3 Jun 2021 09:29:47 +0200 Subject: [PATCH 2/6] Fix and improve _utils import --- django_etebase/admin-cli/management/commands/user-create.py | 2 +- django_etebase/admin-cli/management/commands/user-modify.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/django_etebase/admin-cli/management/commands/user-create.py b/django_etebase/admin-cli/management/commands/user-create.py index 8e0a730..b1a345b 100755 --- a/django_etebase/admin-cli/management/commands/user-create.py +++ b/django_etebase/admin-cli/management/commands/user-create.py @@ -1,5 +1,5 @@ from django.core.management.base import BaseCommand -from django_etebase.users.management.commands._utils import argbool, argdate +from ._utils import argbool, argdate from myauth.models import User from django.contrib.auth.models import Group, Permission from django.db.utils import IntegrityError diff --git a/django_etebase/admin-cli/management/commands/user-modify.py b/django_etebase/admin-cli/management/commands/user-modify.py index bf39e68..8543eb3 100755 --- a/django_etebase/admin-cli/management/commands/user-modify.py +++ b/django_etebase/admin-cli/management/commands/user-modify.py @@ -1,5 +1,5 @@ from django.core.management.base import BaseCommand -from django_etebase.users.management.commands._utils import argbool, argdate +from ._utils import argbool, argdate from myauth.models import User from django.contrib.auth.models import Group, Permission from django.db.utils import IntegrityError From 211fb12d43be73da91b95ab65ab831294c6368b9 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Fri, 4 Jun 2021 00:13:18 +0200 Subject: [PATCH 3/6] Make new users active by default --- django_etebase/admin-cli/management/commands/user-create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_etebase/admin-cli/management/commands/user-create.py b/django_etebase/admin-cli/management/commands/user-create.py index b1a345b..d149054 100755 --- a/django_etebase/admin-cli/management/commands/user-create.py +++ b/django_etebase/admin-cli/management/commands/user-create.py @@ -37,7 +37,7 @@ class Command(BaseCommand): , nargs='?' , type=argbool , const=True - , default=False + , default=True , help="Enable login. [YES]" ) parser.add_argument( '-s' , '--is_staff' From 8803d749db4ae479f332bb67b977aba86c957653 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Fri, 4 Jun 2021 00:14:02 +0200 Subject: [PATCH 4/6] Improve users-delete interface --- .../management/commands/users-delete.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/django_etebase/admin-cli/management/commands/users-delete.py b/django_etebase/admin-cli/management/commands/users-delete.py index 619e245..5618be9 100755 --- a/django_etebase/admin-cli/management/commands/users-delete.py +++ b/django_etebase/admin-cli/management/commands/users-delete.py @@ -4,22 +4,27 @@ from myauth.models import User class Command(BaseCommand): def add_arguments(self, parser): - parser.add_argument( '-y' - , '--yes' + parser.add_argument( 'usernames' + , default=False + , type=str + , nargs='*' + , default=[] + , help="Delete ALL users!" ) + parser.add_argument( '-a' + , '--all' , action='store_true' , default=False - , help="Allow deletion of all users!" ) + , help="Delete ALL users!" ) def handle(self, *args, **options): - if options["yes"] != True: - print('Do you really want to delete all users? [y/N]: ', end='') - if input()[0] not in ('y', 'Y', 'yes', 'YES', 'Yes'): - self.stdout.write(self.style.SUCCESS(f'No users have been deleted.')) - exit(0) - try: - for user in User.objects.all(): - user.delete() - self.stdout.write(self.style.SUCCESS(f'All users have been deleted.')) + if options["all"]: + for user in User.objects.all(): + user.delete() + self.stdout.write(self.style.SUCCESS(f'All users have been deleted.')) + else: + for username in options["usernames"]: + User.objects.get(username=username).delete() + self.stdout.write(self.style.SUCCESS(f'Users have been deleted.')) except User.DoesNotExist as exception: - self.stdout.write(self.style.ERROR(f'Unable to delete all users: ' + str(exception))) + self.stdout.write(self.style.ERROR(f'Unable to delete users: ' + str(exception))) From b72d2121bd5b9e57b438772805fa912e41c071e4 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Fri, 4 Jun 2021 00:40:34 +0200 Subject: [PATCH 5/6] Remove unnecessary/broken features --- .../admin-cli/management/commands/_utils.py | 9 --- .../management/commands/group-create.py | 30 --------- .../management/commands/group-delete.py | 16 ----- .../management/commands/group-modify.py | 48 -------------- .../management/commands/groups-delete.py | 27 -------- .../management/commands/groups-list.py | 8 --- .../management/commands/permissions-list.py | 8 --- .../management/commands/user-create.py | 41 +----------- .../management/commands/user-delete.py | 16 ----- .../management/commands/user-modify.py | 66 ++----------------- .../management/commands/users-delete.py | 30 --------- 11 files changed, 7 insertions(+), 292 deletions(-) delete mode 100755 django_etebase/admin-cli/management/commands/group-create.py delete mode 100755 django_etebase/admin-cli/management/commands/group-delete.py delete mode 100755 django_etebase/admin-cli/management/commands/group-modify.py delete mode 100755 django_etebase/admin-cli/management/commands/groups-delete.py delete mode 100755 django_etebase/admin-cli/management/commands/groups-list.py delete mode 100755 django_etebase/admin-cli/management/commands/permissions-list.py delete mode 100755 django_etebase/admin-cli/management/commands/user-delete.py delete mode 100755 django_etebase/admin-cli/management/commands/users-delete.py diff --git a/django_etebase/admin-cli/management/commands/_utils.py b/django_etebase/admin-cli/management/commands/_utils.py index 2371c78..5aa8dfd 100644 --- a/django_etebase/admin-cli/management/commands/_utils.py +++ b/django_etebase/admin-cli/management/commands/_utils.py @@ -1,14 +1,5 @@ from distutils.util import strtobool -from datetime import datetime -import pytz def argbool(arg): if arg == None: return None return bool(strtobool(arg)) - -def argdate(arg): - if arg == None: return None - try: - return pytz.utc.localize(datetime.strptime(arg, '%Y-%m-%d %H:%M:%S')) - except ValueError: - return pytz.utc.localize(datetime.strptime(arg, '%Y-%m-%d %H:%M:%S.%f')) diff --git a/django_etebase/admin-cli/management/commands/group-create.py b/django_etebase/admin-cli/management/commands/group-create.py deleted file mode 100755 index 3f87570..0000000 --- a/django_etebase/admin-cli/management/commands/group-create.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Group, Permission -from django.db.utils import IntegrityError - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( 'name' - , type=str - , help="New group's name." ) - parser.add_argument( '--permissions' - , type=str - , nargs='*' - , default=[] - , help="New group's permissions." ) - - def handle(self, *args, **options): - try: - for index,permission in enumerate(options["permissions"]): - options["permissions"][index] = Permission.objects.get(name=permission) - - group = Group.objects.create(name=options["name"]) - group.permissions.set(options["permissions"]) - group.save() - except (IntegrityError,Permission.DoesNotExist) as exception: - self.stdout.write(self.style.ERROR(f'Unable to create group "{options["name"]}": ' + str(exception))) - exit(1) - - self.stdout.write(self.style.SUCCESS(f'Group "{options["name"]}" has been created.')) - exit(0) diff --git a/django_etebase/admin-cli/management/commands/group-delete.py b/django_etebase/admin-cli/management/commands/group-delete.py deleted file mode 100755 index 5fd4195..0000000 --- a/django_etebase/admin-cli/management/commands/group-delete.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Group - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( 'name' - , type=str - , help="Name of the group to be deleted." ) - - def handle(self, *args, **options): - try: - Group.objects.get(name = options["name"]).delete() - self.stdout.write(self.style.SUCCESS(f'Grop "{options["name"]}" has been deleted.')) - except Group.DoesNotExist as exception: - self.stdout.write(self.style.ERROR(f'Unable to delete group "{options["name"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/group-modify.py b/django_etebase/admin-cli/management/commands/group-modify.py deleted file mode 100755 index 0ac9347..0000000 --- a/django_etebase/admin-cli/management/commands/group-modify.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Group, Permission -from django.db.utils import IntegrityError - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( 'name' - , type=str - , help="Group's name." ) - parser.add_argument( '-n' - , '--new_name' - , '--new-name' - , type=str - , default=None - , help="Group's new name." ) - parser.add_argument( '-m' - , '--mode' - , type=str - , choices=['set', 'add', 'remove'] - , default='set' - , help="Set modification mode. Affects --permissions." ) - parser.add_argument( '--permissions' - , type=str - , nargs='*' - , default=None - , help="Group's new permissions." ) - - def handle(self, *args, **options): - try: - if options["permissions"] != None: - for index,permission in enumerate(options["permissions"]): - options["permissions"][index] = Permission.objects.get(name=permission) - - group = Group.objects.get(name=options["name"]) - - if options["new_name"] != None: - group.name = options["new_name"] - if options["permissions"] != None: - if options["mode"] == "set" : group.permissions.set ( options["permissions"]) - if options["mode"] == "add" : group.permissions.add (*options["permissions"]) - if options["mode"] == "remove" : group.permissions.remove(*options["permissions"]) - - group.save() - self.stdout.write(self.style.SUCCESS(f'Group "{options["name"]}" has been modified.')) - - except (User.DoesNotExist, ValueError) as exception: - self.stdout.write(self.style.ERROR(f'Unable to modify group "{options["name"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/groups-delete.py b/django_etebase/admin-cli/management/commands/groups-delete.py deleted file mode 100755 index c89e61f..0000000 --- a/django_etebase/admin-cli/management/commands/groups-delete.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Group - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( '-y' - , '--yes' - , action='store_true' - , default=False - , help="Allow deletion of all groups!" ) - - def handle(self, *args, **options): - if options["yes"] != True: - print('Do you really want to delete all groups? [y/N]: ', end='') - if input() not in ('y', 'Y', 'yes', 'YES', 'Yes'): - self.stdout.write(self.style.SUCCESS(f'No groups have been deleted.')) - exit(0) - - try: - for group in Group.objects.all(): - group.delete() - self.stdout.write(self.style.SUCCESS(f'All groups have been deleted.')) - exit(0) - except Group.DoesNotExist as exception: - self.stdout.write(self.style.ERROR(f'Unable to delete all groups: ' + str(exception))) - exit(1) diff --git a/django_etebase/admin-cli/management/commands/groups-list.py b/django_etebase/admin-cli/management/commands/groups-list.py deleted file mode 100755 index 181c3f0..0000000 --- a/django_etebase/admin-cli/management/commands/groups-list.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Group - -class Command(BaseCommand): - - def handle(self, *args, **options): - for group in Group.objects.all(): - print(group.name) diff --git a/django_etebase/admin-cli/management/commands/permissions-list.py b/django_etebase/admin-cli/management/commands/permissions-list.py deleted file mode 100755 index 9697e5d..0000000 --- a/django_etebase/admin-cli/management/commands/permissions-list.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.core.management.base import BaseCommand -from django.contrib.auth.models import Permission - -class Command(BaseCommand): - - def handle(self, *args, **options): - for permission in Permission.objects.all(): - print(permission.name) diff --git a/django_etebase/admin-cli/management/commands/user-create.py b/django_etebase/admin-cli/management/commands/user-create.py index d149054..0d4bbe1 100755 --- a/django_etebase/admin-cli/management/commands/user-create.py +++ b/django_etebase/admin-cli/management/commands/user-create.py @@ -1,7 +1,6 @@ from django.core.management.base import BaseCommand -from ._utils import argbool, argdate +from ._utils import argbool from myauth.models import User -from django.contrib.auth.models import Group, Permission from django.db.utils import IntegrityError class Command(BaseCommand): @@ -55,40 +54,9 @@ class Command(BaseCommand): , const=True , default=False , help="Mark user as superuser. [NO]" ) - parser.add_argument( '-g' - , '--groups' - , type=str - , nargs='*' - , default=[] - , help="New user's groups." ) - parser.add_argument( '--user_permissions' - , '--user-permissions' - , '--permissions' - , type=str - , nargs='*' - , default=[] - , help="New user's user permissions." ) - parser.add_argument( '-j' - , '--date_joined' - , '--date-joined' - , type=str - , default=None - , help="New user's date joined, formated as '%Y-%m-%d %H:%M:%S.%f'." ) - parser.add_argument( '--last_login' - , '--last-login' - , type=str - , default=None - , help="New user's last login date, formated as '%Y-%m-%d %H:%M:%S.%f'." ) def handle(self, *args, **options): try: - for index,group in enumerate(options["groups"]): - options["groups"][index] = Group.objects.get(name=group) - for index,permission in enumerate(options["user_permissions"]): - options["user_permissions"][index] = Permission.objects.get(name=permission) - options["date_joined"] = argdate(options["date_joined"]) - options["last_login" ] = argdate(options["last_login" ]) - user = User.objects.create_user( username = options["username" ] , password = options["password" ] , email = options["email" ] @@ -96,12 +64,7 @@ class Command(BaseCommand): , last_name = options["last_name" ] , is_superuser = options["is_superuser" ] , is_staff = options["is_staff" ] - , is_active = options["is_active" ] - , last_login = options["last_login" ] ) - user.groups.set(options["groups"]) - user.user_permissions.set(options["user_permissions"]) - if options["date_joined"] != None: - user.date_joined = options["date_joined"] + , is_active = options["is_active" ] ) user.save() except (IntegrityError,Group.DoesNotExist,Permission.DoesNotExist) as exception: self.stdout.write(self.style.ERROR(f'Unable to create user "{options["username"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/user-delete.py b/django_etebase/admin-cli/management/commands/user-delete.py deleted file mode 100755 index 9a35d8d..0000000 --- a/django_etebase/admin-cli/management/commands/user-delete.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.core.management.base import BaseCommand -from myauth.models import User - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( 'username' - , type=str - , help="Login username of the user to be deleted." ) - - def handle(self, *args, **options): - try: - User.objects.get(username = options["username"]).delete() - self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been deleted.')) - except User.DoesNotExist as exception: - self.stdout.write(self.style.ERROR(f'Unable to delete user "{options["username"]}": ' + str(exception))) diff --git a/django_etebase/admin-cli/management/commands/user-modify.py b/django_etebase/admin-cli/management/commands/user-modify.py index 8543eb3..2f0f686 100755 --- a/django_etebase/admin-cli/management/commands/user-modify.py +++ b/django_etebase/admin-cli/management/commands/user-modify.py @@ -1,7 +1,6 @@ from django.core.management.base import BaseCommand -from ._utils import argbool, argdate +from ._utils import argbool from myauth.models import User -from django.contrib.auth.models import Group, Permission from django.db.utils import IntegrityError class Command(BaseCommand): @@ -16,10 +15,6 @@ class Command(BaseCommand): , type=str , default=None , help="User's new login username." ) - parser.add_argument( '-p' - , '--password' - , type=str - , help="User's new plain text login password." ) parser.add_argument( '-f' , '--first_name' , '--first-name' @@ -66,72 +61,21 @@ class Command(BaseCommand): , const=True , default=None , help="Mark/Unmark user as superuser." ) - parser.add_argument( '-m' - , '--mode' - , type=str - , choices=['set', 'add', 'remove'] - , default='set' - , help="Set modification mode. Affects --groups and --user_permissions." ) - parser.add_argument( '-g' - , '--groups' - , type=str - , nargs='*' - , default=None - , help="User's new groups." ) - parser.add_argument( '--user_permissions' - , '--user-permissions' - , '--permissions' - , type=str - , nargs='*' - , default=None - , help="User's new user permissions." ) - parser.add_argument( '-j' - , '--date_joined' - , '--date-joined' - , type=str - , default=None - , help="User's new date joined, formated as '%Y-%m-%d %H:%M:%S.%f'." ) - parser.add_argument( '--last_login' - , '--last-login' - , type=str - , default=None - , help="User's new last login date, formated as '%Y-%m-%d %H:%M:%S.%f'." ) def handle(self, *args, **options): try: - if options["groups"] != None: - for index,group in enumerate(options["groups"]): - options["groups"][index] = Group.objects.get(name=group) - if options["user_permissions"] != None: - for index,permission in enumerate(options["user_permissions"]): - options["user_permissions"][index] = Permission.objects.get(name=permission) - options["date_joined"] = argdate(options["date_joined"]) - options["last_login" ] = argdate(options["last_login" ]) - user = User.objects.get(username = options["username"]) - if options["new_username"] != None: user.username = options["new_username"] - if options["password" ] != None: user.password = options["password" ] if options["email" ] != None: user.email = options["email" ] if options["first_name" ] != None: user.first_name = options["first_name" ] if options["last_name" ] != None: user.last_name = options["last_name" ] if options["is_active" ] != None: user.is_active = options["is_active" ] if options["is_staff" ] != None: user.is_staff = options["is_staff" ] if options["is_superuser"] != None: user.is_superuser = options["is_superuser"] - if options["date_joined" ] != None: user.date_joined = options["date_joined" ] - if options["last_login" ] != None: user.last_login = options["last_login" ] - - if options["groups"] != None: - if options["mode"] == "set" : user.groups.set ( options["groups"]) - if options["mode"] == "add" : user.groups.add (*options["groups"]) - if options["mode"] == "remove" : user.groups.remove(*options["groups"]) - if options["user_permissions"] != None: - if options["mode"] == "set" : user.user_permissions.set ( options["user_permissions"]) - if options["mode"] == "add" : user.user_permissions.add (*options["user_permissions"]) - if options["mode"] == "remove" : user.user_permissions.remove(*options["user_permissions"]) - user.save() - self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been modified.')) - except (User.DoesNotExist, ValueError) as exception: self.stdout.write(self.style.ERROR(f'Unable to modify user "{options["username"]}": ' + str(exception))) + exit(1) + + self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been modified.')) + exit(0) diff --git a/django_etebase/admin-cli/management/commands/users-delete.py b/django_etebase/admin-cli/management/commands/users-delete.py deleted file mode 100755 index 5618be9..0000000 --- a/django_etebase/admin-cli/management/commands/users-delete.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.core.management.base import BaseCommand -from myauth.models import User - -class Command(BaseCommand): - - def add_arguments(self, parser): - parser.add_argument( 'usernames' - , default=False - , type=str - , nargs='*' - , default=[] - , help="Delete ALL users!" ) - parser.add_argument( '-a' - , '--all' - , action='store_true' - , default=False - , help="Delete ALL users!" ) - - def handle(self, *args, **options): - try: - if options["all"]: - for user in User.objects.all(): - user.delete() - self.stdout.write(self.style.SUCCESS(f'All users have been deleted.')) - else: - for username in options["usernames"]: - User.objects.get(username=username).delete() - self.stdout.write(self.style.SUCCESS(f'Users have been deleted.')) - except User.DoesNotExist as exception: - self.stdout.write(self.style.ERROR(f'Unable to delete users: ' + str(exception))) From a9c0807a6945dee445b9018c25aedb7b6f628503 Mon Sep 17 00:00:00 2001 From: Martin Michalec Date: Mon, 14 Jun 2021 21:26:45 +0200 Subject: [PATCH 6/6] Change admin-cli as discussed in #99 --- .../{user-create.py => users-create.py} | 22 +++++-------- .../management/commands/users-delete.py | 33 +++++++++++++++++++ .../management/commands/users-list.py | 1 + .../{user-modify.py => users-modify.py} | 0 4 files changed, 42 insertions(+), 14 deletions(-) rename django_etebase/admin-cli/management/commands/{user-create.py => users-create.py} (69%) create mode 100755 django_etebase/admin-cli/management/commands/users-delete.py rename django_etebase/admin-cli/management/commands/{user-modify.py => users-modify.py} (100%) diff --git a/django_etebase/admin-cli/management/commands/user-create.py b/django_etebase/admin-cli/management/commands/users-create.py similarity index 69% rename from django_etebase/admin-cli/management/commands/user-create.py rename to django_etebase/admin-cli/management/commands/users-create.py index 0d4bbe1..abc4268 100755 --- a/django_etebase/admin-cli/management/commands/user-create.py +++ b/django_etebase/admin-cli/management/commands/users-create.py @@ -9,10 +9,6 @@ class Command(BaseCommand): parser.add_argument( 'username' , type=str , help="New user's login username." ) - parser.add_argument( '-p' - , '--password' - , type=str - , help="New user's plain text login password." ) parser.add_argument( '-f' , '--first_name' , '--first' @@ -57,16 +53,14 @@ class Command(BaseCommand): def handle(self, *args, **options): try: - user = User.objects.create_user( username = options["username" ] - , password = options["password" ] - , email = options["email" ] - , first_name = options["first_name" ] - , last_name = options["last_name" ] - , is_superuser = options["is_superuser" ] - , is_staff = options["is_staff" ] - , is_active = options["is_active" ] ) - user.save() - except (IntegrityError,Group.DoesNotExist,Permission.DoesNotExist) as exception: + User.objects.create_user( username = options["username" ] + , email = options["email" ] + , first_name = options["first_name" ] + , last_name = options["last_name" ] + , is_active = options["is_active" ] + , is_staff = options["is_staff" ] + , is_superuser = options["is_superuser"] ) + except IntegrityError as exception: self.stdout.write(self.style.ERROR(f'Unable to create user "{options["username"]}": ' + str(exception))) exit(1) diff --git a/django_etebase/admin-cli/management/commands/users-delete.py b/django_etebase/admin-cli/management/commands/users-delete.py new file mode 100755 index 0000000..bc5c56b --- /dev/null +++ b/django_etebase/admin-cli/management/commands/users-delete.py @@ -0,0 +1,33 @@ +from django.core.management.base import BaseCommand +from myauth.models import User +from django.db.models.deletion import ProtectedError + +class Command(BaseCommand): + + def add_arguments(self, parser): + parser.add_argument( 'username' + , type=str + , help="Login username of the user to be deleted." ) + parser.add_argument( '--delete-user-data' + , action='store_true' + , default=False + , help="Delete all user's collections!" ) + + def handle(self, *args, **options): + try: + user = User.objects.get(username = options["username"]) + if options["delete_user_data"]: + collections = user.collection_set.all() + for collection in collections: + collection.delete() + user.delete() + except User.DoesNotExist as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete user "{options["username"]}": ' + str(exception))) + exit(1) + except ProtectedError as exception: + self.stdout.write(self.style.ERROR(f'Unable to delete user "{options["username"]}": ' + str(exception))) + self.stdout.write(self.style.NOTICE('Use --delete-user-data to overcome this protection.')) + exit(2) + + self.stdout.write(self.style.SUCCESS(f'User "{options["username"]}" has been deleted.')) + exit(0) diff --git a/django_etebase/admin-cli/management/commands/users-list.py b/django_etebase/admin-cli/management/commands/users-list.py index 860385e..f4e1402 100755 --- a/django_etebase/admin-cli/management/commands/users-list.py +++ b/django_etebase/admin-cli/management/commands/users-list.py @@ -6,3 +6,4 @@ class Command(BaseCommand): def handle(self, *args, **options): for user in User.objects.all(): print(user.username) + exit(0) diff --git a/django_etebase/admin-cli/management/commands/user-modify.py b/django_etebase/admin-cli/management/commands/users-modify.py similarity index 100% rename from django_etebase/admin-cli/management/commands/user-modify.py rename to django_etebase/admin-cli/management/commands/users-modify.py