I'd like to generate a random 1-time passcode for a user that appears as a disabled field in a Django form.
The passcode will be shown in the form to the user on the page, and the user will not be able to edit it, but it will be submitted with the form. I do not want my users to set their own passcodes, but I want to tell them what their passcode is.
What is the best way to ensure that the 1-time passcode that is generated for users is not edited by users? I have been using session variables to remember the value so that I can confirm it has not been tampered with upon submit.
def my_page(request):
# If submitted
if request.method == "POST":
# Check to make sure passcode wasn't altered
if (request.POST.get('passcode') == request.session['pw']):
... rest of function here ...
# Generate passcode and store it the session
request.session['pw'] = genRandomPassword()
# Init the form with a passcode
form = MyForm(initial={'passcode': request.session['pw']})
return render(request, 'example.html', {'form': form})
Is there a better method for accomplishing what I am attempting to do? I think what I am doing works, as long as users can't edit their session variables. It does break if a user opens up another tab, as then a second passcode session variable will be generated.
I do not want to store the passcodes in a database.
I think instead of checking request.POST.get('passcode') == request.session['pw'] simply ignore the data inside POST and use data from session so even a user somehow changes the password you won't care about it (note that you don't even need to add pw to the form, it can be a template context variable)
Note: I personally think your solution is not a good idea, do not generate a password before submit, let the user fill the form and after successful submit generate their random password, after submitting show a page to user saying your form was submitted and your password is blah blah
Note 2: problem with your solution is assume I open a page and my generated password is 1 then I open a new tab and my generated password is 2, (and now 2 is stored in session as pw) I go and fill the form on the first page and submit it. what happens now? with your sample code you have to show an error to user, but user didn't to anything wrong, and with my fix (first paragraph) user thinks her password is 1 but it's actually 2
Related
I have created a page for review form. All users can fill out the form, but only logged in users can submit the form. If users is not logged in, they will be redirected to the login page. After they login, they will be redirected to the profile page.
So the flow will be like this :
User fills out the form > click the submit > redirected to login page > user login and redirected to profile page (at the same time, the form they have filled in is automatically saved)
I want the form they have filled in automatically saved after they login. How to do that?
My idea is to create a session that saves the form temporarily, then save to database after they login. But I'm confused how to write the code
Can anyone explain a bit what a django session is like? and how to write code to handle this problem?
You can try something like,
1 User fills out the form and hits submit
2 in the POST view where you handle the form, use the "**is_authenticated**" function and,
a)if the user is authenticated you handle the form as usual...
b)else set the contents of the form into a session variable in the views and redirect to the login page like,
request.session['review_body'] = request.post.get(the_form_body_field)
3 as per what you've said, after login it goes to profile page and form is submitted...
a)so in views where you serve the profile page, check if the session variable containing form data's exist and has values
b)if yes, directly save the contents from your views and clear the session data
I have created unique user pages for each person who registers to this social app by using the following:
#app.route("/profile/<username>", methods=["GET", "POST"])
def profile(username):
user = session["user"] or None
# grab the session user's username from db
username = mongo.db.users.find_one(
{"username": session["user"]})["username"]
user_profile = mongo.db.users.find_one({"username": user})
if session["user"]:
return render_template("profile.html", username=username, profile=user_profile)
return redirect(url_for("login"))
This creates a unique username profile built up from edited data that the user puts in. But I want to be able to view that profile from another user's login and then add them as a friend (if I choose).
Because I already have a URL created /profile/ do I need to create a new #app.route to something such as view_profile/<username>?
From there, I would use the Post and Get methods so if someone wanted to add that user as a friend they could click on that. From there, I think I would need the other user to accept the friend request? Not quite sure how to get to that just yet. But one step at a time I guess.
I started with the idea of a view_profile #app.route but now I am not 100% sure which direction to take it that will make the next stages also easy.
I am trying to implement a page using Django where the user is able to create content, hit submit, and then is prompted to register or login if they are not currently logged-in. One of the fields in the model for the content references the user, so they must be logged-in to create it, however I want them to be able to input all of the data before they are prompted with logging-in. I have used the #loginrequired decorator on the view, but that forced the user to be logged-in before they create the content. Here is the view currently:
def search(request):
form = LocationInput()
if request.method == "POST":
form = LocationInput(request.POST)
if form.is_valid():
t = Trip(trip_name = form.cleaned_data['city'])
t.user = request.user
t.save()
form.save()
l = Location.objects.get(google_id=form.cleaned_data['google_id'])
l.trips.add(t)
l.save()
return HttpResponseRedirect('/trips/')
return render(request, 'frontpage/search.html', {'form': form})
It loads the form, creates an object that needs a user associated with it so I need to register the user but keep the data from LocationInput(request.POST) until after the user has registered. Any help would be greatly appreciated!
I can see two options:
Allow NULL for user reference, save the content in a state "pending user login or sign up", and store the ID of the content object in a session. Vacuum "old" contents with no user reference on a regular basis.
Save the whole data of the form in a session.
I like the first one better since you, as a superuser, have access to the content even if the user didn't login or signup, and if the user contact you later telling you he had troubles signing in, you will be able to help and recover the content he submitted. While if everything is stored in session, once the session is deleted, it's all lost.
I'm aware that there is a context variable called "next" which allows you to specify the URL to redirect to after a page, which is commonly used when you want to redirect a user to a login page, then redirect them back to the page they were at before the login page once they've logged in.
In my views I've resorted to manually setting it via the URL such as redirecting to /login/?next=/someurl, but this isn't a clean way. I've tried googling and such, but there is surprisingly little information about it online.
How exactly do you set the context variable "next"? My site has a form that anyone can see, but only logged in users can submit. Right now if the user isn't logged in, they will get redirected to the login page with the "?next=/someurl/" attached to it so they get sent back to the original form once they log in.
However, from the login page there is a link to the sign up page for users who don't have an account, and I want to set "next" to be the original form page so that after they sign up they are redirected back to the original form. Any ideas or advice?
It sounds like you want to not simply use next for one redirect, but persist it across two redirects:
Some form page -> login -> signup -> Get back to some form
For the login page by itself, Django provides some automatic help for this (if you use the built-in auth views). But the second hop to the signup page requires some custom code explained below.
If you are using Django's built-in login view, and have created your own template for it, then Django will pass the current value of next to the template as a context variable called (appropriately) next. You can use that in the link to your sign-up page like this:
Sign me up!
Consequently, in the view you create to handle your user signup page, the value of next will be accessible as a GET param:
def signup(request):
if request.method == 'GET':
next = request.GET.get('next', None)
if next:
# Add it as a hidden input in your signup form
# Or re-append it as a GET param
# Or stick it in the user's session, which is
# what this example does:
request.session['next'] = next
Finally, in the same signup view, when you respond to the POSTed signup form, retrieve the next value in whichever way you chose to propogate it in the GET request, and redirect to it's value.
def signup(request):
....
# POST handling section
if signup_form.is_valid():
next = request.session.get('next', None)
if next:
# See caution note below!
return redirect(next)
Caution:
Be aware that you should check and sanitize the next value before you redirect to it after processing the signup form, to prevent browser-side tampering. For example, it's common to validate that the URL belongs to your own domain (if that's appropriate) and/or that Django's resolve function is able to successfully resolve it.
I've seen Django's samples and I can see they have decent error handling. However I want to see if there is yet a better approach, a general pattern to handle form validation errors in Django. This is the sample I found here:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
In particular, I was wondering:
How can the view in "/thanks/" be sure that the form was validated? Are there any common ways to pass the successful validation of the form to the next view? Or do I need to do something manually such as setting a flag in request's session?
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
EDIT: With regards to #1 I am referring to cases like user manually entering the '/thanks/' url or going back and forth through history pages and accidentally openning it without any form being validated. (Do we still show the "thanks" page? or we need to somehow re-validate why we are in thanks view).
The view can be sure that the form is validated because it will only be called if the form is valid...
If the page is generated through a post request the browser will always ask you that when hitting refresh... I guess the only way to avoid this would be redirecting to another page!
How can the view in "/thanks/" be sure that the form was validated?
form.is_valid() should thoroughly check any field or - if necessary - any combination, cornercase, etc. That's basically it. The views knows, the form was valid if it renders. There is no need to include redundant information in the session.
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
I am not sure what the point would be. The form contains errors and the user may correct them or leave. To render a page that would not ask for form resubmission, one could use a redirect, just as in the valid case. The error markup would have to be done manually in that case.