Why all permissions checks? - python

I am new in django and drf
in my project I have two group of permissions
1.normal_user group : with view_issue,view_project,view_analyzeissue
2.manager_user : with all permission as possible
i have some views that check some permissions
for example IssuesViewApi view, this view need to NormalUserPermissions
so i created new group with composition of permissions in my tests and send request to the view
my new group have view_issue,change_issue
when i send request to the IssuesViewApi i get 403 response
i have a NormalUserPermissions class
class NormalUserPermissions(permissions.BasePermission):
def has_permission(self, request: Request, view):
if request.user.has_perms(get_group_permissions("normal_user")):
return True
return False
class IssuesViewApi(generics.ListAPIView):
class IssueFilter(FilterSet):
labels = CharFilter(field_name="labels", lookup_expr='contains')
project_id = NumberFilter(field_name="project__id", lookup_expr='exact')
user_name = CharFilter(field_name="users__username", lookup_expr='exact')
start_date = DateFilter(field_name="updated_at", lookup_expr='gte')
end_date = DateFilter(field_name="updated_at", lookup_expr='lte')
class Meta:
model = Issue
fields = ["iid", 'is_analyzed', 'project_id', 'labels', 'user_name', 'start_date', 'end_date']
permission_classes = [IsAuthenticated, NormalUserPermissions]
http_method_names = ['get']
pagination_class = StandardPagination
queryset = Issue.objects.all()
serializer_class = IssueSerialize
filter_backends = [OrderingFilter, DjangoFilterBackend]
filterset_class = IssueFilter
ordering_fields = ['iid', 'weight'] # order fields depend on user request
ordering = ['iid'] # default order value
def get(self, request, *args, **kwargs):
response = super(IssuesViewApi, self).get(request, *args, **kwargs)
return Response({
'data': {
'issues': response.data['results'],
},
'paginationInfo': {
"count": response.data['count'],
"next_page": response.data['next'],
"previous_page": response.data['previous'],
"total_pages": self.paginator.page.paginator.num_pages
}
})
def test_create_custom_group_and_filter_issues_and_update_issue(self):
self.run_fake_discovery()
user = self.get_user()
user.groups.add(Group.objects.get_by_natural_key("manager_user"))
self.login(username=user.username, password="123456789")
add_group_url = reverse('group-add')
group_name = "new_group"
group_permissions = list(Permission.objects.filter(codename__in=['view_issue', 'change_issue']).all().values_list('id', flat=True))
response = self.client.post(add_group_url, data=json.dumps({'name': group_name, 'permissions': group_permissions}), content_type=self.CONTENT_TYPE)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertTrue(Group.objects.filter(name=group_name).exists())
sync_user_groups_url = reverse('sync-users-and-groups')
test_user = User.objects.get(username='testuser')
response = self.client.post(sync_user_groups_url, data=json.dumps({'group_name': group_name, 'users': [test_user.id]}), content_type=self.CONTENT_TYPE)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(test_user.groups.filter(name=group_name).exists())
response=self.logout()
self.assertEqual(response.status_code,status.HTTP_200_OK)
self.login(username=test_user.username, password='123456789')
filter_issue_url = reverse('issue-filter')
filter_issue_response = self.client.get(filter_issue_url, data={'username': user.username}, content_type=self.CONTENT_TYPE)
self.assertEqual(filter_issue_response.status_code, status.HTTP_200_OK)
Why all permissions checks ?
Im going to when user has view_issue permission then get response with 200 status

Related

how to upload files in django backend?

I am trying to create an upload file function in my website. However , i keep getting the error
The submitted data was not a file. Check the encoding type on the form. I tried to create a post function in views.py. Does this mean any files that i uploaded is not considered a file ?
models.py
class ClinicVisit(models.Model):
upload_attachment = models.FileField(null=True , blank=True, upload_to='uploads/')
views.py
class ClinicVisitList(generics.ListCreateAPIView):
serializer_class = ClinicVisitSerializer
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_class = ClinicVisitFilter
permission_classes = [permissions.IsAuthenticated,]
pagination_class = LargeResultsSetPagination
ordering = ['-created_at']
parser_class = (FileUploadParser,)
def get_queryset(self):
return ClinicVisit.objects.based_on_access_level(self.request.user)
def post(self, request, *args, **kwargs):
file_serializer = ClinicVisitSerializer(data=request.data)
print(request.data)
if file_serializer.is_valid():
file_serializer.save()
return response.Response(file_serializer.data, status=status.HTTP_200_OK)
else:
return response.Response(file_serializer.errors,status=status.HTTP_400_BAD_REQUEST)
serializers.py
class ClinicVisitSerializer(serializers.ModelSerializer):
upload_attachment = serializers.FileField()
class Meta:
model = ClinicVisit
exclude = clinicvisit_exclude
extra_kwargs = {
"staff_relationship": {"required": True},
"visit_type": {"required": True},
"visit_status": {"required": True},
}

DRF serializer doesn't save instance to the db

Views
class AuthDataViewSet(ModelViewSet):
queryset = AuthData.objects.all()
serializer_class = AuthDataSerializer
def create(self, request, *args, **kwargs):
serializer_data, headers = create_auth_data(self, request.data, {'request': request})
# returning response with the data
create_auth_data function
def create_response_data(view, data: dict = None, context: dict = None):
# I calling the viewset methods below
serializer = view.get_serializer(data=data, context=context)
serializer.is_valid(raise_exception=True)
view.perform_create(serializer)
headers = view.get_success_headers(serializer.data)
return serializer.data, headers
Serializer
class AuthDataSerializer(ModelSerializer):
class Meta:
model = AuthData
fields = ('login', 'password', 'project', 'manager')
def create(self, validated_data):
validated_data['manager'] = self.context['request'].user.manager
return validated_data
I got the correct serializer.data, no errors and pure data, but the instance didn't saved to the database.
Give this a try:
class AuthDataSerializer(ModelSerializer):
class Meta:
model = AuthData
fields = ('login', 'password', 'project', 'manager')
def create(self, validated_data):
auth_data = AuthData.objects.create(**validated_data)
return auth_data

Django/DRF backend not receiving complete data from POST request

I'm trying to send data to my Django/DRF backend via Axios POST requests. Some of them have been working, but some of them haven't been. This snippet, for example, works fine:
axiosInstance.post(
`/api/notes/`,
{
title,
content: serialize(editorContent),
}
)
This one, however, has been causing problems:
axiosInstance.post(
`/api/notebooks/`,
{
name: newNotebookName,
user,
}
)
In the second snippet, the name attribute gets through to the backend, but for some reason the user attribute does not. I tried using console.log to see if user is undefined at the moment it's sent off to the backend and it isn't.
I tried to change the way I define the keys in the object I send to the backend so that they're not dynamic like in this answer and the result hasn't changed.
The user variable is a string containing an email address, like 'foobar#gmail.com'. I thought maybe Django blocked it by default for security reasons, but I split the string at the # and tried sending the first part only and it didn't work either.
I also tried defining a new variable so that the type of user is string rather than string | undefined and it still doesn't work. The value of title is also string and it gets through to the backend (it's defined in the validated_data object my serializer sees in its create() method). So I'm at a loss as to what this could be. Here are some of my files:
axiosAPI.ts
import axios from 'axios'
const baseURL = 'http://localhost:8000/'
export const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
'xsrfHeaderName': 'X-CSRFTOKEN',
'xrsfCookieName': 'csrftoken',
'Authorization': localStorage.getItem('access_token') ? 'JWT ' + localStorage.getItem('access_token') : null,
'Content-Type': 'application/json',
'accept': 'application/json',
}
})
axiosInstance.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config
// Prevent infinite loops if login credentials invalid
if (error.response.status === 406 && originalRequest.url === baseURL + 'auth/token/obtain') {
window.location.href = '/login/'
return Promise.reject(error);
}
// Respond to expired refresh tokens
if (error.response.data.code === 'token_not_valid' && error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken) {
const tokenObj = JSON.parse(atob(refreshToken.split('.')[1]))
const currentTime = Math.ceil(Date.now() / 1000)
if (tokenObj.exp > currentTime) {
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest);
})
.catch(err => {
console.log(err)
throw err;
})
} else {
console.log('Refresh token is expired.')
window.location.href = '/login/'
}
} else {
console.log('Refresh token not available.')
window.location.href = '/login/'
}
}
// Respond to invalid access tokens
if (error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest)
}).catch(err => {
console.log(err)
throw err;
})
}
}
)
notebooks/serializers.py
import bleach
import json
from rest_framework import serializers
from authentication.models import ErsatzNoteUser
from .models import Note, Notebook
class NoteSerializer(serializers.ModelSerializer):
note_id = serializers.SlugField(source='id', read_only=True, required=False)
title = serializers.JSONField(required=False)
content = serializers.CharField(required=False)
notebook = serializers.PrimaryKeyRelatedField(read_only=True, required=False)
date_modified = serializers.DateField(read_only=True, required=False)
date_created = serializers.DateField(read_only=True, required=False)
def create(self, validated_data):
title = json.dumps(validated_data['title'])
# Workaround to fix a currently unpatched bug in Slate
# that occurs when an editor's contents begin with a list
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
response_data = {
'title': title,
'content': content,
}
return Note.objects.create(**response_data)
def update(self, instance, validated_data):
instance.title = json.dumps(validated_data['title'])
# See the above comment in the 'create' method
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
instance.content = content
instance.save()
return instance
class Meta:
model = Note
fields = [ 'note_id', 'title', 'content', 'notebook', 'date_modified', 'date_created' ]
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
# notebook_data = {
# 'name': validated_data['name'],
# 'user': ErsatzNoteUser.objects.get(email=validated_data['user']),
# }
print(validated_data)
return Notebook.objects.create(name=validated_data['name'], user=ErsatzNoteUser.objects.get(email=validated_data['user']))
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
notebooks/views.py
import json
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import NoteSerializer, NotebookSerializer
from .models import Note, Notebook
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
def get_queryset(self):
return self.request.user.notes.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NoteSerializer(instance)
title = json.loads(serializer.data['title'])
response_data = {
'note_id': serializer.data['note_id'],
'title': title,
'content': serializer.data['content'],
'notebook': serializer.data['notebook'],
'date_modified': serializer.data['date_modified'],
'date_created': serializer.data['date_created'],
}
return Response(response_data)
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
notebooks/models.py
from django.db import models
from django.contrib.auth.models import User
from jsonfield import JSONField
from django.conf import settings
from .helpers import generate_slug
class Note(models.Model):
""" Represents an individual note. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
title = JSONField(null=True)
content = models.TextField(null=True)
notebook = models.ForeignKey('Notebook', related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
# Temporary expedients for the sake of development
if not self.notebook:
self.notebook = Notebook.objects.get(id='YyOzNhMFMPtN8HM')
super(Note, self).save(*args, **kwargs)
def __str__(self):
return self.id
class Meta:
ordering = [ '-date_modified', '-date_created', ]
class Notebook(models.Model):
""" A collection of individual notes. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
name = models.CharField(max_length=64, default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notebooks', on_delete=models.CASCADE)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
super(Notebook, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
ordering = [ '-date_modified', '-date_created', 'name' ]
Please don't hesitate to ask if you'd like further info.
EDIT: Changed NotebookViewSet so that it has a retrieve() method, the result hasn't changed:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NotebookSerializer(instance)
response_data = {
'name': serializer.data['name'],
'user': serializer.data['user'],
}
return Response(response_data)
In your example your NotebookViewSet class does not have a retrieve method.
It turns out that under the hood, DRF considers ForeignKey fields to be read_only. Meaning it's solely to be used for representing the object, and shouldn't be part of any create or update process. According to the documentation:
Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.
Source.
But this didn't matter to my database, which requires me to identify the user that corresponds to each new Notebook at the time of its creation, or else a NOT NULL constraint is violated. So I just overwrote the default ModelViewSet create() method to pass on the user string manually to my NotebookSerializer as part of context, allowing me to use it to get the corresponding user object in the database and assign it to the newly created Notebook. I went about it like this:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def perform_create(self, serializer):
serializer.save()
def create(self, request, *args, **kwargs):
serializer = NotebookSerializer(data=request.data, context={ 'request': request })
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
return Notebook.objects.create(
user=ErsatzNoteUser.objects.get(email=self.context['request'].user),
**validated_data
)
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
That's all it took.

Django API Testing : csrf exempt

I'm getting a "Forbidden (CSRF cookie not set.): /user/admin/sign-up" error whenever I test class based views. When I change those views to functional based views with #csrf_exempt on top of the function declaration, it works.
Postman POST Request:
localhost:8000/admin/sign-up
body : {'email' : 'email#gmail.com', 'password' : 123123}
URL path patterns:
...
path(‘/admin/sign-up’, views.AdminSignUpView),
...
Views.py
#csrf_exempt
def token_verification(request,**kwargs):
if request.method == “POST”:
id = kwargs.get(‘id’)
token = kwargs.get(‘token’)
user = User.objects.get(id = id)
redirect_url = ‘/eval/intro’
is_valid = user_activation_token.check_token(user,token)
if is_valid:
user.is_active = True
user.save()
return HttpResponseRedirect(redirect_url,status = 200)
else:
return HttpResponse(status = 403)
class AdminSignInView(View):
#csrf_exempt
def post(self,request):
data = json.loads(request.body)
try:
if User.objects.filter(name = data[‘email’]).exists():
user = User.objects.get(name=data[‘email’])
if bcrypt.checkpw(data[‘password’].encode(‘utf-8’),user.password.encode(‘utf-8’)):
token = jwt.encode({‘email’:data[‘email’]}, SECRET, algorithm = HASH).decode(‘utf-8’)
return JsonResponse({ ‘token’ : token }, status = 200)
return JsonResponse({ ‘message’ : ‘INVALID_USER’ }, status = 401)
return JsonResponse({ ‘message’ : ‘INVALID_USER’ }, status = 401)
except KeyError:
return JsonResponse({ ‘message’ : ‘INVALID_KEYS’ }, status = 400)
class AdminSignUpView(View):
#csrf_exempt
def post(self,request):
try:
data = json.loads(request.body)
if not User.objects.filter(email = data[‘email’]).exists:
password = bcrypt.hashpw(data[‘password’].encode(‘utf-8’),bcrypt.gensalt())
crypted = password.decode(‘utf-8’)
User.objects.create(
name = data[‘name’],
password = bcrypt,
email = data[‘email’],
auth_id = data[‘auth_id’]
)
return HttpResponse(status = 200)
except KeyError:
return JsonResponse({ ‘message’ : ‘INVALID_KEYS’ },status = 4000)
Models.py
class User(models.Model):
name = models.CharField(max_length = 50)
email = models.EmailField(max_length = 200,unique = True, blank = False)
department = models.ForeignKey('Department', on_delete = models.SET_NULL, null = True)
is_active = models.BooleanField(default=False)
question = models.ManyToManyField('eval.Question',through='UserQuestion')
auth = models.ForeignKey('Auth', on_delete = models.SET_NULL, null = True)
class Meta:
db_table = 'users'
token_verification view, which is written in function based, works fine but the last two raised an error. I think the fact that the decorator only goes on top of the function brings up this error, but I'm not sure why #csrf_exempt is necessary for some views.
I have no clue why I'm getting the csrf issue at this time.
I believe the problem is where you're adding the #csrf_exempt.
Django perform the csrf validation before it reaches post
You should check your Django version flowchart to find which method you should override on your View to add the csrf_exempt decorator.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#django.views.generic.base.View.setup
https://docs.djangoproject.com/en/1.8/ref/class-based-views/base/#django.views.generic.base.View
I believe the code bellow should work for django versions < 2.2
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(AdminSignInView, self).dispatch(request, *args, **kwargs)

django rest framework url arguments with patch not working

Edit
I suspect the whole problem with my UpdateApiView is with the url. No matter how I change it, will return 404.
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone'),
it returns
{
"detail": "Not found."
}
[18/Apr/2016 01:39:02] "PATCH /api/verify-phone/phone_id=00980 HTTP/1.1" 404 4941
Why?
views.py
class VerifyPhone(generics.UpdateAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.VerifyPhoneSerializer
allowed_methods = ['PATCH']
lookup_field = 'phone_id'
def get_queryset(self):
phone_id = self.request.query_params.get('phone_id', None)
queryset = User.objects.filter(phone_id=phone_id)
return queryset
def update(self, request, *args, **kwargs):
print('inside update')
print(request.data)
partial = kwargs.pop('partial', False)
instance = self.get_object()
print(instance)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
print(serializer)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
print('done perform update')
return Response(serializer.data)
serializers.py
class VerifyPhoneSerializer(serializers.ModelSerializer):
regex = r'\d+'
verification_code = serializers.RegexField(regex, max_length=7, min_length=7, allow_blank=False)
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
class Meta:
model = User
fields = ('verification_code', 'phone_id')
def validate(self, data):
verification = api.tokens.verify(data['phone_id'], data['verification_code'])
if verification.response.status_code != 200:
raise serializers.ValidationError("Invalid verification code.")
return data
def update(self, instance, validated_data):
instance.phone_number_validated = True
instance.save()
return instance
Second question Is this correct to get phone_id from the views?
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
Looking at your api url def, I think you should call:
/api/verify-phone/00980
instead of
/api/verify-phone/phone_id=00980
I also think something is wrong with the url def itself (the ^ before \d):
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
should be
url(r'verify-phone/(?P<phone_id>\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
or
url(r'verify-phone/(?P<phone_id>\d{5})$', view.VerifyPhone.as_view(), name='verify-phone')

Categories