From eaa5913c8cf05d05b9fc95ca4215952ddc473a8e Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@gmail.com>
Date: Wed, 23 Jun 2021 20:30:26 +1000
Subject: [PATCH] Adds custom DRF metadata handler

- Limit available "actions" data to only what the user is allowed to do
---
 InvenTree/InvenTree/metadata.py | 67 +++++++++++++++++++++++++++++++++
 InvenTree/InvenTree/settings.py |  1 +
 InvenTree/templates/js/forms.js |  8 ++--
 3 files changed, 72 insertions(+), 4 deletions(-)
 create mode 100644 InvenTree/InvenTree/metadata.py

diff --git a/InvenTree/InvenTree/metadata.py b/InvenTree/InvenTree/metadata.py
new file mode 100644
index 0000000000..b9d0732acf
--- /dev/null
+++ b/InvenTree/InvenTree/metadata.py
@@ -0,0 +1,67 @@
+
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from rest_framework.metadata import SimpleMetadata
+
+import users.models
+
+
+class InvenTreeMetadata(SimpleMetadata):
+    """
+    Custom metadata class for the DRF API.
+
+    This custom metadata class imits the available "actions",
+    based on the user's role permissions.
+
+    Thus when a client send an OPTIONS request to an API endpoint,
+    it will only receive a list of actions which it is allowed to perform!
+
+    """
+
+    def determine_metadata(self, request, view):
+        
+        metadata = super().determine_metadata(request, view)
+
+        user = request.user
+
+        if user is None:
+            # No actions for you!
+            metadata['actions'] = {}
+            return metadata
+
+        try:
+            # Extract the model name associated with the view
+            model = view.serializer_class.Meta.model
+
+            # Construct the 'table name' from the model
+            app_label = model._meta.app_label
+            tbl_label = model._meta.model_name
+
+            table = f"{app_label}_{tbl_label}"
+
+            actions = metadata['actions']
+
+            check = users.models.RuleSet.check_table_permission
+
+            # Map the request method to a permission type
+            rolemap = {
+                'GET': 'view',
+                'OPTIONS': 'view',
+                'POST': 'add',
+                'PUT': 'change',
+                'PATCH': 'change',
+                'DELETE': 'delete',
+            }
+
+            # Remove any HTTP methods that the user does not have permission for
+            for method, permission in rolemap.items():
+                if method in actions and not check(user, table, permission):
+                    del actions[method]
+
+        except AttributeError:
+            # We will assume that if the serializer class does *not* have a Meta
+            # then we don't need a permission
+            pass
+
+        return metadata
diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index fceaf9a58f..be13411fd8 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -341,6 +341,7 @@ REST_FRAMEWORK = {
         'InvenTree.permissions.RolePermission',
     ),
     'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
+    'DEFAULT_METADATA_CLASS': 'InvenTree.metadata.InvenTreeMetadata'
 }
 
 WSGI_APPLICATION = 'InvenTree.wsgi.application'
diff --git a/InvenTree/templates/js/forms.js b/InvenTree/templates/js/forms.js
index fa2ca9eb2e..822688d9ad 100644
--- a/InvenTree/templates/js/forms.js
+++ b/InvenTree/templates/js/forms.js
@@ -23,7 +23,8 @@
  */
 function getApiEndpointOptions(url, options={}) {
 
-    $.ajax({
+    // Return the ajax request object
+    return $.ajax({
         url: url,
         type: 'OPTIONS',
         contentType: 'application/json',
@@ -31,8 +32,7 @@ function getApiEndpointOptions(url, options={}) {
         accepts: {
             json: 'application/json',
         },
-        success: function(response) {
-            console.log(response);
-        }
     });
 }
+
+