From e124c830ace065b2744ffa8042ea6b8b6e2003a1 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 7 Oct 2014 13:50:59 +0000 Subject: [PATCH] Disable account --- orchestra/admin/options.py | 6 ++--- orchestra/apps/accounts/actions.py | 22 +++++++++++++++++++ orchestra/apps/accounts/admin.py | 3 +++ orchestra/apps/accounts/models.py | 14 ++++++++++++ .../databases/tests/functional_tests/tests.py | 12 +++++++++- orchestra/apps/orchestration/middlewares.py | 17 ++++++++------ orchestra/apps/payments/actions.py | 14 ++++++------ .../tests/functional_tests/tests.py | 19 ++++++++++++++++ 8 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 orchestra/apps/accounts/actions.py diff --git a/orchestra/admin/options.py b/orchestra/admin/options.py index 8d3c71d4..b3612763 100644 --- a/orchestra/admin/options.py +++ b/orchestra/admin/options.py @@ -73,9 +73,9 @@ class ChangeViewActionsMixin(object): new_urls += patterns('', url('^(\d+)/%s/$' % action.url_name, admin_site.admin_view(action), - name='%s_%s_%s' % (opts.app_label, - opts.model_name, - action.url_name))) + name='%s_%s_%s' % (opts.app_label, opts.model_name, action.url_name) + ) + ) return new_urls + urls def get_change_view_actions(self, obj=None): diff --git a/orchestra/apps/accounts/actions.py b/orchestra/apps/accounts/actions.py new file mode 100644 index 00000000..f6ee4286 --- /dev/null +++ b/orchestra/apps/accounts/actions.py @@ -0,0 +1,22 @@ +from django.contrib import messages +from django.db import transaction +from django.utils.translation import ungettext, ugettext_lazy as _ + +from orchestra.admin.decorators import action_with_confirmation + + +@transaction.atomic +@action_with_confirmation() +def disable(modeladmin, request, queryset): + num = 0 + for account in queryset: + account.disable() + modeladmin.log_change(request, account, _("Disabled")) + num += 1 + msg = ungettext( + _("Selected account and related services has been disabled."), + _("%s selected accounts and related services have been disabled.") % num, + num) + modeladmin.message_user(request, msg) +disable.url_name = 'disable' +disable.verbose_name = _("Disable") diff --git a/orchestra/apps/accounts/admin.py b/orchestra/apps/accounts/admin.py index 65f1592b..75aecb0b 100644 --- a/orchestra/apps/accounts/admin.py +++ b/orchestra/apps/accounts/admin.py @@ -13,6 +13,7 @@ from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, ch from orchestra.core import services, accounts from orchestra.forms import UserChangeForm +from .actions import disable from .filters import HasMainUserListFilter from .forms import AccountCreationForm from .models import Account @@ -55,6 +56,8 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin) filter_horizontal = () change_readonly_fields = ('username',) change_form_template = 'admin/accounts/account/change_form.html' + actions = [disable] + change_view_actions = actions def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ diff --git a/orchestra/apps/accounts/models.py b/orchestra/apps/accounts/models.py index 9f382099..2864619a 100644 --- a/orchestra/apps/accounts/models.py +++ b/orchestra/apps/accounts/models.py @@ -65,6 +65,20 @@ class Account(auth.AbstractBaseUser): if created and hasattr(self, 'systemusers'): self.systemusers.create_user(self.username, account=self, password=self.password, is_main=True) + def disable(self): + self.is_active = False +# self.save(update_fields=['is_active']) + for rel in self._meta.get_all_related_objects(): + if not rel.model in services: + continue + try: + rel.model._meta.get_field_by_name('is_active') + except models.FieldDoesNotExist: + continue + else: + for obj in getattr(self, rel.get_accessor_name()).all(): + obj.save(update_fields=[]) + def send_email(self, template, context, contacts=[], attachments=[], html=None): contacts = self.contacts.filter(email_usages=contacts) email_to = contacts.values_list('email', flat=True) diff --git a/orchestra/apps/databases/tests/functional_tests/tests.py b/orchestra/apps/databases/tests/functional_tests/tests.py index 51326cdd..4885e4f7 100644 --- a/orchestra/apps/databases/tests/functional_tests/tests.py +++ b/orchestra/apps/databases/tests/functional_tests/tests.py @@ -14,7 +14,7 @@ from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, save_re snapshot_on_error) from ... import backends, settings -from ...models import Database +from ...models import Database, DatabaseUser class DatabaseTestMixin(object): @@ -119,6 +119,16 @@ class AdminDatabaseMixin(DatabaseTestMixin): name_field.submit() self.assertNotEqual(url, self.selenium.current_url) + + @snapshot_on_error + def delete(self, dbname): + db = Database.objects.get(name=dbname) + self.admin_delete(db) + + @snapshot_on_error + def delete_user(self, username): + user = DatabaseUser.objects.get(username=username) + self.admin_delete(user) class RESTMysqlDatabaseTest(MySQLBackendMixin, RESTDatabaseMixin, BaseLiveServerTestCase): diff --git a/orchestra/apps/orchestration/middlewares.py b/orchestra/apps/orchestration/middlewares.py index 0f8eaffc..f642b6a1 100644 --- a/orchestra/apps/orchestration/middlewares.py +++ b/orchestra/apps/orchestration/middlewares.py @@ -73,13 +73,16 @@ class OperationsMiddleware(object): else: update_fields = kwargs.get('update_fields', None) if update_fields: - append = False - for field in kwargs.get('update_fields', [None]): - if field not in backend.ignore_fields: - append = True - break - if not append: - continue + # "update_fileds=[]" is a convention for explicitly executing backend + # i.e. account.disable() + if not update_fields == []: + execute = False + for field in update_fields: + if field not in backend.ignore_fields: + execute = True + break + if not execute: + continue instance = copy.copy(instance) pending_operations.add(Operation.create(backend, instance, action)) diff --git a/orchestra/apps/payments/actions.py b/orchestra/apps/payments/actions.py index 258ade55..b29b191c 100644 --- a/orchestra/apps/payments/actions.py +++ b/orchestra/apps/payments/actions.py @@ -27,7 +27,7 @@ def process_transactions(modeladmin, request, queryset): procs = method.process(transactions) processes += procs for trans in transactions: - modeladmin.log_change(request, trans, 'Processed') + modeladmin.log_change(request, trans, _("Processed")) if not processes: return opts = modeladmin.model._meta @@ -46,7 +46,7 @@ def process_transactions(modeladmin, request, queryset): def mark_as_executed(modeladmin, request, queryset, extra_context={}): for trans in queryset: trans.mark_as_executed() - modeladmin.log_change(request, trans, 'Executed') + modeladmin.log_change(request, trans, _("Executed")) msg = _("%s selected transactions have been marked as executed.") % queryset.count() modeladmin.message_user(request, msg) mark_as_executed.url_name = 'execute' @@ -58,7 +58,7 @@ mark_as_executed.verbose_name = _("Mark as executed") def mark_as_secured(modeladmin, request, queryset): for trans in queryset: trans.mark_as_secured() - modeladmin.log_change(request, trans, 'Secured') + modeladmin.log_change(request, trans, _("Secured")) msg = _("%s selected transactions have been marked as secured.") % queryset.count() modeladmin.message_user(request, msg) mark_as_secured.url_name = 'secure' @@ -70,7 +70,7 @@ mark_as_secured.verbose_name = _("Mark as secured") def mark_as_rejected(modeladmin, request, queryset): for trans in queryset: trans.mark_as_rejected() - modeladmin.log_change(request, trans, 'Rejected') + modeladmin.log_change(request, trans, _("Rejected")) msg = _("%s selected transactions have been marked as rejected.") % queryset.count() modeladmin.message_user(request, msg) mark_as_rejected.url_name = 'reject' @@ -105,7 +105,7 @@ _format_commit = partial(_format_display_objects, related=('all', 'secured')) def mark_process_as_executed(modeladmin, request, queryset): for process in queryset: process.mark_as_executed() - modeladmin.log_change(request, process, 'Executed') + modeladmin.log_change(request, process, _("Executed")) msg = _("%s selected processes have been marked as executed.") % queryset.count() modeladmin.message_user(request, msg) mark_process_as_executed.url_name = 'executed' @@ -117,7 +117,7 @@ mark_process_as_executed.verbose_name = _("Mark as executed") def abort(modeladmin, request, queryset): for process in queryset: process.abort() - modeladmin.log_change(request, process, 'Aborted') + modeladmin.log_change(request, process, _("Aborted")) msg = _("%s selected processes have been aborted.") % queryset.count() modeladmin.message_user(request, msg) abort.url_name = 'abort' @@ -129,7 +129,7 @@ abort.verbose_name = _("Abort") def commit(modeladmin, request, queryset): for trans in queryset: trans.mark_as_rejected() - modeladmin.log_change(request, trans, 'Rejected') + modeladmin.log_change(request, trans, _("Rejected")) msg = _("%s selected transactions have been marked as rejected.") % queryset.count() modeladmin.message_user(request, msg) commit.url_name = 'commit' diff --git a/orchestra/apps/systemusers/tests/functional_tests/tests.py b/orchestra/apps/systemusers/tests/functional_tests/tests.py index 1c54292d..5055fdf5 100644 --- a/orchestra/apps/systemusers/tests/functional_tests/tests.py +++ b/orchestra/apps/systemusers/tests/functional_tests/tests.py @@ -348,3 +348,22 @@ class AdminSystemUserTest(AdminSystemUserMixin, BaseLiveServerTestCase): self.account = self.create_account(username=self.account.username, superuser=True) self.selenium.delete_all_cookies() self.admin_login() + + @snapshot_on_error + def test_disable_account(self): + username = '%s_systemuser' % random_ascii(10) + password = '@!?%spppP001' % random_ascii(5) + self.add(username, password) + self.addCleanup(self.delete, username) + self.validate_ftp(username, password) + self.disable(username) + self.validate_user(username) + + disable = reverse('admin:accounts_account_disable', args=(self.account.pk,)) + url = self.live_server_url + disable + self.selenium.get(url) + confirmation = self.selenium.find_element_by_name('post') + confirmation.submit() + self.assertNotEqual(url, self.selenium.current_url) + + self.assertRaises(ftplib.error_perm, self.validate_ftp, username, password)