Repository: koladev32/drf-simple-apikey (language: python)

Issue:
Add a parameter for retrieving the entity attribute from the request instead of `request.user`
For the moment, we are using `request.user` to retrieve the entity attached to the API key. For a better developer experience, we are planning to retrieve the entity from `request.entity` instead of `request.user`. 
The catch here is that we should also allow the user to set the attribute on the request object. Meaning that we would do something like:

```python
entity = getattr(request, settings.SIMPLE_API_KEY["REQUEST_ENTITY_ATTRIBUTE"], None)
```

To discuss surely.


Discussion / hints:
cc @Ederene20 

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4cfef7..b7f8d7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@ Changelog
 
 [Unreleased]
 ------------
+- Add a parameter for retrieving the entity attribute from the request instead of request.user #20
 
 [v0.1.0] - 2023-02-06
 ------------------
diff --git a/README.md b/README.md
index 2688c55..fd33916 100644
--- a/README.md
+++ b/README.md
@@ -96,6 +96,36 @@ or
 django-admin generate_fernet_key
 ```
 
+## Custom attribute on the request object
+
+By default, when using Django, you can access the entity associated with an API key using `request.user`. However, 
+we understand that this may not always be convenient for your use case. Therefore, we have provided you with the 
+flexibility to choose your own custom attribute name.
+
+1 - Add the `rest_framework_simple_api_key.middleware.ApiKeyAuthenticationMiddleware` middleware in the `MIDDLEWARE` 
+list in your Django settings.
+
+```python
+MIDDLEWARE = (
+    # ...
+    "rest_framework_simple_api_key.middleware.ApiKeyAuthenticationMiddleware"
+)
+```
+
+2 - Define a value for the `custom_entity` key in the `SIMPLE_API_KEY` dictionnary.
+
+```python
+SIMPLE_API_KEY={
+    # ...
+    "CUSTOM_ENTITY_NAME": "organization"
+}
+```
+
+That's it! With these changes in place, you can retrieve your entity using your custom attribute name on every 
+authenticated API request. For example, if you set `CUSTOM_ENTITY_NAME` to `organization`, you can access the entity with 
+`request.organization`. If you don't define a `CUSTOM_ENTITY_NAME`, you can still access the entity using the default 
+attribute name `request.entity`.
+
 ## Changelog
 
 See [CHANGELOG.md](https://github.com/koladev32/djangorestframework-simple-apikey/blob/main/CHANGELOG.md).
diff --git a/docs/settings.rst b/docs/settings.rst
index d2cf80c..5c3241b 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -14,6 +14,7 @@ Some of Django REST Framework Simple API Key's behavior can be customized throug
        "API_KEY_LIFETIME": 365,
        "AUTHENTICATION_MODEL": settings.AUTH_USER_MODEL,
        "AUTHENTICATION_KEYWORD_HEADER": "Api-Key",
+       "CUSTOM_ENTITY_NAME": "entity"
   }
 
 
@@ -46,3 +47,9 @@ By default, it points to the ``settings.AUTH_USER_MODEL`` settings which in Djan
 Determines the keyword that should come with every request made to your API. The default value is ``Api-Key`` and it is used in the following format:
 
  Api-Key API_KEY
+
+``CUSTOM_ENTITY_NAME``
+----------------------------
+
+Gives you the flexibility to define a custom attribute name that you can use to retrieve the entity attached to each
+valid API Key on the **request** object.
diff --git a/rest_framework_simple_api_key/middleware.py b/rest_framework_simple_api_key/middleware.py
new file mode 100644
index 0000000..bb0189b
--- /dev/null
+++ b/rest_framework_simple_api_key/middleware.py
@@ -0,0 +1,14 @@
+from django.conf import settings
+from django.utils.deprecation import MiddlewareMixin
+
+from rest_framework_simple_api_key.backends import APIKeyAuthentication
+
+
+class ApiKeyAuthenticationMiddleware(MiddlewareMixin):
+    def process_request(self, request):
+        api_key_authentication = APIKeyAuthentication()
+        entity, _ = api_key_authentication.authenticate(request)
+        custom_entity_name = settings.SIMPLE_API_KEY["CUSTOM_ENTITY_NAME"]
+
+        if not hasattr(request, custom_entity_name):
+            setattr(request, custom_entity_name, entity)
diff --git a/rest_framework_simple_api_key/settings.py b/rest_framework_simple_api_key/settings.py
index 96a761a..de3a6e3 100644
--- a/rest_framework_simple_api_key/settings.py
+++ b/rest_framework_simple_api_key/settings.py
@@ -11,6 +11,7 @@
     "API_KEY_LIFETIME": 365,
     "AUTHENTICATION_MODEL": settings.AUTH_USER_MODEL,
     "AUTHENTICATION_KEYWORD_HEADER": "Api-Key",
+    "CUSTOM_ENTITY_NAME": "entity"
 }
 
 REMOVED_SETTINGS = ()


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 45e7907..d1227e9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -6,6 +6,7 @@ def pytest_configure():
         "django.contrib.sessions.middleware.SessionMiddleware",
         "django.contrib.auth.middleware.AuthenticationMiddleware",
         "django.contrib.messages.middleware.MessageMiddleware",
+        "rest_framework_simple_api_key.middleware.ApiKeyAuthenticationMiddleware",
     )
 
     settings.configure(
@@ -39,7 +40,8 @@ def pytest_configure():
         ),
         PASSWORD_HASHERS=("django.contrib.auth.hashers.MD5PasswordHasher",),
         SIMPLE_API_KEY={
-            "FERNET_SECRET": "sVjomf7FFy351xRxDeJWFJAZaE2tG3MTuUv92TLFfOA="
+            "FERNET_SECRET": "sVjomf7FFy351xRxDeJWFJAZaE2tG3MTuUv92TLFfOA=",
+            "CUSTOM_ENTITY_NAME": "organization"
         },
     )
 
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
new file mode 100644
index 0000000..8439b1f
--- /dev/null
+++ b/tests/test_middleware.py
@@ -0,0 +1,29 @@
+import pytest
+
+from django.urls import reverse
+from django.contrib.auth.models import User
+
+from rest_framework.test import APIClient
+from rest_framework_simple_api_key.settings import package_settings
+
+from .fixtures.api_key import active_api_key
+from .fixtures.user import user
+
+
+@pytest.mark.django_db
+class TestApiKeyMiddleware:
+
+    def test_custom_request_attribute(self, active_api_key):
+        client = APIClient()
+        _, api_key = active_api_key
+
+        client.credentials(HTTP_AUTHORIZATION=f"{package_settings.AUTHENTICATION_KEYWORD_HEADER} {api_key}")
+
+        response = client.get(reverse("test-request"))
+        request = response.wsgi_request
+
+        assert request.user
+        assert request.organization
+        assert request.user == request.organization
+        assert isinstance(request.organization, User)
+        assert isinstance(request.user, User)
diff --git a/tests/urls.py b/tests/urls.py
new file mode 100644
index 0000000..8e52c82
--- /dev/null
+++ b/tests/urls.py
@@ -0,0 +1,24 @@
+from django.urls import path
+
+from rest_framework.request import Request
+from rest_framework.response import Response
+from rest_framework.decorators import (
+    api_view,
+    permission_classes,
+    authentication_classes,
+)
+
+from rest_framework_simple_api_key.backends import APIKeyAuthentication
+from rest_framework_simple_api_key.permissions import IsActiveEntity
+
+
+@api_view()
+@authentication_classes([APIKeyAuthentication])
+@permission_classes([IsActiveEntity])
+def view(request: Request) -> Response:
+    return Response({"detail: success"}, status=200)
+
+
+urlpatterns = [
+    path("test-request/", view, name="test-request")
+]