0

I am using Django 2.2 and Python 3.6.

I deployed a Django REST server using AWS EB, but I get the following error.

It works fine on the local side, but an error occurs in the EB instance.

As a result of my analysis, request.user is recognized normally on the local, but on the EB it is marked as an anonymous user.

I am using the same code, but why does this happen?

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
}

I changed the above code to the below code because it is a problem of AUTHENTICATION_CLASSES, but I still get an error.

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
        "rest_framework.authentication.BasicAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ],
}

Error Detail

AttributeError
'AnonymousUser' object has no attribute 'is_admin'

users/permissions.py in has_permission at line 26
    def has_permission(self, request, view):
        print("=" * 50)
        print(request.user)
        print("=" * 50)
        return bool(request.user and request.user.is_admin)

Timeline

> GET /api/v1/users/ HTTP/1.1
> Host: instance.ap-northeast-2.elasticbeanstalk.com
> User-Agent: insomnia/2020.2.2
> Content-Type: application/json
> Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkzNjA4Nzg5LCJqdGkiOiJmZGY5YmM4MWM3M2I0YTU3YmZkODg2YmU5ZWVlMGEzZCIsInVzZXJfaWQiOjN9.kLv3H7ygzVomI2DgU84I900m4CydhL48Ob86SX5IEaQ

users/models.py

class User(AbstractBaseUser, TimeStampedModel):
    objects = UserManager()

    GENDER_MALE = "male"
    GENDER_FEMALE = "female"
    GENDER_OTHER = "other"

    GENDER_CHOICES = (
        (GENDER_MALE, "Male"),
        (GENDER_FEMALE, "Female"),
        (GENDER_OTHER, "Other"),
    )

    email = models.EmailField(unique=True)
    username = models.CharField(max_length=20, unique=True)
    gender = models.CharField(max_length=5, choices=GENDER_CHOICES)
    birth = models.DateField()
    avatar = models.ImageField(upload_to="user_avatars/%Y/%m/%d", blank=True)
    is_admin = models.BooleanField(default=False)

users/views.py

from .permissions import IsSelf, IsAdminOrSelf, IsAdminUser

class UsersViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_permissions(self):
        if self.action == "list":
            permission_classes = [IsAdminUser]
        elif self.action == "create" or self.action == "retrieve":
            permission_classes = [AllowAny]
        elif self.action == "destroy":
            permission_classes = [IsAdminOrSelf]
        else:
            permission_classes = [IsSelf]

users/permissions.py

from rest_framework.permissions import BasePermission


class IsSelf(BasePermission):
    def has_object_permission(self, request, view, user):
        return bool(user == request.user)


class IsAdminOrSelf(BasePermission):
    def has_object_permission(self, request, view, user):
        is_self = bool(user == request.user)
        is_admin = request.user.is_admin

        return is_self or is_admin


class IsAdminUser(BasePermission):
    """
    Allows access only to admin users.
    """

    def has_permission(self, request, view):
        print("=" * 50)
        print(request.user)
        print("=" * 50)
        return bool(request.user and request.user.is_admin)

Server Traceback

Traceback:

File "/opt/python/run/venv/local/lib64/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/opt/python/run/venv/local/lib64/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/opt/python/run/venv/local/lib64/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/opt/python/run/venv/local/lib64/python3.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
  54.         return view_func(*args, **kwargs)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/viewsets.py" in view
  114.             return self.dispatch(request, *args, **kwargs)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in dispatch
  505.             response = self.handle_exception(exc)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in handle_exception
  465.             self.raise_uncaught_exception(exc)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in raise_uncaught_exception
  476.         raise exc

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in dispatch
  493.             self.initial(request, *args, **kwargs)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/sentry_sdk/integrations/django/__init__.py" in sentry_patched_drf_initial
  258.                     return old_drf_initial(self, request, *args, **kwargs)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in initial
  411.         self.check_permissions(request)

File "/opt/python/run/venv/local/lib/python3.6/site-packages/rest_framework/views.py" in check_permissions
  332.             if not permission.has_permission(request, self):

File "/opt/python/current/app/users/permissions.py" in has_permission
  26.         return bool(request.user and request.user.is_admin)

Exception Type: AttributeError at /api/v1/users/
Exception Value: 'AnonymousUser' object has no attribute 'is_admin'
Request information:
USER: AnonymousUser

GET: No GET data

POST: No POST data

FILES: No FILES data

COOKIES: No cookie data

JWT Auth
users/urls.py

from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt import views as jwt_views

from django.urls import path

from . import views

urlpatterns = [
    path("token/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("token/refresh/", jwt_views.TokenRefreshView.as_view(), name="token_refresh"),
]

Session-based authentication seems to work.

session-based

In my opinion, the Authorization header doesn't seem to work.

# code
class IsAdminUser(BasePermission):
    """
    Allows access only to admin users.
    """

    def has_permission(self, request, view):
        print("=" * 50)
        print(request.auth)
        print(request.data)
        print(request.user)
        print("=" * 50)
        return bool(request.user and request.user.is_admin)

# result in AWS EB
[Wed Jul 01 20:37:28.712785 2020] [:error] [pid 3995] ==================================================
[Wed Jul 01 20:37:28.712834 2020] [:error] [pid 3995] None
[Wed Jul 01 20:37:28.713505 2020] [:error] [pid 3995] <QueryDict: {}>
[Wed Jul 01 20:37:28.713522 2020] [:error] [pid 3995] AnonymousUser
[Wed Jul 01 20:37:28.713529 2020] [:error] [pid 3995] ==================================================

# result in localhost
System check identified no issues (0 silenced).
July 01, 2020 - 20:43:04
Django version 2.2.12, using settings 'config.settings'
Starting development server at http://127.0.0.1:9000/
Quit the server with CONTROL-C.
==================================================
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkzNjA1MzM0LCJqdGkiOiIyOTY3ZTQ3MDEzY2Q0MDNlODQxN2VjNTNkMDU4ZDRjZiIsInVzZXJfaWQiOjF9.3czMFSzMR-g-vraPnOhhf0UCWamlIpSLuD0I1RBJOnA
<QueryDict: {}>
1 : tim
==================================================
[01/Jul/2020 20:43:13] "GET /api/v1/users/ HTTP/1.1" 200 1768

What the hell is the problem..

Tim
  • 637
  • 1
  • 5
  • 9

2 Answers2

1

The first problem is that AnonymousUser does not have an is_admin property in django. You can maybe check is_superuser or check if your user is authenticated before calling is_admin on it. See How to check if a user is logged in (how to properly use user.is_authenticated)? for that.

About the difference between local and distant, I would guess that you're logged in on your local app but not on your distant app. This is why AnonymousUser gets returned by request.user on your distant app.

Leogout
  • 1,187
  • 1
  • 13
  • 32
  • I'm sorry, but I got an error despite putting the Authorization header with Json type. Heather is sending: Authorization: Bearer {JWT} – Tim Jul 01 '20 at 09:00
  • I don't seem to be able to log in only in the deployment environment by JWT Auth, but I can log in from the Django Admin window. how can I fix this? – Tim Jul 01 '20 at 09:02
  • If you are unable to log in, where does the JWT you pass in your Header comes from ? (you mention it in your first comment). It may be a lot of things, maybe your API can't communicate with your backend? Try to add more info in your question about the networking, what is passed on login, what gets returned by the server, etc. – Leogout Jul 01 '20 at 09:06
  • I added more information to the question, check it please! Thanks for spending time for me. – Tim Jul 01 '20 at 09:16
0

The problem I encountered was the same as the cause in this question.

This is also a known issue on the AWS forums.

You can fix it in the following way:

# .ebextensions/wsgihacks.config

files:
  "/etc/httpd/conf.d/wsgihacks.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      WSGIPassAuthorization on

Original thread: https://forums.aws.amazon.com/message.jspa?messageID=376244

Tim
  • 637
  • 1
  • 5
  • 9