From 3a93095437ba8b433f9b108234fc2879d8d763f5 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 19 Jun 2021 12:22:08 +0200
Subject: [PATCH] Clean code

---
 billing/models/payment_methods.py |   1 +
 billing/views/note_kfet.py        | 105 ++++++++++++++++++++----------
 constellation/settings.py         |   1 +
 3 files changed, 73 insertions(+), 34 deletions(-)

diff --git a/billing/models/payment_methods.py b/billing/models/payment_methods.py
index 7394ce6..56450e4 100644
--- a/billing/models/payment_methods.py
+++ b/billing/models/payment_methods.py
@@ -57,6 +57,7 @@ class NoteKfetPaymentMethod(PaymentMethod):
     def get_payment_url(self, invoice, request=None):
         scheme = request.scheme if request else "https"
         host = request.get_host() if request else Site.objects.first().domain
+        # Go to the authorization link of the Note Kfet
         return settings.NOTE_KFET_URL + "o/authorize/?client_id=" + settings.NOTE_KFET_CLIENT_ID \
             + "&response_type=code&scope=" + settings.NOTE_KFET_SCOPES + "&state=" + str(invoice.pk) \
             + f"&redirect_uri={scheme}://{host}{reverse_lazy('billing:note_auth')}"
diff --git a/billing/views/note_kfet.py b/billing/views/note_kfet.py
index 0026976..b214565 100644
--- a/billing/views/note_kfet.py
+++ b/billing/views/note_kfet.py
@@ -1,6 +1,7 @@
 import requests
 from django.conf import settings
 from django.contrib import messages
+from django.contrib.auth.mixins import LoginRequiredMixin
 from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
 from django.views.generic import RedirectView
@@ -8,23 +9,41 @@ from django.views.generic import RedirectView
 from ..models import Invoice
 
 
-class NoteKfetAuthorizeView(RedirectView):
+class NoteKfetAuthorizeView(LoginRequiredMixin, RedirectView):
+    """
+    This view is called after an authorization from the Note Kfet.
+    Constellation requests an authorization to create a transaction.
+    When the user gave the authorization, it is redirected here.
+
+    With the grant token, we get an access token, then we can query
+    the API to create the transaction.
+    """
+
     url = reverse_lazy('index')
 
     def get(self, request, *args, **kwargs):
+        if 'error' in request.GET:
+            messages.error(request, _("Error:") + " " + request.GET['error'])
+            return super().get(request, *args, **kwargs)
+
         if 'code' not in request.GET or 'state' not in request.GET:
             messages.error(request, _("Bad request"))
             return super().get(request, *args, **kwargs)
 
         invoice_id = request.GET['state']
+        qs = Invoice.objects.filter(pk=invoice_id)
+        # Don't validate invoices from other users
+        if not request.user.has_perm('billing.change_invoice'):
+            qs = qs.filter(target=request.user)
         if not Invoice.objects.filter(pk=invoice_id).exists():
             messages.error(request, _("Bad request"))
             return super().get(request, *args, **kwargs)
 
-        invoice = Invoice.objects.get(pk=invoice_id)
+        invoice = qs.get()
 
         code = request.GET['code']
 
+        # Generate access token
         response = requests.post(settings.NOTE_KFET_URL + "o/token/", data={
             'client_id': settings.NOTE_KFET_CLIENT_ID,
             'client_secret': settings.NOTE_KFET_CLIENT_SECRET,
@@ -33,38 +52,56 @@ class NoteKfetAuthorizeView(RedirectView):
         })
         resp_json = response.json()
 
-        if 'error' not in resp_json:
-            resp_json = response.json()
-            access_token = resp_json['access_token']
-
-            note = requests.get(settings.NOTE_KFET_URL + "api/note/note/",
-                                headers={"Authorization": f"Bearer {access_token}"}).json()['results'][0]
-
-            response = requests.post(
-                settings.NOTE_KFET_URL + "api/note/transaction/transaction/",
-                json={
-                    'source_alias': note['name'],
-                    'destination_alias': 'Crans',
-                    'quantity': 1,
-                    'amount': invoice.price,
-                    'reason': f'Facture n°{invoice.id}',
-                    'valid': True,
-                    'polymorphic_ctype': 26,
-                    'source': note['id'],
-                    'destination': 2088,
-                    'resourcetype': 'Transaction',
-
-                },
-                headers={"Authorization": f"Bearer {access_token}"},
-            )
-
-            if response.status_code == 201:
-                messages.success(request, _("Payment succeed!"))
-                invoice.valid = True
-                invoice.save()
-            else:
-                messages.error(request, _("Error:") + " " + str(response.json()))
-        else:
+        if 'error' in resp_json:
             messages.error(request, _("Error:") + " " + str(resp_json))
+            return super().get(request, *args, **kwargs)
+
+        resp_json = response.json()
+        access_token = resp_json['access_token']
+
+        # Get user note id
+        response = requests.get(settings.NOTE_KFET_URL + "api/note/note/",
+                                headers={"Authorization": f"Bearer {access_token}"})
+
+        if response.status_code != 200:
+            messages.error(request, _("Error:") + " " + str(response.json()))
+            return super().get(request, *args, **kwargs)
+
+        note = response.json()['results'][0]
+
+        # Get Transaction polymorphic ctype
+        response = requests.get(settings.NOTE_KFET_URL + "api/models/?app_label=note&model=transaction",
+                                headers={"Authorization": f"Bearer {access_token}"})
+        if response.status_code != 200:
+            messages.error(request, _("Error:") + " " + str(response.json()))
+            return super().get(request, *args, **kwargs)
+        polymorphic_ctype_id = response.json()['results'][0]['id']
+
+        # Create transaction
+        response = requests.post(
+            settings.NOTE_KFET_URL + "api/note/transaction/transaction/",
+            json={
+                'source_alias': note['name'],
+                'destination_alias': 'Crans',
+                'quantity': 1,
+                'amount': invoice.price,
+                'reason': f'Facture n°{invoice.id}',
+                'valid': True,
+                'polymorphic_ctype': polymorphic_ctype_id,
+                'source': note['id'],
+                'destination': settings.NOTE_KFET_CLUB_NOTE_ID,
+                'resourcetype': 'Transaction',
+
+            },
+            headers={"Authorization": f"Bearer {access_token}"},
+        )
+
+        if response.status_code == 201:
+            messages.success(request, _("Payment succeed!"))
+            # Validate invoice
+            invoice.valid = True
+            invoice.save()
+        else:
+            messages.error(request, _("Error:") + " " + str(response.json()))
 
         return super().get(request, *args, **kwargs)
diff --git a/constellation/settings.py b/constellation/settings.py
index 4f31b9c..04087fa 100644
--- a/constellation/settings.py
+++ b/constellation/settings.py
@@ -251,6 +251,7 @@ NOTE_KFET_URL = "https://note.crans.org/"
 NOTE_KFET_CLIENT_ID = "CHANGEME"
 NOTE_KFET_CLIENT_SECRET = "CHANGEME"
 NOTE_KFET_SCOPES = "3_1 17_1"  # View own note, create transaction from our own note
+NOTE_KFET_CLUB_NOTE_ID = 2088  # WARNING: this is the note ID, not the club ID.
 
 try:
     from .settings_local import *  # noqa: F401, F403&
-- 
GitLab