django rest framework - browsable api remove delete - django

I have the following view:
def retrieve(self, request, pk=None, **kwargs):
try:
instance = self.get_object()
self.check_object_permissions(self.request, instance)
serializer = PasswordFolderSerializer(instance, context={'request': request})
return Response(serializer.data)
except Http404:
return Response(status=status.HTTP_404_NOT_FOUND)
When not logged in I will get a 403 which is good however the "DELETE" button still shows in the browsable API. how do I get rid of this? Here is my permission:
class CanRetrievePasswordFolder(permissions.DjangoObjectPermissions):
def has_permission(self, request, view):
if request.user is None:
return False
else:
return True
def has_object_permission(self, request, view, obj):
access_levels = ['Owner', 'Admin', 'Read']
if get_permission_level(request, obj) is None:
return False
else:
level = AccessLevel.objects.get(pk=get_permission_level(request, obj).level_id).name
if request.method in permissions.SAFE_METHODS:
return True
else:
for access in access_levels:
if level == access:
return True
else:
return False

really stupid, I had to add IsAuthenticated to my permissions tuple on the view like so:
permission_classes_by_action = {'create': [CanCreatePasswordFolder, IsAuthenticated],
'list': [CanListPasswordFolder, IsAuthenticated],
'retrieve': [CanRetrievePasswordFolder, IsAuthenticated],
'partial_update': [CanUpdatePasswordFolder, IsAuthenticated],
'update': [CanUpdatePasswordFolder, IsAuthenticated],
'destroy': [CanDestroyPasswordFolder, IsAuthenticated]}

Related

Django rest framework custom permission for ViewSet

this is my modelViewSet
class UserViewSet(viewsets.ModelViewSet):
def list(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
pass
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
def get_permissions(self):
if self.action == "list":
permission_classes = [
IsAdminUser,
]
elif self.action == "create":
permission_classes = [AllowAny]
else:
permission_classes = [AccountOwnerPermission]
return [permission() for permission in permission_classes]
and this is the custom permission class
class AccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(object)
print(request.user)
return obj == request.user
i access this view from another user and it show me user retrieve, and that 2 prints on
AccountOwnerPermission won't run. can someone tell me what is wrong with what i did and why has_object_permission wont run.
i change has_object_permission to has_permission and it works, but i dont have access to obj on the other hand
From the docs:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So you'll need to call check_object_permissions in your retrieve to be able to trigger has_object_permission:
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
self.check_object_permissions(request, user) # Add this line
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)

Django redirect to login url with next ,without using method decorator

I have multiple links on my page which redirects user to take quiz.
Some quiz requires user to login or create an account and some does not.
def dispatch(self, request, *args, **kwargs):
self.quiz = get_object_or_404(Quiz, url=self.kwargs['quiz_name'])
if self.quiz.draft and not request.user.has_perm('quiz.change_quiz'):
raise PermissionDenied
try:
self.logged_in_user = self.request.user.is_authenticated()
except TypeError:
self.logged_in_user = self.request.user.is_authenticated
if self.logged_in_user:
self.sitting = Sitting.objects.user_sitting(request.user,
self.quiz)
else:
self.sitting = self.anon_load_sitting()
if self.sitting is False:
if self.logged_in_user:
return render(request, self.single_complete_template_name)
else:
return redirect(settings.LOGIN_URL)
return super(QuizTake, self).dispatch(request, *args, **kwargs)
I would like user to redirect like how method decorator does
login/?next=/quiz/f506cb92-ccca-49ff-b2e5-730bbfea6a5a/take/
but instead I get /login/
I would like my user to come back to the page instead of going to "/dashboard"
In my settings I have
LOGIN_REDIRECT_URL ="/dashboard"
My LoginView:
class LoginView(FormView):
template_name = 'login.html'
form_class = LoginForm
success_url = '/dashboard'
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect ("/dashboard")
else:
return super(LoginView, self).get(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if (self.request.user.is_authenticated) and (self.request.user.user_type==4):
return redirect('/dashboard')
else:
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""Use this to add extra context."""
context = super(LoginView, self).get_context_data(**kwargs)
if 'show_captcha' in self.request.session:
show_captcha = self.request.session['show_captcha']
context['show_captcha'] = True
return context
def form_valid(self, form):
user = form.login(self.request)
recaptcha_response = self.request.POST.get('g-recaptcha-response')
url = 'https://www.google.com/recaptcha/api/siteverify'
payload = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
data = urllib.parse.urlencode(payload).encode()
req = urllib.request.Request(url, data=data)
# verify the token submitted with the form is valid
response = urllib.request.urlopen(req)
result = json.loads(response.read().decode())
if result['success']:
if user.two_factor_auth is False and (user.phone_number_verified is True):
login(self.request, user)
try:
UserLog.objects.filter(username=user.id).update(failed_attempt=0)
except Exception:
print("No failed attempts ")
return redirect('/dashboard')
else:
try:
response = send_verfication_code(user)
pass
except Exception as e:
messages.add_message(self.request, messages.ERROR,
'verification code not sent. \n'
'Please retry logging in.')
return redirect('/login')
data = json.loads(response.text)
if data['success'] == False:
messages.add_message(self.request, messages.ERROR,
data['message'])
return redirect('/login')
if data['success'] == True:
self.request.method = "GET"
print(self.request.method)
kwargs = {'user':user}
return PhoneVerificationView(self.request, **kwargs)
else:
messages.add_message(self.request, messages.ERROR,
data['message'])
return redirect('/login')
else:
messages.add_message(self.request, messages.ERROR, 'Invalid reCAPTCHA. Please try again.')
return redirect('/login')
You should be fine by using #login_required() decorator docs are here.
This appends a ?next=... field which within login field you can take from the request.REQUEST.get('next', '/dashboard') and use this for redirect on successful login also look up this question to see if any other answer there satisifies the requirements
Also since you don't want decorators you can try saving to session as request.session["next"]=(source url) and within login view get session param and use it

I want to force change password on first login Django Rest

I have a custom User model with field is_resetpwd as a boolean field with default value True
I want that to change to False when the password is changed, following is my password change view
class ChangePasswordView(APIView):
"""
An endpoint for changing password.
"""
serializer_class = ChangePasswordSerializer
model = User
permission_classes = (IsAuthenticated,)
def get_object(self, queryset=None):
obj = self.request.user
# print("obj", obj)
return obj
def post(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
# Check old password
if not self.object.check_password(serializer.data.get("old_password")):
return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
response = {
'message': 'Password updated successfully',
}
return Response(response)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
what should I add in this to change my boolean value
Just before save() add this
self.object.is_resetpwd = False
and then save the objects just like before
The updated code will look like this
"""
An endpoint for changing passwords.
"""
serializer_class = ChangePasswordSerializer
model = User
permission_classes = (IsAuthenticated,)
def get_object(self, queryset=None):
obj = self.request.user
# print("obj", obj)
return obj
def post(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
# Check old password
if not self.object.check_password(serializer.data.get("old_password")):
return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
self.object.set_password(serializer.data.get("new_password"))
self.object.is_resetpwd = False
self.object.save()
response = {
'message': 'Password updated successfully',
}
return Response(response)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Can I get cookies value or session value inside the has_permission method of django rest framework?

I am working on a project where i have to check the user whether they belong the company or not.i am already put check while login user.
how i can use company id inside the has_permission() method?
class IsCompanyEmployee(permissions.BasePermission):
message = 'You are unauthorized to perform any action on this company.'
def has_permission(self, request, view):
if request.user.is_authenticated():
if request.user.is_superuser:
return True
else:
#company_id = request.COOKIES["company_id"]
#or
#company_id = request.session["company_id"]
return request.user.companyemployee_set.filter(company__id=company_id).exists()
else:
return False
class IsCompanyEmployee(permissions.BasePermission):
message = 'You are unauthorized to perform any action on this company.'
def has_permission(self, request, view):
if request.user.is_authenticated():
if request.user.is_superuser:
return True
else:
if 'company_id' in request.session:
company_id = request.session.get('company_id')
return request.user.companyemployee_set.filter(company__id=company_id).exists()
else:
return False
else:
return False

django rest framework - object permission does not trigger

I have the following setup:
permission.py
def get_permission_level(request, obj):
try:
level = PasswordListACL.objects.get(list=obj, user=request.user)
except PasswordListACL.DoesNotExist:
level = None
return level
class IsPasswordListOwner(permissions.DjangoObjectPermissions):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
print(get_permission_level(request, obj))
if get_permission_level(request, obj) == None:
print('false')
return False
else:
level = AccessLevel.objects.get(pk=get_permission_level(request, obj).level_id).name
if level == 'Owner':
return True
else:
return False
I can confirm through my print('false') that the permission is being acted on and it is returning the correct value (I'm expecting False) however the view is still returning the data instead of a 403.
views.py
class PasswordListViewSet(viewsets.ModelViewSet):
queryset = PasswordList.objects.all()
serializer_class = PasswordListSerializer
permission_classes = (IsPasswordListOwner,)
def list(self, request):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset().filter(passwordlistacl__user=request.user)
serializer = PasswordListSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, pk=None):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset()
if get_object_or_404(queryset, pk=pk):
password = get_object_or_404(queryset, pk=pk)
else:
pass
serializer = PasswordListSerializer(password, context={'request': request})
return Response(serializer.data)
edit - changing the return on has_permission confirms the permission there is overriding my has_object_permission?
Doing this gives me a 403 (correct):
def has_permission(self, request, view):
return False
The documentation states:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So since you're not calling get_object() you need to be calling self.check_object_permissions(self.request, obj) at some point.