DjangoModelPermissions not working with DRF - django

I am trying to use Django Rest Framework and face a problem. The problem is when i try to use DjangoModelPermissions it doesn't follow the permission from Django admin panel.
What I excepted, is that when I set permission Projects|projects|can view projects then user can see the output.
Here are my codes:
Views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAdminUser, IsAuthenticated, DjangoModelPermissions
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from .models import Projects
from .serializers import ProjectSerializer
class ProjectsViewSet(viewsets.ViewSet):
permission_classes_by_action = {'list': [DjangoModelPermissions]}
queryset = Projects.objects.none()
serializer_class = ProjectSerializer
def list(self, request):
queryset = Projects.objects.all()
serializer = ProjectSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = Projects.objects.all()
project = get_object_or_404(queryset, pk=pk)
serializer = ProjectSerializer(project)
return Response(serializer.data)
def get_permissions(self):
try:
return [permission() for permission in self.permission_classes_by_action[self]]
except KeyError:
return [permission() for permission in self.permission_classes]
serializers.py
from rest_framework import serializers
from .models import Projects
class ProjectSerializer(serializers.ModelSerializer):
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
class Meta:
model = Projects
fields = ('id','name')
Django admin site permission for the user: []
Output
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"name": "proj 1"
},
{
"id": 2,
"name": "proj 222"
}
]
I don't know if it would help but, I am using custom User AUTH_USER_MODEL="users.Users"
Thanks in advance

Related

TypeError at / can only concatenate str (not "builtin_function_or_method") to str

Thank you for your time.
I was working on django rest framework documentation, and only with Localhost "http://127.0.0.1:8000/" URL I get this error.
TypeError at /
can only concatenate str (not "builtin_function_or_method") to str
I have attached Views.py, snippets/url.py and snippets/serializers.py
views.py
from snippets.permissions import IsOwnerOrReadOnly
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from rest_framework import generics, permissions
from django.contrib.auth.models import User
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import renderers
from rest_framework.reverse import reverse
#api_view(['GET']
def api_root(request, fromat=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})
class SnippetHighlight(generics.GenericAPIView):
queryset = Snippet.objects.all()
renderer_classes = [renderers.StaticHTMLRenderer]
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
snippets/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = format_suffix_patterns([
path('', views.api_root),
path('snippets/', views.SnippetList.as_view(), name='snippet-list'),
path('snippets/<int:pk>/', views.SnippetDetail.as_view(), name='snippet-detail'),
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view(), name='snippet-
highlight'),
path('users/', views.UserList.as_view(), name='user-list'),
path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail')
])
serializer.py
from rest_framework import serializer
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
from django.contrib.auth.models import User
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight',
format='html')
class Meta:
model = Snippet
fields = ['url','id','highlight', 'owner' ,'title', 'code', 'linenos', 'language',
'style']
class UserSerializer(serializers.ModelSerializer):
snippets = serializers.HyperlinkedIdentityField(many=True, view_name='snippet-detail',
read_only=True)
class Meta:
model = User
fields = ['url', 'id', 'username', 'snippets']
irrespective of this everything's working perfectly, like http://127.0.0.1:8000/snippets/

Password becoming unhashed even after saving to database

I have a generic APIView where I'm overriding the create method. This is for the Group model, where I've added a password field:
views.py
from django.contrib.auth.hashers import check_password, make_password
from django.contrib.auth.models import Group
from django.http import Http404
from rest_framework import generics
from rest_framework import status
from rest_framework.response import Response
from .permissions import AllowOptionsAuthentication
from .serializers import GroupSerializer
class GroupList(generics.ListCreateAPIView):
queryset = Group.objects.all()
serializer_class = GroupSerializer
permission_classes = [AllowOptionsAuthentication]
def create(self, request, *args, **kwargs):
data = request.data
data['password'] = make_password(data['password'])
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
print(Group.objects.get(name=data['d']).__dict__) # shows hashed password
print(serializer.data) # shows hashed password
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class GroupDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Group.objects.all()
serializer_class = GroupSerializer
permission_classes = [AllowOptionsAuthentication]
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
try:
# Grabs the 'name' parameter from the URL
group = queryset.get(name=self.kwargs['name'])
except Group.DoesNotExist:
raise Http404
if not check_password(self.request.data['password'], group.password):
raise Http404
group.user_set.add(self.request.user)
self.check_object_permissions(self.request, group)
return group
serializers.py
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id', 'name', 'password')
models.py
from django.contrib.auth.models import Group
from django.db.models import CharField, Model
Group.add_to_class('password', CharField(max_length=180, null=True, blank=True))
urls.py
from django.urls import path
from .views import GroupList, GroupDetail
urlpatterns = [
path('groups/', GroupList.as_view()),
path('groups/<str:name>/', GroupDetail.as_view()),
]
permissions.py
from rest_framework.permissions import IsAuthenticated
class AllowOptionsAuthentication(IsAuthenticated):
def has_permission(self, request, view):
if request.method == 'OPTIONS':
return True
return super(IsAuthenticated, self).has_permission(request, view)
settings.py
INSTALLED_APPS = [
'rest_framework',
'rest_framework.authtoken',
'djoser',
'corsheaders',
# ...
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
}
Inexplicably, after checking both the serializer.data and querying the database directly in the above print statements, I confirm that I have passed a hashed password into the column. However, whenever I query this afterwards, I see that the raw password has been saved.
I cannot explain how the password is becoming unhashed after saving the hashed password to the database. I am not writing to the Group model anywhere else.
But when I query this model after leaving the create method:
print(Group.objects.get(name='TestGroup').__dict__) # shows raw password

Setting different permissions for different kinds of requests on serializer based api (using router)?

I'm using DRF and working on a single URL for handling all user operations such as LIST, GET, POST, etc.
I have used a router but I'm unable to find how I should go about tweaking the permissions such as...
Even anonymous users can create users.
Only registered users (or admins, as an exception) can delete their own posts.
And so on..
urls.py
from django.urls import path, include, re_path
from .api import UserAPI
from rest_framework.routers import DefaultRouter
urlpatterns = [
# path('register/<int:pk>', UserAPI.as_view(), name='user_create'),
]
router = DefaultRouter()
router.register(r'user', UserAPI)
urlpatterns = [
] + router.urls
Serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.forms import ValidationError
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
write_only_fields = ('password',)
read_only_fields = ('id',)
extra_kwargs = {'last_name': {'required': True}}
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create(email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
)
user.set_password(validated_data['password'])
user.save()
return user
api.py
from rest_framework.viewsets import ModelViewSet
from django.contrib.auth import get_user_model # used custom user model
from rest_framework import mixins
from .serializers import UserSerializer
User = get_user_model()
class UserAPI(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Create a custom permission class and define what all things you need.
Example
from rest_framework.compat import is_authenticated
from rest_framework import permissions
class MyCustomPermissionClass(permissions.BasePermission):
def is_authenticated(self, request):
return request.user and is_authenticated(request.user)
def has_permission(self, request, view):
if view.action == 'create': # create new user by anyone
return True
if is_authenticated(request) and view.action == 'destroy' and request.user == post_created_by_user:
return True
# add all other conditions you want to implement
return False # default case
and add the permission class to your views
from .permissions import MyCustomPermissionClass
class UserAPI(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (MyCustomPermissionClass,)
One thing you should remember, unless a boolean True is returned from permission class, you won't get the permisson to access the corresponding view
Read more info about DRF- Custom Permissions
Update on 31-07-2018
rest_framework.compat.is_authenticated is depricated. request.user.is_authenticated will do the samething
from rest_framework import permissions
class MyCustomPermissionClass(permissions.BasePermission):
def is_authenticated(self, request):
return request.user and request.user.is_authenticated
def has_permission(self, request, view):
if view.action == 'create': # create new user by anyone
return True
if self.is_authenticated(request) and view.action == 'destroy' and request.user == post_created_by_user:
return True
# add all other conditions you want to implement
return False # default case
permissions.py
from rest_framework.permissions import BasePermission
class UserPermissions(BasePermission):
def has_permission(self, request, view):
# request.user.is_authenticated to make sure user is authed
# view.action == 'create' will allow Even anonymous users can create users.
return request.user.is_authenticated or view.action == 'create'
def has_object_permission(self, request, view, obj):
# obj == request.user make sure the changeing(update/read/delete) user is itself and only itself.
# if you want authed user can read and only myself can update\delete ,change it to obj == request.user or view.action == 'retrieve'
return obj == request.user
views.py
class UserAPI(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (UserPermissions,)

django ApiView login returns 404

I am having problems with my login API view. When trying to login I get a POST 404 but I know the url path is correct because if I put a simple GET request in the view it returns data. There are still some rough parts of my code but I thought this would work.
login.py
from django.contrib.auth import authenticate
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from apiV1.v1.accounts.models.user import User
from apiV1.v1.accounts.serializers.user import UserSerializerLogin
# login
class LoginView(APIView):
authentication_classes = ()
permission_classes = ()
#staticmethod
def post(request):
user = get_object_or_404(User, email=request.data.get('email'))
user = authenticate(username=user.email, password=request.data.get('password'))
if user:
serializer = UserSerializerLogin(user)
return Response(serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializerLogin(UserSerializer):
token = serializers.SerializerMethodField()
#staticmethod
def get_token(user):
"""
Get or create token
"""
token, created = Token.objects.get_or_create(user=user)
return token.key
class Meta:
model = User
fields = ('id', 'email', 'first_name', 'last_name', 'profile', 'role', 'token')
POST
{
"email": "admin",
"password": "password"
}

"detail": "CSRF Failed: CSRF token missing or incorrect."

I'm making RESTful API using Tastypie, and when I try to POST/PUT/DELETE a request it says:
"detail": "CSRF Failed: CSRF token missing or incorrect.".
It works fine with GET. I've read various threads on SO, saying:
to delete the cookies
or use #csrf_exempt
or use #method_decorator(csrf_exempt)
but neither of it worked.
How can I over-pass this error?
views.py
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
request._dont_enforce_csrf_checks = True
print request.DATA
return self.create(request, *args, **kwargs)
serializer.py
from django.forms import widgets
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
urls.py
from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = patterns('',
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
)
urlpatterns = format_suffix_patterns(urlpatterns)
Change rest_framework default permissions to AllowAny in settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
...
}