From ec447e0dc4a28e29de11f269c1e42bf39d7ed11a Mon Sep 17 00:00:00 2001
From: Nico Schottelius <nico@nico-notebook.schottelius.org>
Date: Sun, 10 May 2020 21:47:44 +0200
Subject: [PATCH] Add support for primary address in user.

Closes #35

Fixes #35
---
 uncloud/urls.py                               |  1 -
 .../0004_user_primary_billing_address.py      | 20 +++++++++++++++
 .../migrations/0005_auto_20200510_1736.py     | 20 +++++++++++++++
 uncloud_auth/models.py                        |  7 +++++-
 uncloud_auth/serializers.py                   | 19 ++++++++++----
 uncloud_auth/views.py                         | 25 ++++++++++++++-----
 .../migrations/0014_paymentsettings.py        | 25 +++++++++++++++++++
 uncloud_pay/serializers.py                    |  2 ++
 8 files changed, 106 insertions(+), 13 deletions(-)
 create mode 100644 uncloud_auth/migrations/0004_user_primary_billing_address.py
 create mode 100644 uncloud_auth/migrations/0005_auto_20200510_1736.py
 create mode 100644 uncloud_pay/migrations/0014_paymentsettings.py

diff --git a/uncloud/urls.py b/uncloud/urls.py
index b20f136..723ef45 100644
--- a/uncloud/urls.py
+++ b/uncloud/urls.py
@@ -63,7 +63,6 @@ router.register(r'v1/my/order', payviews.OrderViewSet, basename='order')
 router.register(r'v1/my/payment', payviews.PaymentViewSet, basename='payment')
 router.register(r'v1/my/payment-method', payviews.PaymentMethodViewSet, basename='payment-method')
 
-
 # admin/staff urls
 router.register(r'v1/admin/bill', payviews.AdminBillViewSet, basename='admin/bill')
 router.register(r'v1/admin/payment', payviews.AdminPaymentViewSet, basename='admin/payment')
diff --git a/uncloud_auth/migrations/0004_user_primary_billing_address.py b/uncloud_auth/migrations/0004_user_primary_billing_address.py
new file mode 100644
index 0000000..640c9c5
--- /dev/null
+++ b/uncloud_auth/migrations/0004_user_primary_billing_address.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.0.6 on 2020-05-10 17:31
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('uncloud_pay', '0014_paymentsettings'),
+        ('uncloud_auth', '0003_auto_20200318_1345'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='user',
+            name='primary_billing_address',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='uncloud_pay.BillingAddress'),
+        ),
+    ]
diff --git a/uncloud_auth/migrations/0005_auto_20200510_1736.py b/uncloud_auth/migrations/0005_auto_20200510_1736.py
new file mode 100644
index 0000000..38c303e
--- /dev/null
+++ b/uncloud_auth/migrations/0005_auto_20200510_1736.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.0.6 on 2020-05-10 17:36
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('uncloud_pay', '0014_paymentsettings'),
+        ('uncloud_auth', '0004_user_primary_billing_address'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='user',
+            name='primary_billing_address',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='uncloud_pay.BillingAddress'),
+        ),
+    ]
diff --git a/uncloud_auth/models.py b/uncloud_auth/models.py
index 4549a2a..c456648 100644
--- a/uncloud_auth/models.py
+++ b/uncloud_auth/models.py
@@ -3,7 +3,6 @@ from django.db import models
 from django.core.validators import MinValueValidator
 
 from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
-
 from uncloud_pay.models import get_balance_for_user
 
 class User(AbstractUser):
@@ -18,6 +17,12 @@ class User(AbstractUser):
             decimal_places=AMOUNT_DECIMALS,
             validators=[MinValueValidator(0)])
 
+    # Need to use the string here to prevent a circular import
+    primary_billing_address = models.ForeignKey('uncloud_pay.BillingAddress',
+                                                   on_delete=models.PROTECT,
+                                                   blank=True,
+                                                   null=True)
+
     @property
     def balance(self):
         return get_balance_for_user(self)
diff --git a/uncloud_auth/serializers.py b/uncloud_auth/serializers.py
index e01aab3..92bbf01 100644
--- a/uncloud_auth/serializers.py
+++ b/uncloud_auth/serializers.py
@@ -2,15 +2,24 @@ from django.contrib.auth import get_user_model
 from rest_framework import serializers
 
 from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
+from uncloud_pay.models import BillingAddress
 
 class UserSerializer(serializers.ModelSerializer):
-
-    balance = serializers.DecimalField(max_digits=AMOUNT_MAX_DIGITS,
-                                       decimal_places=AMOUNT_DECIMALS)
-
     class Meta:
         model = get_user_model()
-        fields = ['username', 'email', 'balance', 'maximum_credit' ]
+        read_only_fields = [ 'username', 'balance', 'maximum_credit' ]
+        fields =  read_only_fields + [ 'email', 'primary_billing_address' ]
+
+    def validate(self, data):
+        """
+        Ensure that the primary billing address belongs to the user
+        """
+
+        if 'primary_billing_address' in data:
+            if not data['primary_billing_address'].owner == self.instance:
+                raise serializers.ValidationError("Invalid data")
+
+        return data
 
 class ImportUserSerializer(serializers.Serializer):
     username = serializers.CharField()
diff --git a/uncloud_auth/views.py b/uncloud_auth/views.py
index 9c5bd1f..77f0a0f 100644
--- a/uncloud_auth/views.py
+++ b/uncloud_auth/views.py
@@ -3,24 +3,37 @@ from .serializers import *
 from django_auth_ldap.backend import LDAPBackend
 from rest_framework.decorators import action
 from rest_framework.response import Response
-from rest_framework import mixins
 
-class UserViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
+class UserViewSet(viewsets.GenericViewSet):
     permission_classes = [permissions.IsAuthenticated]
     serializer_class = UserSerializer
 
+    def get_queryset(self):
+        return self.request.user
+
     def list(self, request, format=None):
         # This is a bit stupid: we have a user, we create a queryset by
         # matching on the username. But I don't know a "nicer" way.
         # Nico, 2020-03-18
-        user = get_user_model().objects.get(
-                username=self.request.user.username)
+        user = request.user
         serializer = self.get_serializer(user, context = {'request': request})
         return Response(serializer.data)
 
+    def create(self, request):
+        """
+        Modify existing user data
+        """
+
+        user = request.user
+        serializer = self.get_serializer(user,
+                                         context = {'request': request},
+                                         data=request.data)
+        serializer.is_valid(raise_exception=True)
+        serializer.save()
+        return Response(serializer.data)
+
 class AdminUserViewSet(viewsets.ReadOnlyModelViewSet):
-    # FIXME: make this admin
-    permission_classes = [permissions.IsAuthenticated]
+    permission_classes = [permissions.IsAdminUser]
 
     def get_serializer_class(self):
         if self.action == 'import_from_ldap':
diff --git a/uncloud_pay/migrations/0014_paymentsettings.py b/uncloud_pay/migrations/0014_paymentsettings.py
new file mode 100644
index 0000000..2a4f9a0
--- /dev/null
+++ b/uncloud_pay/migrations/0014_paymentsettings.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.0.6 on 2020-05-10 13:53
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('uncloud_pay', '0013_auto_20200508_1446'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PaymentSettings',
+            fields=[
+                ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+                ('owner', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+                ('primary_billing_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.BillingAddress')),
+            ],
+        ),
+    ]
diff --git a/uncloud_pay/serializers.py b/uncloud_pay/serializers.py
index edccd07..5ee5ad5 100644
--- a/uncloud_pay/serializers.py
+++ b/uncloud_pay/serializers.py
@@ -1,6 +1,8 @@
 from django.contrib.auth import get_user_model
 from rest_framework import serializers
 from uncloud_auth.serializers import UserSerializer
+from django.utils.translation import gettext_lazy as _
+
 from .models import *
 
 ###