From 71d1e6e3c9ba82e8f2f5a3a98cf75829c0398676 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 28 Apr 2019 23:13:54 +0200 Subject: [PATCH 01/11] Add delete method for UserHostingKey --- hosting/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index d2011654..616b036f 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -212,6 +212,12 @@ class UserHostingKey(models.Model): # self.save(update_fields=['public_key']) return private_key, public_key + def delete(self,*args,**kwargs): + if os.path.isfile(self.private_key.path): + os.remove(self.private_key.path) + + super(UserHostingKey, self).delete(*args,**kwargs) + class HostingBill(AssignPermissionsMixin, models.Model): customer = models.ForeignKey(StripeCustomer) From c8bd3f97c652654454a351b8b2dfb74a9755ef96 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 28 Apr 2019 23:14:14 +0200 Subject: [PATCH 02/11] Add deleteuser management command --- .../management/commands/deteteuser.py | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 datacenterlight/management/commands/deteteuser.py diff --git a/datacenterlight/management/commands/deteteuser.py b/datacenterlight/management/commands/deteteuser.py new file mode 100644 index 00000000..efed7e17 --- /dev/null +++ b/datacenterlight/management/commands/deteteuser.py @@ -0,0 +1,128 @@ +import logging +import sys +import stripe + +from django.core.management.base import BaseCommand +from membership.models import CustomUser +from hosting.models import ( + HostingOrder, HostingBill, VMDetail, UserCardDetail, UserHostingKey +) +logger = logging.getLogger(__name__) + + +def query_yes_no(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is True for "yes" or False for "no". + """ + valid = {"yes": True, "y": True, "ye": True, + "no": False, "n": False} + if default is None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + sys.stdout.write(question + prompt) + choice = input().lower() + if default is not None and choice == '': + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' " + "(or 'y' or 'n').\n") + + +class Command(BaseCommand): + help = '''Deletes all resources of the user from the project''' + + def add_arguments(self, parser): + parser.add_argument('customer_email', nargs='+', type=str) + + def handle(self, *args, **options): + try: + for email in options['customer_email']: + r = query_yes_no("Are you sure you want to delete {} ?".format( + email, None + )) + if r: + logger.debug("Deleting user {}".format(email)) + # Get stripe customer instance and delete the customer + try: + cus_user = CustomUser.objects.get(email=email) + except CustomUser.DoesNotExist as dne: + logger.error("CustomUser with email {} does " + "not exist".format(email)) + sys.exit(1) + stripe_customer = cus_user.stripecustomer + del_response = stripe.Customer.delete( + stripe_customer.stripe_id + ) + if del_response.deleted: + logger.debug( + "StripeCustomer {} associated with {} deleted" + "".format(stripe_customer.stripe_id, email) + ) + else: + logger.error("Error while deleting the StripeCustomer") + + hosting_orders = HostingOrder.objects.filter( + customer=stripe_customer.id + ) + + vm_ids = [] + for order in hosting_orders: + vm_ids.append(order.vm_id) + # Delete Billing Address + order.billing_address.delete() + + # Delete Order Detail + order.order_detail.delete() + + # Delete order + order.delete() + + hosting_bills = HostingBill.objects.filter( + customer=stripe_customer.id + ) + + # delete hosting bills + for bill in hosting_bills: + bill.billing_address.delete() + bill.delete() + + # delete VMDetail + for vm_id in vm_ids: + VMDetail.objects.get(vm_id=vm_id) + + # delete UserCardDetail + ucd = UserCardDetail.objects.filter( + customer=stripe_customer.id + ) + ucd.delete() + + # delete UserHostingKey + uhks = UserHostingKey.objects.filter( + user=cus_user.id + ) + for uhk in uhks: + uhk.delete() + + # delete stripe customer + stripe_customer.delete() + + # delete CustomUesr + cus_user.delete() + logger.debug("Deleted {} SUCCESSFULLY.".format(email)) + except Exception as e: + print(" *** Error occurred. Details {}".format(str(e))) From 591614ade5071258e3b023d8e508135d7a4adcb9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 28 Apr 2019 23:57:39 +0200 Subject: [PATCH 03/11] Remove user from opennebula also --- datacenterlight/management/commands/deteteuser.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/datacenterlight/management/commands/deteteuser.py b/datacenterlight/management/commands/deteteuser.py index efed7e17..e508304b 100644 --- a/datacenterlight/management/commands/deteteuser.py +++ b/datacenterlight/management/commands/deteteuser.py @@ -1,4 +1,5 @@ import logging +import oca import sys import stripe @@ -7,6 +8,7 @@ from membership.models import CustomUser from hosting.models import ( HostingOrder, HostingBill, VMDetail, UserCardDetail, UserHostingKey ) +from opennebula_api.models import OpenNebulaManager logger = logging.getLogger(__name__) @@ -123,6 +125,13 @@ class Command(BaseCommand): # delete CustomUesr cus_user.delete() + + # remove user from OpenNebula + manager = OpenNebulaManager() + manager.oneadmin_client.call( + oca.User.METHODS['delete'], email + ) + logger.debug("Deleted {} SUCCESSFULLY.".format(email)) except Exception as e: print(" *** Error occurred. Details {}".format(str(e))) From a67284a89d9554a819779ca413f09303ee0f2e2f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 29 Apr 2019 00:02:45 +0200 Subject: [PATCH 04/11] Rename management command deteteuser -> deleteuser --- .../management/commands/{deteteuser.py => deleteuser.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename datacenterlight/management/commands/{deteteuser.py => deleteuser.py} (100%) diff --git a/datacenterlight/management/commands/deteteuser.py b/datacenterlight/management/commands/deleteuser.py similarity index 100% rename from datacenterlight/management/commands/deteteuser.py rename to datacenterlight/management/commands/deleteuser.py From 0352096fa75dea71b524386fbe88b91db4338a89 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 29 Apr 2019 00:17:59 +0200 Subject: [PATCH 05/11] Call delete on Customer object --- datacenterlight/management/commands/deleteuser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py index e508304b..4ba0c986 100644 --- a/datacenterlight/management/commands/deleteuser.py +++ b/datacenterlight/management/commands/deleteuser.py @@ -67,10 +67,10 @@ class Command(BaseCommand): "not exist".format(email)) sys.exit(1) stripe_customer = cus_user.stripecustomer - del_response = stripe.Customer.delete( + c = stripe.Customer.retrieve( stripe_customer.stripe_id ) - if del_response.deleted: + if c.delete(): logger.debug( "StripeCustomer {} associated with {} deleted" "".format(stripe_customer.stripe_id, email) From 51100fd6273999eba1c1c9e67af7ed758754d228 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 29 Apr 2019 00:53:06 +0200 Subject: [PATCH 06/11] Add missing code --- .../management/commands/deleteuser.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py index 4ba0c986..27e63c3d 100644 --- a/datacenterlight/management/commands/deleteuser.py +++ b/datacenterlight/management/commands/deleteuser.py @@ -70,7 +70,8 @@ class Command(BaseCommand): c = stripe.Customer.retrieve( stripe_customer.stripe_id ) - if c.delete(): + cus_delete_obj = c.delete() + if cus_delete_obj.deleted: logger.debug( "StripeCustomer {} associated with {} deleted" "".format(stripe_customer.stripe_id, email) @@ -87,10 +88,8 @@ class Command(BaseCommand): vm_ids.append(order.vm_id) # Delete Billing Address order.billing_address.delete() - # Delete Order Detail order.order_detail.delete() - # Delete order order.delete() @@ -105,17 +104,19 @@ class Command(BaseCommand): # delete VMDetail for vm_id in vm_ids: - VMDetail.objects.get(vm_id=vm_id) + vm_detail = VMDetail.objects.get(vm_id=vm_id) + vm_detail.delete() # delete UserCardDetail - ucd = UserCardDetail.objects.filter( - customer=stripe_customer.id + ucds = UserCardDetail.objects.filter( + stripe_customer=stripe_customer ) - ucd.delete() + for ucd in ucds: + ucd.delete() # delete UserHostingKey uhks = UserHostingKey.objects.filter( - user=cus_user.id + user=cus_user ) for uhk in uhks: uhk.delete() From 2b118ff5409aa9cdd0be7ea8bd4f3cce419934af Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 5 May 2019 16:56:56 +0200 Subject: [PATCH 07/11] Correct spelling --- datacenterlight/management/commands/deleteuser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py index 27e63c3d..a1ab1e94 100644 --- a/datacenterlight/management/commands/deleteuser.py +++ b/datacenterlight/management/commands/deleteuser.py @@ -124,7 +124,7 @@ class Command(BaseCommand): # delete stripe customer stripe_customer.delete() - # delete CustomUesr + # delete CustomUser cus_user.delete() # remove user from OpenNebula From ba88bbf6bd77671902242a2f0fd0c7af487b5b01 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 6 May 2019 08:04:57 +0200 Subject: [PATCH 08/11] Add migration to change user_id to customuser_id in legacy code Pertains to djangocms_blog --- ...ange_user_id_to_customer_id_in_djangocms_blog.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 membership/migrations/0008_change_user_id_to_customer_id_in_djangocms_blog.py diff --git a/membership/migrations/0008_change_user_id_to_customer_id_in_djangocms_blog.py b/membership/migrations/0008_change_user_id_to_customer_id_in_djangocms_blog.py new file mode 100644 index 00000000..4a8530e9 --- /dev/null +++ b/membership/migrations/0008_change_user_id_to_customer_id_in_djangocms_blog.py @@ -0,0 +1,13 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [('membership', '0007_auto_20180213_0128'), + ('djangocms_blog', '0032_auto_20180109_0023'), + ] + + operations = [ + migrations.RunSQL( + "ALTER TABLE djangocms_blog_authorentriesplugin_authors " + "RENAME COLUMN user_id TO customuser_id;"), + ] From c40331fcc1653d1f28089f3cffb9ed1f6c3e1438 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 6 May 2019 08:07:26 +0200 Subject: [PATCH 09/11] Add deleteduser model --- membership/migrations/0009_deleteduser.py | 25 +++++++++++++++++++++++ membership/models.py | 9 ++++++++ 2 files changed, 34 insertions(+) create mode 100644 membership/migrations/0009_deleteduser.py diff --git a/membership/migrations/0009_deleteduser.py b/membership/migrations/0009_deleteduser.py new file mode 100644 index 00000000..146d3847 --- /dev/null +++ b/membership/migrations/0009_deleteduser.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-05-06 06:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('membership', '0008_change_user_id_to_customer_id_in_djangocms_blog'), + ] + + operations = [ + migrations.CreateModel( + name='DeletedUser', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_id', models.PositiveIntegerField()), + ('name', models.CharField(max_length=254)), + ('email', models.EmailField(max_length=254, unique=True)), + ('deleted_at', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/membership/models.py b/membership/models.py index c5e83735..1a622bd5 100644 --- a/membership/models.py +++ b/membership/models.py @@ -265,6 +265,15 @@ class CreditCards(models.Model): pass +class DeletedUser(models.Model): + user_id = models.PositiveIntegerField() + + # why 254 ? => to be consistent with legacy code + name = models.CharField(max_length=254) + email = models.EmailField(unique=True, max_length=254) + deleted_at = models.DateTimeField(auto_now_add=True) + + class Calendar(models.Model): datebooked = models.DateField() user = models.ForeignKey(CustomUser) From 0f777e66d83ac6f9d7f87547f970958c832409a9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 6 May 2019 08:08:51 +0200 Subject: [PATCH 10/11] Add entry to DeletedUser + fix code to delete user from opennebula --- .../management/commands/deleteuser.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py index a1ab1e94..e3be76f1 100644 --- a/datacenterlight/management/commands/deleteuser.py +++ b/datacenterlight/management/commands/deleteuser.py @@ -4,7 +4,7 @@ import sys import stripe from django.core.management.base import BaseCommand -from membership.models import CustomUser +from membership.models import CustomUser, DeletedUser from hosting.models import ( HostingOrder, HostingBill, VMDetail, UserCardDetail, UserHostingKey ) @@ -124,14 +124,32 @@ class Command(BaseCommand): # delete stripe customer stripe_customer.delete() + # add user to deleteduser + DeletedUser.objects.create( + email=cus_user.email, name=cus_user.name, + user_id = cus_user.id + ) + # delete CustomUser cus_user.delete() # remove user from OpenNebula manager = OpenNebulaManager() - manager.oneadmin_client.call( - oca.User.METHODS['delete'], email - ) + user_pool = manager._get_user_pool() + on_user = user_pool.get_by_name(email) + if on_user.id > 0: + logger.debug( + "Deleting user {} => ID={} from opennebula".format( + email, on_user.id) + ) + manager.oneadmin_client.call( + oca.User.METHODS['delete'], on_user.id + ) + else: + logger.error( + "User not found with email {}. " + "Not doing anything".format(email) + ) logger.debug("Deleted {} SUCCESSFULLY.".format(email)) except Exception as e: From 3f01145cd1f890360506c21d5737710ccc90bbdb Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 6 May 2019 08:30:50 +0200 Subject: [PATCH 11/11] Add additional None checks --- .../management/commands/deleteuser.py | 74 +++++++++++++++++-- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/datacenterlight/management/commands/deleteuser.py b/datacenterlight/management/commands/deleteuser.py index e3be76f1..cc1cd477 100644 --- a/datacenterlight/management/commands/deleteuser.py +++ b/datacenterlight/management/commands/deleteuser.py @@ -86,12 +86,39 @@ class Command(BaseCommand): vm_ids = [] for order in hosting_orders: vm_ids.append(order.vm_id) + # Delete Billing Address - order.billing_address.delete() + if order.billing_address is not None: + order.billing_address.delete() + logger.debug( + "Billing Address {} associated with {} deleted" + "".format(order.billing_address.id, email) + ) + else: + logger.error( + "Error while deleting the billing_address") + # Delete Order Detail - order.order_detail.delete() + if order.order_detail is not None: + order.order_detail.delete() + logger.debug( + "Order Detail {} associated with {} deleted" + "".format(order.order_detail.id, email) + ) + else: + logger.error( + "Error while deleting the order_detail. None") + # Delete order - order.delete() + if order is not None: + order.delete() + logger.debug( + "Order {} associated with {} deleted" + "".format(order.id, email) + ) + else: + logger.error( + "Error while deleting the Order") hosting_bills = HostingBill.objects.filter( customer=stripe_customer.id @@ -99,20 +126,53 @@ class Command(BaseCommand): # delete hosting bills for bill in hosting_bills: - bill.billing_address.delete() - bill.delete() + if bill.billing_address is not None: + bill.billing_address.delete() + logger.debug( + "HostingBills billing address {} associated with {} deleted" + "".format(bill.billing_address.id, email) + ) + else: + logger.error( + "Error while deleting the HostingBill's Billing address") + + if bill is not None: + bill.delete() + logger.debug( + "HostingBill {} associated with {} deleted" + "".format(bill.id, email) + ) + else: + logger.error( + "Error while deleting the HostingBill") # delete VMDetail for vm_id in vm_ids: vm_detail = VMDetail.objects.get(vm_id=vm_id) - vm_detail.delete() + if vm_detail is not None: + vm_detail.delete() + logger.debug( + "vm_detail {} associated with {} deleted" + "".format(vm_detail.id, email) + ) + else: + logger.error( + "Error while deleting the vm_detail") # delete UserCardDetail ucds = UserCardDetail.objects.filter( stripe_customer=stripe_customer ) for ucd in ucds: - ucd.delete() + if ucd is not None: + ucd.delete() + logger.debug( + "User Card Detail {} associated with {} deleted" + "".format(ucd.id, email) + ) + else: + logger.error( + "Error while deleting the User Card Detail") # delete UserHostingKey uhks = UserHostingKey.objects.filter(