How can I restrict user from accessing some specific urls? - django

I have created a view which allow user to delete a specific Booking provided with id but I dont want other user except the user associated with the Booking object to delete it. How I can restrict user to access urls which delete booking they not own.
The urls:
path('manage/', views.ManageView, name='manage'),
path('manage/delete_booking/(?P<pk>\d+)', views.delete_booking, name='delete_booking'),
The views:
#login_required
def delete_booking(request, pk):
booking = Booking.objects.get(pk=pk)
booking.delete()
return redirect('manage')
#login_required
def ManageView(request):
bookings = Booking.objects.filter(user=request.user)
context = {
'user': request.user,
'bookings': bookings
}
return render(request, 'timetable/manage.html', context)
Template:
<div class="w3-content">
<div class="w3-third w3-padding w3-center">
<h4 class="bg-color">Infomation</h4>
<p>Student ID: {{ user.profile.student_id }}</p>
<p>Full name: {{ user.last_name }} {{ user.first_name }}</p>
</div>
<div class="w3-two w3-padding w3-center">
<h4 class="bg-color">Booking</h4>
{% if not bookings.exists %}
<p>You don't have any booking</p>
{% else %}
{% for booking in bookings %}
<p>{{ booking.room }} {{ booking.date }} {{ booking.lesson.number }} Delete</p>
{% endfor %}
{% endif %}
</div>

You can add a check in the view, for example:
#login_required
def delete_booking(request, pk):
booking = get_object_or_404(Booking, pk=pk, user=request.user)
booking.delete()
return redirect('manage')
Here it will raise a 404 error (not found) in case it can not find an object with that pk, or the user is not request.user.
You can also check:
#login_required
def delete_booking(request, pk):
booking = get_object_or_404(Booking, pk=pk)
if booking.user_id != request.user.id:
return HttpResponse('Unauthorized', status=401)
booking.delete()
return redirect('manage')
This will return a 401 error (not authorized).
Both methods have its merits. For example by raising a 401, you give a "hint" that there exists an object with this pk. A malicious user could then try to access (view, alter, or delete) the object through another way. On the other hand a 401 describes better what the problem is: the user can not view/update/change the object. The frontend can give a more informative message. For example if a certain administrator has not enough rights to handle a certain object.
Note: usually only POST, PATCH, PUT, and DELETE methods should have side-effects. So you might want to omit the request.method == 'GET' case as well. Robots like search engines typically "assume" that GET requests are safe (well actually they should be safe). So by not checking the method, a search engine could "trigger" removal, updates, etc.

Related

why django username parameters passed to urls is connecting directly and how prevent it

I'm trying to access to a user profil if urls parameters is profile/username
it work but i have a security problem because user is directly connected.
How i can define in django user profil access depending if user is authenticated or not
profil.html
{% if user.is_authenticated %}
<p>{{ user.username}}</p>
edit your profile
{% else %}
<p>{{ user.username}}</p>
<p>basic profile of user</p>
{% endif%}
views.py
def profil(request,username):
user=get_object_or_404(User, username=username)
context = {
'user':user
}
return render(request, 'service/profil.html',context)
You can use the login required decorator. Just add it to your view:
from django.contrib.auth.decorators import login_required
#login_required
def profil(request, username):
...
I fixed my problem condition was
{% if request.user == requested_user %}
<p>{{ requested_user.username}}</p>
edit your profile
{% else %}
<p>{{ requested_user.username}}</p>
<p>basic profile of user</p>
{% endif%}

NoReverseMatch Django: can't find pattern

I'm building my first application (guess app) with Django, I've been doing well so far. But I've encountered an error when trying to redirect to a Detail View when a user submits a file through a submit function (similar to a 'post blog' scenario).
I've looked up several posts with the same problem and I can not figure out why my code is not working.
views.py
#login_required
def submit(request):
if request.method == 'POST':
submited_form = SubmitFileForm(request.POST, request.FILES)
if submited_form.is_valid():
...
form.save()
return HttpResponseRedirect(reverse('result-detail'), kwargs={'pk': form.pk})
else:
submited_form = SubmitFileForm()
return render(request, 'guess/submit.html', context)
class ResultDetailView(LoginRequiredMixin, DetailView):
model = Result
template_name = 'guess/result_detail.html'
context_object_name = 'result'
I know I'm mixing class based views with function based views, but for some reason I can not get my submit function to work when I try to implement it as a class based view. Anyway, I think that shouldn't be a problem
urls.py
url_patterns = [
...
path('result/<int:pk>', guess_views.ResultDetailView.as_view(), name='result-detail'),
...
]
result_detail.html
{% extends "guess/base.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ result.author }}</a>
<small class="text-muted">{{ result.date_posted }}</small>
</div>
<h2 class="article-title">{{ result.title }}</h2>
<p class="article-content">{{ result.statistic }}</p>
</div>
</article>
{% endblock content %}
I'd expect a redirect to the detail view of the object that has been created with the submit function and submitfileform (model form). I can access the details if I just type /result/ and the primary key of any created object. But apparently I can't do the same through a redirect.
The error I am getting:
NoReverseMatch at /submit/
Reverse for 'result-detail' with no arguments not found. 1 pattern(s) tried: ['result/(?P<pk>[0-9]+)$']
In your code, you have two problems. First, form is not a Model instance. When you call form.save(), it will return the model instance. So you need to store it in a variable. Second problem is that, you need to pass the kwargs as a known argument in the reverse, not in HttpResponseRedirect. So the following code should work:
instance = form.save()
return HttpResponseRedirect(reverse('result-detail',kwargs={'pk': instance.pk}))

django display message after POST form submit

I have a page with a POST form, that have a action set to some url.
i.e assume this page url is /form_url/ :
..
The view in /submit_url/ take care of the form data. After this, I want to return the same page of the form with a success message.
In the view that take care for the POST form, I use HttpResponseRedirect, in order to "clear" the form data from the browser.
But in this way I can't display a message in the form page, unless I do something like:
return HttpResponseRedirect("/form_url/?success=1")
and then check for this parameter in the template. I don't like this way, since if the user refreshes the page, he will still see the success message.
I've noticed that in django admin site, the delete/add of objects does use redirect after POST submit, and still display a success message somehow.
How?
I've already briefly seen django "messaging" app, but I want to know how it work first..
The django admin uses django.contrib.messages, you use it like this:
In your view:
from django.contrib import messages
def my_view(request):
...
if form.is_valid():
....
messages.success(request, 'Form submission successful')
And in your templates:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li>
{% endfor %}
</ul>
{% endif %}
For Class Based Views use self.request
I also use self.request.path_info in my return
from django.contrib import messages
class MyCreateView(CreateView):
...
def form_valid(self, form):
....
self.object.save()
messages.success(self.request, 'Form submission successful')
return HttpResponseRedirect(self.request.path_info)
Same template as damio's answer:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li>
{% endfor %}
</ul>
{% endif %}
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(SuccessMessageMixin, CreateView):
model = Author
success_url = '/success/'
success_message = "%(name)s was created successfully"
https://docs.djangoproject.com/en/1.11/ref/contrib/messages/
You don't need to do a redirect to clear the form data. All you need to do is re-instantiate the form:
def your_view(request):
form = YourForm(request.POST or None)
success = False
if request.method == 'POST':
if form.is_valid():
form.save()
form = YourForm()
success = True
return render(request, 'your_template.html', {'form': form})
If the user refreshes the page, they're going to initiate a GET request, and success will be False. Either way, the form will be unbound on a GET, or on a successful POST.
If you leverage the messages framework, you'll still need to add a conditional in the template to display the messages if they exist or not.
Django messages framework stores the messages in the session or cookie (it depends on the storage backend).
If you're using a classed based view with django forms, and granted you've got the messages.html template set up, you can simply pass the 'SuccessMessageMixin' into your view like below
class YourView(SuccessMessageMixin,View):
success_message = 'your message_here'
This will display your message upon form success

Request another user object in django

I am currently faced with a challenge, when a user gets authenticated they can see objects posted by other users they are following. The challenge here is I want the authenticated user to click on the name of a user following them and it automatically takes then to the a page containing all the posts by that user they clicked on. So far my code keeps showing me just the posts by the authenticated user only.
#Views.py
def article(request):
return render(request, 'article.html',
{'posts': post.objects.filter(user = request.user),'form' : CommentForm()})
#article.html
{% for post in posts %}
<div class = "username">
<font size="4.5">{{post.user}}</font>
</div>
<div class = "postbody">
<p>{{ post.body|lower|truncatewords:50 }}</p>
<p>{{post.likes}} people liked this article</a></p>
</div>
<p>likes {{post.likes}} </p>
{% endfor %}
{% endif %}
I appreciate any help thanks.
Change this line
post.objects.filter(user = request.user)
to filter on some other user object most likely like this
post.objects.filter(user__pk=user_id)
For a more complete solution your urls.py should look like this
url(r'^article/(?P<user_id>)/$', views.article),
in your views.py, accept the user_id as a new parameter
def article(request, user_id):
return render(request, 'article.html',
{'posts': post.objects.filter(user__pk=user_id),'form' : CommentForm()})

Django - Retrieve Unauthenticated User object

As the titles states, I have a need for users to retrieve User information of different users, whether or not they're authenticated.
I have a 'profile' template with some logic to either display static user information (for public) or editable user information (for the user it belongs to).
PROBLEM: The condition in my template is returning "true" - the User object appears to be authenticated BUT it's not. If I navigate from that page (profile) no user logged in.
I'm fairly new to Django. Am I not retrieving the User object correctly in my view.py?
views.py
def GetProfileById(request, userid):
try:
user = User.objects.get(pk=userid)
except User.DoesNotExist:
return HttpResponseRedirect('/')
try:
user_profile = user.get_profile()
except Exception:
return HttpResponseRedirect('/')
context = {'user': user, 'user_profile': user_profile}
return render_to_response('profile.html', context, context_instance=RequestContext(request))
profile.html
{% if user.is_authenticated %}
<form id="form-about" class="form-horizontal" action="{% url update-user-profile %}" method="POST">
{% csrf_token %}
<p>{{ about_form.first_name }}</p>
<p>{{ about_form.last_name }}</p>
<p>{{ about_form.birthday }}</p>
<p>{{ about_form.profession }}</p>
<button class="btn btn-primary" type="submit">Save</button>
</form>
{% else %}
<p><b>Name:</b> {{ user.first_name }} {{ user.last_name }}</p>
<p><b>DOB:</b> {{ user_profile.birthday }}</p>
<p><b>Profession:</b> {{ user_profile.profession }}</p>
<p><b>Member since:</b> {{ user.date_joined|date:'Y-M-d' }}</p>
<p><b>Last login:</b> {{ user.last_login|date:'Y-M-d # H:i' }}</p>
{% endif %}
It seems like, what you're trying to do is, if a user is currently logged in, you want to show a form that lets them update a profile.
So the problem is:
In your view, you're setting user to a User object from the database.
This object does not represent or contain information about whether the current user is logged in. It's just the record from the database.
You want to check request.user, which represents the User object for the user who requested the web page.
So in your template, change:
{% if user.is_authenticated %}
to:
{% if request.user.is_authenticated %}
I believe you're confusing user and request.user. The former is the user being displayed, the latter is the user that is currently logged in, or an AnonymousUser if no one is logged in.
You want to display that form if the user that is currently logged in and the user being displayed are the same, right? Then just change
{% if user.is_authenticated %}
To
{% ifequal user request.user %}