How do you secure django hidden input FORM from malicious intent? - python

Lets say I have the following form rendered in a template :
# Form
class CandidatTourForm(models.ModelForm):
class Meta:
model = Candidat
fields = [
"email", #charField
"first_name", #charField
"last_name", #charField
"dossier", #foreignKey
]
# parent Model
class Candidat(models.Model):
email = models.EmailField()
first_name = models.CharField(max_length=64, blank=True, null=True)
last_name = models.CharField(max_length=64, blank=True, null=True)
dossier = models.ForeignKey(Dossier, on_delete=models.CASCADE)
It is a classical form, nothing fancy. the dossier field is a hidden input.
def candidat_form_tour(request, dossier_uuid, candidat_id):
dossier = get_object_or_404(Dossier, uuid=dossier_uuid)
candidat = get_object_or_404(Candidat, id=candidat_id, dossier__uuid=dossier_uuid)
if request.method == "GET":
form = CandidatTourForm(instance=candidat,)
elif request.method == "POST":
form = CandidatTourForm(request.POST)
if form.is_valid() and assertDataIsLegit(form._meta.model): # see below
candidat = form.save()
return redirect(
"main:document_tour", dossier_uuid=dossier_uuid, candidat_id=candidat_id
)
return render(
request,
"candidature/candidat_form_tour.html",
{
"form": form,
"candidat": candidat,
"dossier_uuid": dossier_uuid,
},
)
When I test this view with POSTMAN and put a dossier_id that has nothing to do with the one received in the form on the GET request, the candidat is saved to the a wrong dossier.
I though about creating a helper function like so
def assertDataIsLegit(_object, _id, _id2):
_type = type(_object)
instance = _type.objects.get(id=_id)
return instance.id == _id2
and calling it to verify that the dossier id sent back by the post request match the original dossier:
if form.is_valid() and assertDataIsLegit(form._meta.model, dossier.id, form.cleaned_data['dossier']):
#do stuff save and all
But I'm pretty sure a better method exists.
I've looked into the form's clean method, but since I need to verify related data im not sure it is the good way.
How would you prevent this ? I know you are not supposed to trust the data sent and verify it.
Would you verify it in the view function ? In a form method ?
Any lead is welcomed !

Related

Accessing the attributes/elements of a form in Django

Hello so I've a form and I'm trying to make it so that when that form is validated. But before it is saved. It will create a new instance of a EC_Node and save the command given in the Command_Form not only to the Command_Node but to the EC_Nodes. Which should have a manytoone relationship with the Command_Nodes and which should record all commands send to a command_node. But this will only work if when entering a command for the Command_Node I also capture that in a EC_Node. Which I don't know how to do exactly although I'm some ideas.
So any advice would be greatly appreciated. Relevant Code follows.
Relevant views.py
def update(request, host_id):
host_id = Command_Node.objects.get(pk=host_id)
form = Command_Form(request.POST or None, instance=host_id)
if form.is_valid():
# Original suggestion was command_form = Command_Form.objects.first()
command_form = form['host_id']
command_form.EC_Node_set.all() # <- not sure with all _ and Maj here
form.save()
return redirect('home')
return render (request, 'update.html', {'host_id':host_id,'form':form})
forms.py
class Command_Form(ModelForm):
class Meta:
model = Command_Node
fields = ('host_id','current_commands')
host_id = forms.ModelChoiceField(
required=True,
queryset=Beacon.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'form-control'
},
)
)
current_comamnds = forms.ChoiceField(
required=True,
choices=CHOICES
)
def save(self, **kwargs):
EC_Node.objects.create(
command=self.cleaned_data["current_commands"],
Command_node=self.instance
)
return super().save(**kwargs)
models.py
class Command_Node(models.Model):
host_id = models.ForeignKey(Beacon, on_delete=models.CASCADE)
current_commands = models.CharField(choices=CHOICES, max_length=50, null=True)
def __str__(self):
return str(self.host_id)
class EC_Node(models.Model):
Command_node = models.ForeignKey(Command_Node, on_delete=models.DO_NOTHING)
command = models.CharField(choices=CHOICES, max_length=50, null=True)
def __str__(self):
return str(self.Command_node)
When I try to update things, via the update-view the present error I'm getting is:
'BoundField' object has no attribute 'EC_Node_set'
Thus my question on here. Thanks.
Forms validated data are accessible in the Form.cleaned_data attribute. See the documentation.
So you need to access your user data using this dictionary:
def update(request, host_id):
host_id = Command_Node.objects.get(pk=host_id)
form = Command_Form(request.POST or None, instance=host_id)
if form.is_valid():
# Original suggestion was command_form = Command_Form.objects.first()
command_form = form.cleaned_data['host_id']

question about forms, how do i get instant user in a form field pyhton django

i have build some user to user message function. i have sender, receiver and text see below. The user now must choose his email and then the email where the message should go.
But what i want is that the user dont need tho choose it self i need a form function that query instantly request.user. but i dont know how to implement that on form. And that the user is not shown in the receiver list. Srry for my bad english hope you understand me.
views.py
def mailEMployee(request):
mail = Mailsysthem.objects.filter(ontvanger=request.user)
receiver = Mailsysthem.objects.filter(ontvanger=request.user)
sender = Mailsysthem.objects.filter(zender=request.user)
user = CustomUser.objects.filter(email=request.user)
form = Mailsythemform()
if request.method == 'POST':
form = Mailsythemform(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('mail')
context={
'form':form,
'receiver':receiver,
'sender':sender.all,
'mail':mail,
'user':user
}
return render(request,'emp/mail.html', context)
Forms.py
class Mailsythemform(forms.ModelForm):
class Meta:
model= Mailsysthem
fields= ['zender','ontvanger','subject','text']
models.py
class Mailsysthem(models.Model):
zender = models.ForeignKey(to=CustomUser, null=True, on_delete=models.SET_NULL,related_name='Zender')
ontvanger = models.ForeignKey(to=CustomUser, null=True, on_delete=models.SET_NULL,related_name='Ontvanger')
subject = models.CharField(null=True, max_length=200)
text = models.TextField(max_length=300, null=True, blank=True, verbose_name='Bericht')
date = models.DateTimeField(auto_now_add=True, blank=True)
solftdelete = models.BooleanField(default=False)
mail_opened = models.BooleanField(default=False)
url.py
path('mailemployee/', views.mailEMployee, name='mail'),
You need to provide initial value to your ModelForm (docs) as following:
form = JournalForm(initial={'sender': user.id})

Show last uploaded file details on redirected page - Django

folks. In my application user uploads his/her document on upload_document page, submits the form and he/she should see details (for example, document name, document author and etc.) on redirected result page. Now I don't know where should I get document itself from POST request. Or I'm entirely in wrong way. Here are my codes.
views.py:
#login_required(login_url='sign_in')
def upload_document(request):
context = {}
form = UploadDocumentForm()
if request.method == 'POST':
form = UploadDocumentForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
return redirect('result')
context = {
'form':form
}
return render(request, 'upload_document.html', context)
#login_required(login_url='sign_in')
def result(request):
# Don't know what to do here.
return render(request, 'result.html', context)
models.py:
class OriginalDocument(models.Model):
document = models.FileField(upload_to='static/original_document', blank=False)
document_title = models.CharField(max_length=300)
student_name = models.CharField(max_length=100)
teacher_name = models.CharField(max_length=100)
document_type = models.CharField(max_length=100)
university = models.ForeignKey(University, on_delete=models.CASCADE, null=True, blank=True)
date_added = models.DateTimeField(auto_now_add = True)
checked_by = CurrentUserField()
def __str__(self):
return self.document_title
As a user, I'm using django's built-in user.
Finding solution with the help of #k33da_lets_debug become so easy. I've changed query to this:
last_uploaded = OriginalDocument.objects.latest('id')
And that's all.

How to edit user profile in django rest framework from two models and save the change

I am trying to create an endpoint to edit both the user model and custom profile model below.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500)
location = models.CharField(max_length=50)
image = models.ImageField(default='default.jpg', upload_to='profile')
In the regular django I would do:
views.py
def edit_profile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
extended_profile_form = ProfileForm(request.POST,
request.FILES,
instance=request.user.profile)
if form.is_valid() and extended_profile_form.is_valid():
form.save()
extended_profile_form.save()
return redirect('accounts:profile')
else:
form = EditProfileForm(instance=request.user)
extended_profile_form = ProfileForm(instance=request.user.profile)
context = {
'form':form,
'extended_profile_form':extended_profile_form
}
return render(request, 'accounts/edit-profile.html', context)
what is the equivalent for django rest framework?
I have tried:
views.py (Django Rest Framework)
#api_view(['GET','PUT'])
def profile(request):
if request.method == 'GET':
user = User.objects.filter(username=request.user)
profile_user = Profile.objects.filter(user=request.user)
serializer_user = UserSerializer(user, many=True)
serializer_profile_user = ProfileSerializer(profile_user, many=True)
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result)
elif request.method == 'PUT':
user = User.objects.filter(username=request.user)
profile_user = Profile.objects.filter(user=request.user)
serializer_user = UserSerializer(user, data=request.data)
serializer_profile_user = ProfileSerializer(profile_user, data=request.data)
if serializer_user.is_valid() and serializer_profile_user.is_valid():
serializer_user.save()
serializer_profile_user.save()
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result)
result = {'serializer_user': serializer_user.data, 'serializer_profile_user': serializer_profile_user.data}
return Response(result.errors, status=status.HTTP_400_BAD_REQUEST)
When I am browsing the endpoint, it does display serializer_user and serializer_profile_user data but I am unable to edit any of those data using the DRF browsable API.
Am I right thinking the codes above is the equivalent of the codes from the codes from the normal django to edit the profile of the user?
It looks fine to me, but you need to replace this:
if request.method == 'GET':
user = User.objects.filter(username=request.user)
with this:
if request.method == 'GET':
try:
user = User.objects.get(id=request.user.id)
except User.DoesNotExist:
return Response(data='no such user!', status=status.HTTP_400_BAD_REQUEST)
# you need to use objects.get because objects.filter returns a queryset not an abject
Because, request.user is an instance of User model, you cannot compare it to an attribute of user (in your case username)
PS: same goes with your PUT method as well.
Hope this helps!
Look. You can make it easier. Let's take Post model (for example):
class Post(models.Model):
author = models.ForeignKey(base.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
text = models.TextField()
likes = models.ManyToManyField(base.AUTH_USER_MODEL, blank=True, related_name='post_likes')
created_date = models.DateTimeField(default=timezone.now)
And that You should describe it in your serializer (serializer is something similar to DTO. It converts data into a service-friendly JSON view):
class PostCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'text']
And the last part - Endpoint:
class PostUpdateView(UpdateAPIView):
serializer_class = PostCreateUpdateSerializer
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
It will be more comfortable to use CBV for Django and DRF
And One more thing. You shouldn't create one more table for your user model. This is due to the extension of the BaseUser model. Link for help

Model Choice Field - get the id

I am busy trying to get the id only in integer format preferably for the ModelChoiceField. I get the list to display but get's returned in a string format. Please helping me in retrieving the id of ModelChoiceField. I think I need to do this in the view.
forms.py
class ProjectForm(forms.ModelForm):
items = forms.ModelChoiceField(queryset=Project.objects.all())
class Meta:
model = Project
fields = ['items']
models.py
class Project(models.Model):
items = models.IntegerField(default=0, blank=True, null=True)
views.py
def ProjectView(request):
form = ProjectForm(request.POST)
if request.method == 'POST':
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
return HttpResponseRedirect('/')
else:
form = ProjectForm()
return render(request, 't.html', {'form': form })
From what I can tell, items should never be an IntegerField. Your usage has it set up to be a ForeignKey to a Project so you should just make that explicit
items = models.ForeignKey('self', null=True, blank=True)
Possibly with a better descriptive name than items.
Then, you don't need to define anything on the form, it just becomes a standard model form, with a standard model form usage.

Categories

Resources