add user_id attribute in AuthenticationMiddleware | Django - django

I have a following question regarding Django authentication middleware:
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
)
request.user = SimpleLazyObject(lambda: get_user(request))
As you can see here middleware calls method get_user from backend (in my case simple_jwt) and puts this method in a SimpleLazyObject in order to evaluate later.
def get_user(self, validated_token):
"""
Attempts to find and return a user using the given validated token.
"""
try:
user_id = validated_token[api_settings.USER_ID_CLAIM]
except KeyError:
raise InvalidToken(_('Token contained no recognizable user identification'))
try:
user = User.objects.get(**{api_settings.USER_ID_FIELD: user_id})
except User.DoesNotExist:
raise AuthenticationFailed(_('User not found'), code='user_not_found')
if not user.is_active:
raise AuthenticationFailed(_('User is inactive'), code='user_inactive')
return user
What I want to do is to keep request.user = SimpleLazyObject(lambda: get_user(request)) in order to provide user instance for apps that uses it but for my custom apps I want to add something like
pseudocode
request.user_id = user_id from user (user_id = validated_token[api_settings.USER_ID_CLAIM])
in order to not query database each and every request for user object in case I need only user_id which I already have directly in get_user method in backend.
Question – how to pass user_id from get_user() to AuthenticationMiddleware.process_request() and set request.user_id attribute to request without evaluating SimpleLazyObject?
Strangelly i can't assign attributes to request in class JWTAuthentication(authentication.BaseAuthentication): where get_user() is belong
Thank you.
Internal Server Error: /auth/users/me/
Traceback (most recent call last):
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\asgiref\sync.py", line 330, in thread_handler
raise exc_info[1]
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\django\core\handlers\exception.py", line 38, in inner
response = await get_response(request)
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\django\utils\deprecation.py", line 126, in __acall__
response = await sync_to_async(
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\asgiref\sync.py", line 296, in __call__
ret = await asyncio.wait_for(future, timeout=None)
File "C:\ProgramData\Miniconda3\envs\entropy\lib\asyncio\tasks.py", line 440, in wait_for
return await fut
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\asgiref\current_thread_executor.py", line 23, in run
result = self.fn(*self.args, **self.kwargs)
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\asgiref\sync.py", line 334, in thread_handler
return func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\django\contrib\auth\middleware.py", line 26, in process_request
request.user_id = _get_user_session_key(request)
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\django\contrib\auth\__init__.py", line 58, in _get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
File "C:\ProgramData\Miniconda3\envs\entropy\lib\site-packages\django\contrib\sessions\backends\base.py", line 65, in __getitem__
return self._session[key]
KeyError: '_auth_user_id'

You already have the user id available. It's decoded from the session inside auth.get_user() and you can just copy that to your own MiddleWare:
from django.contrib.auth import _get_user_session_key
request.user_id = _get_user_session_key(request)
This is a Django private API, not sure why they kept it private. But you can also just copy the one liner it implements:
request.user_id = get_user_model()._meta.pk.to_python(
request.session[SESSION_KEY]
)

Related

Django JsonResponse: User matching query does not exist

So I´ve tried to make an ajax for my models but it is giving me a matching query does not exist, here is the full error:
Internal Server Error: /messages/notification/
Traceback (most recent call last):
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\contrib\auth\mixins.py", line 52, in dispatch
return super().dispatch(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\detail.py", line 106, in get
self.object = self.get_object()
File "C:\Users\berna\Desktop\Python & Javascript\Web development\MiFamiliaEsUnDesastre\mifamiliaesundesastre\chat\views.py", line 26, in get_object
obj, created = Thread.objects.get_or_new(self.request.user, other_username)
File "C:\Users\berna\Desktop\Python & Javascript\Web development\MiFamiliaEsUnDesastre\mifamiliaesundesastre\chat\models.py", line 29, in get_or_new
user2 = Klass.objects.get(username=other_username)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\db\models\query.py", line 415, in get
raise self.model.DoesNotExist(
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
HTTP GET /messages/notification/ 500 [0.16, 127.0.0.1:56012]
Also here is my code:
models.py
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
#to=settings.AUTH_USER_MODEL,
to = User,
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.FloatField(default='0')
views.py
def notifications(request, user_id, *args, **kwargs):
user = User.objects.get(pk=user_id)
user.profile.notifications = user.profile.notifications + 1
user.save()
return JsonResponse(data=user.profile.notifications, safe=False)
urls.py
path('messages/notification/', notifications)
my html for the ajax call
// Add the notification val
$.get('notification/')
someone tell me that is that I dont have users, but when I do have a user, I dont know what is going on
You need to send user_id to the view:
path('messages/notification/(?P<user_id>[a-zA-Z0-9/_\.-]*)', notifications)
and:
$.get('messages/notification/{{ user.pk }}')
Update
You have to know their user id to get their notifications:
$.get('messages/notification/12234')
I assume you have a User model somewhere?
Also, user_id is coming in as a string, so need to int() that:
user = User.objects.get(pk=int(user_id))
Or use:
path('messages/notification/<int:user_id>', notifications)
So for does who saw this later, what I did is to change the
path('messages/notification/', notifications)
To
url(r'^ajax/notification/$', notification),

Django test case ValueError: The given username must be set

I am testing a Django application, for its user sign-up feature, whether the posted data is correct and post request executes successfully.
In views.py, the Class CustomerSignUpView
class CustomerSignUpView(View):
def post(self, request):
name_r = request.POST.get('customer_username')
password_r = request.POST.get('customer_password')
email_r = request.POST.get('customer_email')
contact_number_r = request.POST.get('customer_contact_number')
profile_picture_r = request.POST.get('customer_profile_picture')
if checkemail(email_r):
# receiving an error here in TEST CASE not in actual program execution
c = User(username=name_r, password=password_r, email=email_r)
c.save()
p = Profile(user=c, phone_number=contact_number_r, profile_picture=profile_picture_r)
p.save()
return render(request, 'catalog/customer_login.html')
else:
return render(request, 'catalog/customer_signup.html')
def get(self, request):
return render(request, 'catalog/customer_signup.html')
This is the test case for user account creation:
class CustomerSignUpViewTest(TestCase):
"""
Test case for User Sign in
"""
def test_registration_view_post_success(self):
"""
A ``POST`` to the ``customer_signup`` view with valid data properly
creates a new user and issues a redirect.
"""
data = {
'username': 'testuser1',
'password': '1X<ISRUkw+tuK',
'email': 'foobar#test.com',
'phone_number': '9876543210',
}
response = self.client.post(reverse('customer_signup'), data, follow=True)
self.assertEqual(response.status_code, 200)
self.assertTrue(response.url.startswith('/catalog/customer_login/'))
The test encounters the following error: ValueError('The given username must be set')
Error
Traceback (most recent call last):
File "/Users/sndtcsi/PycharmProjects/Library/catalog/tests.py", line 54, in test_registration_view_post_success
response = self.client.post(reverse('customer_signup'), data, follow=True)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/test/client.py", line 535, in post
response = super().post(path, data=data, content_type=content_type, secure=secure, **extra)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/test/client.py", line 349, in post
secure=secure, **extra)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/test/client.py", line 414, in generic
return self.request(**r)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/test/client.py", line 495, in request
raise exc_value
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 124, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/views/generic/base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "/Users/sndtcsi/PycharmProjects/Library/catalog/views.py", line 107, in post
c = User.objects.create_user(username=name_r, password=password_r, email=email_r)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/contrib/auth/models.py", line 150, in create_user
return self._create_user(username, email, password, **extra_fields)
File "/Users/sndtcsi/PycharmProjects/Library/venv/lib/python3.7/site-packages/django/contrib/auth/models.py", line 139, in _create_user
raise ValueError('The given username must be set')
While my sign-up feature works perfectly, the test execution shows that the username is not set. I do not understand why that is happening.
You are using bad key in your TestCase. You are trying to get customer_username in the form in your view, but you are actually posting username key in the data in your TestCase. To make this work you should make this lines to use the same keys:
name_r = request.POST['customer_username']
and
'username': 'testuser1',
The same problems are with all another forms in this your code.

Validate each value of an ArrayField of emails in Django

I am using PostgreSQL with Django and am trying to use ArrayField(models.EmailField(max_length=200)). But I am ending up with an 500 error code(TypeError) when giving invalid email address.
How can I validate each email in the ArrayField. I have tried writing validate method in both serializer and models. But nothing helped. What is the right way to validate ArrayFields? I am using Django 2.1 and Python 3.6
The traceback of the error is given below...please have a look
models.py
billing_emails = ArrayField(models.EmailField(max_length=200))
company = models.OneToOneField(Company, on_delete=models.CASCADE)
Traceback
Traceback (most recent call last):
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/django/core/handlers/base.py", line 156, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/django/core/handlers/base.py", line 154, in _get_response
response = response.render()
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/django/template/response.py", line 106, in render
self.content = self.rendered_content
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/rest_framework/response.py", line 72, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/djangorestframework_camel_case/render.py", line 8, in render
return super(CamelCaseJSONRenderer, self).render(camelize(data), *args,
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/djangorestframework_camel_case/util.py", line 17, in camelize
new_dict[new_key] = camelize(value)
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/site-packages/djangorestframework_camel_case/util.py", line 16, in camelize
new_key = re.sub(r"[a-z]_[a-z]", underscoreToCamel, key)
File "/home/user/.local/share/virtualenvs/backend-Amy45Zrz/lib/python3.6/re.py", line 191, in sub
return _compile(pattern, flags).sub(repl, string, count)
TypeError: expected string or bytes-like object
Thanks in advance.
If you are using Forms, use Form or Serializer, you can use the clean_<attribute_name> method to validate the data. For example:
from django.core.validators import email_re
from django.core.exceptions import ValidationError
class YourForm(forms.ModelForm): # serializers.ModelSerializer
def clean_billing_emails(self):
data = self.cleaned_data['billing_emails']
for email in data:
if not email_re.match(email):
raise ValidationError('Invalid email {}'.format(email) # or raise serializers.ValidationError
return data
Also you can use model's clean method.
class YourModel(models.Model):
...
def clean(self):
try:
for email in self.billing_emails:
if not email_re.match(email):
raise ValidationError('Invalid email {}'.format(email)
except (TypeError, Exception) as e:
# log error
# handle error
def save(self, *args, **kwargs):
self.full_clean()
return super(YourModel, self).save(*args, **kwargs)

django rest framework has_object_permission raising PermissionDenied exception

Am trying to build a permission class for my django rest framework view, which should either allow or disallow the user access.
class UserPermissions(permissions.DjangoModelPermissions):
"""
"""
def has_object_permission(self, request, view, obj):
# if safe methods (get, retrieve) we check permissions from django perms
if request.method in permissions.SAFE_METHODS:
return super(UserPermissions, self).has_object_permission(request, view, obj)
# else if methods (post, put, patch), we match user with obj and if obj is owner we grant him access
return request.user == obj
the above code seems to be working fine, but while I was build test cases. I built one case where I try to intentionally update user x using user y credentials
def test_patching_someones_else_profile(self):
""" Test patching someone's else profile """
response = self.c.post("/api/account/api-token-auth/", {'username': 'lion', 'password': 'password'})
self.assertEqual(response.status_code, 200, "User couldn't log in")
token = response.data['token']
user_id = response.data['id']
header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token)}
response = self.c.patch(reverse('user-detail', args=['1', ]), {'last_name': 'mooz'}, **header)
the problem am seeing now, its suddenly throws an exception PermissionDenied(), it does not return any status_code to handle, instead the application is crashing with following exception
======================================================================
ERROR: test_patching_someones_else_profile (dlap.apps.account.tests.UserTestCase)
Test patching someone's else profile
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/tests.py", line 217, in test_patching_someones_else_profile
response = self.c.patch(reverse('user-detail', args=['1', ]), {'last_name': 'mooz'}, **header)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 84, in patch
return self.generic('PATCH', path, data, content_type, **extra)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/compat.py", line 487, in generic
return self.request(**r)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 143, in request
return super(APIClient, self).request(**kwargs)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 95, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/test/client.py", line 444, in request
six.reraise(*exc_info)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/core/handlers/base.py", line 139, in get_response
response = response.render()
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/template/response.py", line 105, in render
self.content = self.rendered_content
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/response.py", line 59, in rendered_content
ret = renderer.render(self.data, media_type, context)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 577, in render
context = self.get_context(data, accepted_media_type, renderer_context)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 537, in get_context
raw_data_put_form = self.get_raw_data_form(view, 'PUT', request)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 475, in get_raw_data_form
serializer = view.get_serializer(instance=obj)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/generics.py", line 99, in get_serializer
serializer_class = self.get_serializer_class()
File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 96, in get_serializer_class
obj = self.get_object()
File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 107, in get_object
return super(UserViewSet, self).get_object(queryset=queryset)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/generics.py", line 321, in get_object
self.check_object_permissions(self.request, obj)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/views.py", line 284, in check_object_permissions
self.permission_denied(request)
File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/views.py", line 135, in permission_denied
raise exceptions.PermissionDenied()
PermissionDenied
Any one faced this issue before? is there a way to return 403 with permission denied message without the app crashing?
Update:
As suggested below that get_seralizer_class() could be causing the problem, it is highly the reason why am facing this error. But, what alternatives do I have to build a dynamic seralizer class method similar to the below?
def get_serializer_class(self):
# decide on which seralizer to use
if u'create_guest' in self.action_map.itervalues():
return UserGuestSerializer
if u'create_user' in self.action_map.itervalues():
return PublicUserSerializer
obj = self.get_object()
if self.request.user.id == obj.id:
if u'set_password' in self.action_map.itervalues():
return PasswordSerializer
return PrivateUserSerializer
return PublicUserSerializer
You appear to be using a custom .get_serializer_class() method that is calling .get_object() for some reason.
File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 96, in get_serializer_class
obj = self.get_object()
This is causing the per-object permission checks to be re-run when the Browsable API response is being rendered. Is it possible to reconsider your get_serializer_class implementation?
Edit: Note that you can probably simply inspect self.kargs['pk'] rather than actually calling through to get_object. It doesn't look like you need to perform the full object retrieval in order to make the ownership check.

BaseDeleteView throws AttributeError (render_to_response missing)

I try to implement a view based on BaseDeleteView for a website that acts as a frontend to an REST backend. Both sides communicate over HTTP requests. What I want to achieve is that I send a GET request to an activation URI (send per email after registration). Inside this view I first send a HTTP request to a backend, and then delete the activation object from the database of the frontend. I don't want to have a confirmation page, so DeleteView is not possible.
class ActivationView(BaseDeleteView):
success_url = "/activation/success/"
def get_object(self, queryset=None):
uuid = self.kwargs['uuid']
try:
obj = AccountRegistration.objects.get(uuid=uuid)
except ObjectDoesNotExist:
raise Http404('Registration not found.')
return obj
def delete(self, request, *args, **kwargs):
obj = self.get_obj()
if obj.expire_date < datetime.now():
obj.delete()
raise Http404('Registration expired.')
# send a http request to the backend
t = Transaction('/activate/%s/' % obj.account_name)
t.emit()
# delete the object
obj.delete()
# and redirect the request
return HttpResponseRedirect(self.get_success_url())
My urls.py looks like that:
url(r'^activate/(?P<uuid>\w+)/$',
ActivationView.as_view(), name="account-activate"),
But I get the following error:
Traceback (most recent call last):
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 68, in __call__
return self.application(environ, start_response)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 272, in __call__
response = self.get_response(request)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/core/handlers/base.py", line 169, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/core/handlers/base.py", line 203, in handle_uncaught_exception
return debug.technical_500_response(request, *exc_info)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/views/generic/base.py", line 47, in view
return self.dispatch(request, *args, **kwargs)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in dispatch
return handler(request, *args, **kwargs)
File "/home/crito/.pythonbrew/venvs/Python-2.7.2/thirty-web/lib/python2.7/site-packages/django/views/generic/detail.py", line 100, in get
return self.render_to_response(context)
AttributeError: 'ActivationView' object has no attribute 'render_to_response'
In my eyes it shouldn't even call render_to_response. Any ideas?
If you want to leave out the confirmation page, just call your DeleteView directly with POST. This is most desirable as the deletion of an object should be protected by csrf.
You've inherited from BaseDeleteView, which as the documentation states, doesn't include the TemplateResponseMixin - ie all the bits that are to do with rendering a response.
Inherit from DeleteView instead.