Ruby on rails Password is visible in url as parameter - ruby-on-rails

I have an initial login screen where we can enter a "username" and "password".
I have four views and I am using the "username" and "password" entered in the first login view to establish an SSH connection in the fourth view.
To pass and use the username and password, I'm passing them through a hidden_field_tag in all these views, and when we click on the submit button, these hidden field values are passed to the second view.
All these submits are get requests.
The username and passwords are appearing in the URL as I am passing them as parameters.
If I use a post request instead of a get, the page expires when I click on the back button in my browser.
Is there anyway to hide/encrypt those parameters in the URL?
Thanks.

I don't think you're going to be able to hide them as we know GET exposes the params. I'm wondering if you can store them in the session instead? I don't know what you're doing off hand but the logic seems a bit opaque. Maybe you can re-think the logic into more of a common pattern. Good luck!

You really shouldn't be passing the username and password as a get request. If you don't want the page to expire as a post, I suppose what you could do something which would send the username and password via AJAX post request and the rest with a get request.

You really shouldn't be passing the username and password as GET requests. I would recommend instead having another form button to go back in the 4-step process so that the whole process is done by POST whichever direction you are travelling, this would also ensure the user doesn't lose data when going to a previous page.
<%= form.submit 'Previous Step' %>
<%= form.submit 'Next Step' %>
Controller:
if params[:commit] == "Next Step"
object.update_attributes(params[:item])
<..logic..>
end

What's wrong with the page expiring when you hit the back button? Seems fine to me. You often can't login to something then hit the back button to return to the page - google for example. Use post.

Related

Well formed URLs in Grails application

I have an application, that register a user. I get the user through various pages, and everyone takes some data from the user, that info is stored in the session, and only in the last page, the data is stored in the DDBB.
When the first page is fulfilled by the user, he clicks the "next" button:
<div class='btn_sig_pag_uno'>
<!-- <fieldset id="barra">-->
<g:actionSubmit action="registro2"
title="${message(code:'infoPersonal.siguiente') }"
name="siguiente" class="siguiente"
value="${message(code: 'infoPersonal.siguiente', default: 'Siguiente')}"
style="cursor:pointer" />
<!-- </fieldset>-->
</div>
The in the controller, in the action "registro2" the data introduced by the user is validated, and "registro2.gsp" is presented... but the url presented is the same than in the first page. I would like it to be controller/action...and don't know why the url doesn't change.
Any help? Thank you!
What you need to do is to create an action to display registro2.gsp and then do a redirect after having execute registro2 like this:
redirect(action: "showRegistro2")
That way the URL will change.
I think this is the way forms work, and the way Grails deals with the actionSubmit. Form submits are by default POST. And since POSTs are about sending data (and not going somewhere, e.g. like a GET), the URL stays the same.
You can change your form to have method="GET", but I don't think this will get what you want when using Grails' actionSubmit since it still uses same URL (but with ?_action_actionSubmitActionName=param as URL param).
Usually you would just do a redirect to another action your action that receives the form POST:
redirect(action: "registroFinished")
And have a registroFinished.gsp for the end result.

How can we circumvent these remote forms drawback?

In an effort to have everything translateable in our website ( including the error messages for the validations ), we switched almost all of our forms to remote forms. While this helps with the ability to translate error messages, we have encountered other problems, like:
if the user clicks on the submit button multiple times, the action gets called multiple times. If we have a remote form for creating a new record in the database, and assuming that the user's data is valid, each click will add a new object ( with the exact same contents ). Is there any way of making sure that such things cannot happen?
Is there somewhere I could read about remote forms best practices? How could I handle the multiple clicks problem? Is switching all the forms to remote forms a very big mistake?
There is a rails 3 option called :disable_with. Put this on input elements to disable and re-label them while a remote form is being submitted. It adds a data-disable-with tag to those inputs and rails.js can select and bind this functionality.
submit_tag "Complete sale", :disable_with => "Please wait..."
More info can be found here
Easy, and you can achieve that in many ways depending your preferences:
Post the form manually simply using an ajax request and while you wait for the response disable/hide (or whatever you need) the form to ensure the user can't keep doing posts as crazy. Once you get the response from the server, again you can allow the user to post again (cleaning the form first), or show something else or redirect it to another page or again whatever you need.
Use link_to :remote=>true to submit the form and add a callback function to handle the response and also to disable/hide (or whatever you need) the form when it's submitted
Add a js listener to the form to detect when it's submitted and then disable/hide/whatever the form
As you see, there are lots of different ways to achieve what you need.
EDIT: If you need info about binding or handling a form submit from js here you'll find very easy and interesting examples that may help you to do what I suggested you! jQuery Submit
I have remote forms extensively myself, and in most cases I would avoid them. But sometimes your layout or UX demands for on-the-fly drop-down forms, without reloading or refreshing the complete page.
So, let me tackle this in steps.
1. Preventing Normal form double-post
Even with a normal form, a user could double-click your button, or click multiple times, if the user does not get a clear indication that the click has been registered and the action has started.
There are a lot of ways (e.g. javascript) to make this visible, but the easiest in rails is this:
= f.button :submit, :disable_with => "Please wait..."
This will disable the button after the first click, clearly indicating the click has been registered and the action has started.
2. Handling the remote form
For a remote form it is not that much different, but the difference most likely is: what happens afterward ?
With a remote form you have a few options:
In case of error: you update the form with the errors.
you leave the form open, allowing users to keep on entering the data (I think this is your case?)
you redirect the users to some place.
Let me handle those cases. Please understand that those three cases are completely standard when doing a normal form. But not when doing a remote call.
2.1 In case of error
For a remote form to update correctly, you have to do a bit more magic. Not a lot, but a bit.
When using haml, you would have a view called edit.js.haml which would look something like
:plain
$('#your-form-id').replaceWith('#{j render(:partial => '_form') }');
What this does: replace the complete haml with only the form. You will have to structure your views accordingly, to make this work. That is not hard, but just necessary.
2.2 Clearing the form
You have two options:
* re-render the form completely, as with the errors. Only make sure you render the form from a new element, not the just posted one!!
* just send the following javascript instead:
$('#your-form-id').reset();
This will blank the form, and normally, that would effectively render any following clicking useless (some client validation could block posting until some fields are filled in).
2.3 Redirecting
Since you are using a remote form, you can't just redirect. This has to happen client-side, so that is a tad more complicated.
Using haml again this would be something like
:plain
document.location.href = '#{#redirect_uri}';
Conclusion
To prevent double (triple, quadruple, more) posts using remote forms you will have to
disable the button after first click (use :disable_with)
clear the form after succesful submission (reset the form or render with a new element)
Hope this helps.
The simplest solution would be to generate a token for each form. Then your create action could make sure it hasn't been used yet and determine whether the record should be created.
Here's how I would go about writing this feature. Note that I haven't actually tested this, but the concept should work.
1.
Inside the new action create a hash to identify the form request.
def new
#product = Product.new
#form_token = session["form_token"] = SecureRandom.hex(15)
end
2.
Add a hidden field to the form that stores the form token. This will be captured in the create action to make sure the form hasn't been submitted before.
<%= hidden_field_tag :form_token, #form_token %>
3.
In the create action you can make sure the form token matches between the session and params variables. This will give you a chance to see if this is the first or second submission.
def create
# delete the form token if it matches
if session[:form_token] == params[:form_token]
session[:form_token] = nil
else
# if it doesn't match then check if a record was created recently
product = Product.where('created_at > ?', 3.minutes.ago).where(title: params[:product][:title]).last
# if the product exists then show it
# or just return because it is a remote form
redirect_to product and return if product.present?
end
# normal create action here ...
end
Update: What I have described above has a name, it is called a Synchronizer (or Déjà vu) Token. As described in this article, is a proper method to prevent a double submit.
This strategy addresses the problem of duplicate form submissions. A synchronizer token is set in a user's session and included with each form returned to the client. When that form is submitted, the synchronizer token in the form is compared to the synchronizer token in the session. The tokens should match the first time the form is submitted. If the tokens do not match, then the form submission may be disallowed and an error returned to the user. Token mismatch may occur when the user submits a form, then clicks the Back button in the browser and attempts to resubmit the same form.
On the other hand, if the two token values match, then we are confident that the flow of control is exactly as expected. At this point, the token value in the session is modified to a new value and the form submission is accepted.
I hate to say it, but it sounds like you've come up with a cure that's worse than the disease.
Why not use i18n for translations? That certainly would be the 'Rails way'...
If you must continue down this route, you are going to have to start using Javascript. Remote forms are usually for small 'AJAXy things' like votes or comments. Creating whole objects without leaving the page is useful for when people might want to create lots of them in a row (the exact problem you're trying to solve).
As soon as you start using AJAX, you have to deal with the fact that you'll have to get into doing some JS. It's client-side stuff and therefore not Rail's speciality.
If you feel that you've gone so far down this road that you can't turn back, I would suggest that the AJAX response should at least reset the form. This would then stop people creating the same thing more than once by mistake.
From a UI/UX point of view, it should also bring up a flash message letting users know that they successfully created the object.
So in summary - if you can afford the time, git reset and start using i18n, if you can't, make the ajax callback reset the form and set a flash message.
Edit: it just occurred to me that you could even get the AJAX to redirect the page for you (but you'd have to handle the flash messages yourself). However, using a remote form that then redirects via javascript is FUGLY...
I've had similar issues with using a popup on mouseover, and not wanting to queue several requests. To get more control, you might find it easier to use javascript/coffeescript directly instead of UJS (as I did).
The way I resolved it was assigning the Ajax call to a variable and checking if the variable was assigned. In my situation, I'd abort the ajax call, but you would probably want to return from the function and set the variable to null once the ajax call is completed successfully.
This coffeescript example is from my popup which uses a "GET", but in theory it should be the same for a "POST" or "PUT".
e.g.
jQuery ->
ajaxCall = null
$("#popupContent").html " "
$("#popup").live "mouseover", ->
if ajaxCall
return
ajaxCall = $.ajax(
type: "GET"
url: "/whatever_url"
beforeSend: ->
$("#popupContent").prepend "<p class=\"loading-text\">Loading..please wait...</p>"
success: (data) ->
$("#popupContent").empty().append(data)
complete: ->
$"(.loading-text").remove()
ajaxCall = null
)
I've left out my mouseout, and timer handling for brevity.
You can try something like that for ajax requests.
Set block variable true for ajax requests
before_filter :xhr_blocker
def xhr_blocker
if request.xhr?
if session[:xhr_blocker]
respond_to do |format|
format.json, status: :unprocessable_entity
end
else
session[:xhr_blocker] = true
end
end
end
Clear xhr_blocker variable with an after filter method
after_filter :clear_xhr_blocker
def clear_xhr_blocker
session[:xhr_blocker] = nil
end
I would bind to ajax:complete, (or ajax:success and ajax:error) to redirect or update the DOM to remove/change the form as necessary when the request is complete.

Symfony/Routing: Using POST to Avoid Query Params in URL

I want to pass the id from one action to the next action, but I do not want it seen in the URL. Is there a way to hide it?
Using Symfony, I have created a sign-up done page whose URL should be /signup/done
When the form is processed, I want to redirect to the signupSuccess action and pass the recently created ID for future use. So I use...
$this->redirect('#signup_done?id=' . $sign_up->getId());
The routing is as follows:
signup_done:
url: /signup/done
param: { module: SignUp, action: signupDone }
I have avoided the :id at the end because I don't want it in the URL.
But the resulting URL is /signup/done?id=1
Just as an experiment, I tried putting this on a template.
<?php echo link_to('Sign-up again', '#signup_done?id=1', 'post=true') ?>
Even when using post, the query parameter appears in the URL.
The need is: I want to pass the id from one action to the next action, but I do not want it seen in the URL. Is there a way to hide it?
I set the id as a parameter in the request using $request->setParameter('id', $id) and it was available in the next action.
This kept the URL clean.
If you want to post, you need a form. Back in symfony 1.2 there were helpers that you could call and made you just that - but they were removed for the promotion of promoting good code.
Depending on how you want the 'Sign up again' text to look, you can either create a simple form and a submit button, or create a link, attach a click listener, and create a form there via JS, finally post it.
Any parameter that you pass to the route in url_for, link_to and such end up in the get parameters.

how to reset page text boxes data after page post back?

my question seems simple and stupid
but first read this,
suppose you have a login form which take username and password and post back to controller
if login successful then return Homepage(return View("HomePage")) View (not Redirect) then suppose i am Logged off
and return Login (return View("Login")) View (again not Redirect) and now if i press Back button and Refresh the page then it will automatically get logged IN by using those username and password which i entered before.
So can i make those password Null from Browser's Memory or where ever it is i don't know.
"i know that not redirecting (RedirectToAction("ViewName")) is causing the problem" But Why or May be i don't know any important concept
Some browsers, especially Firefox try to be helpful and 'remember' values you have input into text fields and checkboxes. in order to switch this off in a particular input add the following attribute
autocomplete="off"
Alternatively, using jQuery you can switch it off for a whole form:
$("form").attr("autocomplete", "off");
ModelState.Remove("key");
or
ModelState.Clear()

ASP.NET MVC - How can I pass FormCollection data in a post to another Action?

I have a page "Results" with a form and when "submit" is clicked, the form is submmited to another Action. So far, so good...
But, this works fine just if user is logged in. If not, he will be redirected to "Login" page and my FormCollection loses its data.
Is there a way to persist this data without using TempData??
Thanks!!
I don't think that's possible. The only thing the system remembers during the redirect to the login page is the 'return url'. No post data is saved (this could be megabytes of data...)
You can use the Session object as alternative, or make sure that the user is logged in before you post.
Or, if it's just a search result, try to live without the POST, and use a GET (which also has other advantages)
I would prefer to disallow unauthorized user to visit "Results" page or at least to show him message "Please login first" instead of the form.

Resources