User account delete in django's rest - python

I want create view which can deactivation user's account. when i create view and send delete request i have error - > "detail": "You do not have permission to perform this action.". i have authenticated permissionn but i am login in my account. i also use APIview
this is code ->
class DeleteAccount(generics.RetrieveUpdateDestroyAPIView):
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def delete(self, request, *args, **kwargs):
user=self.request.user
user.delete()
return Response({"result":"user delete"})

Since you seem to be using a purely view-based approach, APIView might do the trick instead of the generic class. Additionally, setting the serializer class isn't necessary as well.
from rest_framework.views import APIView
class DeleteAccount(APIView):
permission_classes = [permissions.IsAuthenticated]
def delete(self, request, *args, **kwargs):
user=self.request.user
user.delete()
return Response({"result":"user delete"})
Additionally as a general practice, it's better/safer to reserve user deletion capabilities only for admin/staff users.

Related

Django : class based view with multiple permissions

I am trying to create a class based view with different permissions per function. I have already created my permissions in a file that I import :
utils.py
from rest_framework.permissions import BasePermission
from rest_framework.authentication import TokenAuthentication
from rest_framework.views import APIView
class IsOwner(BasePermission):
"""
Check if the user who made the request is owner.
Use like that : permission_classes = [IsOwner]
"""
def has_object_permission(self, request, view, obj):
# if request.method in permissions.SAFE_METHODS:
# return True
return obj.user == request.user
class IsAdmin(BasePermission):
"""
Check if the user who made the request is admin.
Use like that : permission_classes = [IsAdmin]
"""
def has_permission(self, request, view):
return request.user.is_admin
class BaseView(APIView):
"""
Check if a user is authenticated
"""
authentication_classes = [
TokenAuthentication,
]
class AdminOrOwnerView(APIView):
"""
Check if a user is admin or owner
"""
authentication_classes = ( IsOwner | IsAdmin,)
I want to create a class with different method. The GET method would allow admins to have a view of all users. The POST method would allow any user to connect.
I have already created the serializer for each of these methods but I cannot assign different permissions per method.
This is my class :
class Users(APIView):
def get(self, request):
"""Only for admin"""
try:
user = Users.objects.all()
except User.DoesNotExist():
return HttpResponse(status=404)
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
"""For everyone"""
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.error)
How I can apply my permissions on each of the methods separately ?
Thank you in advance for your help
You just need to create a permission class like this:
class CustomPermissionClass(BasePermission):
def has_permission(self, request, view):
if request.method == 'GET':
# logic for GET method
elif request.method == 'POST'
# logic for POST metod
# default logic
And add it to your view:
class Users(APIView):
permission_classes = [CustomPermissionClass]

Adding a redirect to CreateAPIView

I want to redirect the user to the AddQuestionsView after the user creates a quiz(after adding title).
My CreateQuiz
class CreateQuizzView(CreateAPIView):
serializer_class = CreateQuizSerializer
My serializers.py file
class CreateQuizSerializer(serializers.ModelSerializer):
class Meta:
model = Quizzer
fields = ['title']
def create(self, validated_data):
user = self.context['request'].user
new_quiz = Quizzer.objects.create(
user=user,
**validated_data
)
return new_quiz
Can i add redirect by adding any Mixin or change need to change the GenericView.
An APIView is normally not used by a browser, or at least not directly, hence a redirect makes not much sense. The idea is that some program makes HTTP requests, and thus retrieves a response. Most API handlers will not by default follow a redirect anyway.
You can however make a redirect, by overriding the post method:
from django.shortcuts import redirect
class CreateQuizzView(CreateAPIView):
serializer_class = CreateQuizSerializer
def post(self, *args, **kwargs):
super().post(*args, **kwargs)
return redirect('name-of-the-view')

How to logout in rest_framework with JWT authentication?

How to log out in rest_framework?
This is my user serializer. I'm using rest and very very newbie. Sign up and Login working, but have no clue how to impelement logout.
class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(write_only=True)
class Meta:
model = User
fields = ('id', 'username', 'password', 'email')
write_only_fields = ('username', 'email', 'password',)
read_only_fields = ('id', )
Sign up part
class UserCreateAPIView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.AllowAny]
Loggin code; i am using JWT authentication.
path(r'login/', obtain_jwt_token, name='ObtainJWTToken'),
When i use this code:
#api_view(['POST'])
def logout(request):
request.auth.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
i get error: 'bytes' object has no attribute 'delete'
When i use Djoser code:
permission_classes = settings.PERMISSIONS.token_destroy
def post(self, request):
utils.logout_user(request)
return Response(status=status.HTTP_204_NO_CONTENT)
getting this error: type object 'Token' has no attribute 'objects'
I assume you use TokenAuthentication.
In this case, give this method a shot:
class LogoutAPIView(APIView):
def get(self, request):
request.user.auth_token.delete()
return Response(status=status.HTTP_200_OK)
However, please note that this deletes the token and consequently forces a logout.
In case you use SessionAuthentication, the body of your get() method inside the LogoutAPIView class could be just logout(request).
eg.
def get(self, request):
logout(request)
return Response(status=status.HTTP_200_OK)
In the case of JWT authentication, as it is stateless and each JWT token is valid for some time, that means even if you remove the token from the DB, the user will still be able to use your endpoints for a short amount of time, until it's expired.
Therefore, depending on how deep you want to go, you can eg. implement a "tokens_unable_to_login" cache key and check if the requests include a JWT token that should not log in. Or you let it expire - you can toy around with the expiration time of the token and see what's ideal for your use-case. That's up to your business requirements.
Let me know how did this go for you.

Rest Framework serializers, forbid users to change others password

I'm creating a simple web app and I cannot find any way to forbid other users from changing your password. This is minimal code:
# serializers.py
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
# have to use custom create method because default method calls `User.objects.create()` which doesn't take care of password hashing and other important stuff
return User.objects.create_user(**validated_data)
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
read_only_fields = ('id', 'username')
# password is set to write_only because I don't want to send it to anybode (even though it's just a hash)
extra_kwargs = {'password': {'write_only': True}}
# views.py
from .serializers import UserSerializer
from rest_framework import generics
from django.contrib.auth.models import User
class UserDetails(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
I could take care of this myself by using APIView.
# views.py
from .serializers import UserSerializer
from rest_framework.views import APIView
from django.contrib.auth.models import User
class UserDetails(APIView):
def put(self, request, format=None):
serialized = UserSerializer(data=request.DATA)
if not serialized.is_valid():
return # probably HTTP 400 Error code
if request.user.id != serialized.data['id']:
# this if is what I'm trying to achieve
return # probably HTTP 403 Error code
user = User.objects.update(
id=serialized.data['id'],
email=serialized.data['email']
)
if 'password' in request.DATA:
user.set_password(request.DATA['password'])
return # probably HTTP 200 Error code
Unfortunately, that would have caused that scheme generated by rest_framework.schemas.get_schema_view would be incomplete. And I use for communication CoreAPI (which cannot communicate with something that is not described in the scheme) so I cannot do that. I haven't found anything in the official documentation.
This seems to be a too basic problem that has a super easy solution that I missed. Thanks for any ideas or places where to look.
PS: I'm using django2.1 with python3.6
Edit: osobacho's solutions is clean and works like charm. Anyway I also need to allow modifications (let's say of TODO-list) only to creator of that todo list. Thought that solution for password problem would be applicable but it's not.
You can get the user in the request. That way each user will only be able to change their own password.
class UserDetails(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_object(self):
return self.request.user
For your second question take a look at django_restframework http://www.django-rest-framework.org/api-guide/permissions/ permissions here is an example:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
then you need to add to your view:
permission_classes = (IsOwnerOrReadOnly,)
hope it helps

CSRF Exempt Failure - APIView csrf django rest framework

I have the following code:
The problem is when I try to access user-login/ I get an error:
"CSRF Failed: CSRF cookie not set."
What can I do?
I am using the django rest framework.
urls.py:
url(r'^user-login/$',
csrf_exempt(LoginView.as_view()),
name='user-login'),
views.py:
class LoginView(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
startups = Startup.objects.all()
serializer = StartupSerializer(startups, many=True)
return Response(serializer.data)
def post(self, request, format=None):
profile = request.POST
if ('user_name' not in profile or 'email_address' not in profile or 'oauth_secret' not in profile):
return Response(
{'error': 'No data'},
status=status.HTTP_400_BAD_REQUEST)
username = 'l' + profile['user_name']
email_address = profile['email_address']
oauth_secret = profile['oauth_secret']
password = oauth_secret
I assume you use the django rest framework SessionBackend. This backend does a implicit CSRF check
You can avoid this by:
from rest_framework.authentication import SessionAuthentication
class UnsafeSessionAuthentication(SessionAuthentication):
def authenticate(self, request):
http_request = request._request
user = getattr(http_request, 'user', None)
if not user or not user.is_active:
return None
return (user, None)
And set this as authentication_classes in your View
class UnsafeLogin(APIView):
permission_classes = (AllowAny,) #maybe not needed in your case
authentication_classes = (UnsafeSessionAuthentication,)
def post(self, request, *args, **kwargs):
username = request.DATA.get("u");
password = request.DATA.get("p");
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return redirect("/")
Actually, better way to disable csrf check inside SessionAuthentication is:
from rest_framework.authentication import SessionAuthentication as OriginalSessionAuthentication
class SessionAuthentication(OriginalSessionAuthentication):
def enforce_csrf(self, request):
return
The easiest way to solve this problem:
For that there are two ways of authentication in drf see drf auth
BasicAuthentication
SessionAuthentication (default)
SessionAuthentication has a forced csrf check, but BasicAuthentication doesn't.
So my way is using BasicAuthentication in my view instead of SessionAuthentication.
from rest_framework.authentication import BasicAuthentication
class UserLogin(generics.CreateAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = UserSerializer
authentication_classes = (BasicAuthentication,)
def post(self, request, *args, **kwargs):
return Response({})
Probably better to just make the enforce_csrf check do nothing:
from rest_framework.authentication import SessionAuthentication
class UnsafeSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, *args, **kwargs):
'''
Bypass the CSRF checks altogether
'''
pass
Otherwise you'll possibly end up with issues in the future if the upstream authenticate() method changes. Also, it's MUCH simpler to just make the check not do anything :-)

Categories

Resources