Modify

Opened 2 years ago

Last modified 5 months ago

#10393 new defect

POST data is lost after redirect

Reported by: ejucovy Owned by: ejucovy
Priority: normal Component: PermRedirectPlugin
Severity: normal Keywords:
Cc: Trac Release: 1.0

Description

The situation is described in comment:ticket:2210:14 and comment:ticket:2210:16 -- if a user has a browser window to a Trac edit screen open for a long enough time before submitting the edit form, their session can expire and they'll be redirected to the login screen. After successful login, they'll be redirected back to the edit screen, but their edits will be lost, which can be very annoying.

Simple way to reproduce in a browser:

  1. Log in to your trac site
  2. Open a browser tab to http://trac-site.com/newticket
  3. Fill in some text to create the new ticket
  4. Don't submit the form yet!
  5. Open a second browser tab to http://trac-site.com/logout and then close it
  6. Back in the first tab, submit the form

If PermRedirectPlugin is installed, you'll be redirected to the login screen; log in, and you'll land back on the "newticket" form. But all your hard work is completely lost.

I think that it's fair to consider this a bug in PermRedirectPlugin. The reason is that, without PermRedirectPlugin installed, Trac's behavior is "safer." If you follow the exact same reproduction steps without the plugin installed, you'll be given an "Error: Forbidden" screen that prompts you to log in.

This screen is actually rendering an HTTP response to the POST request, rather than instructing the browser to issue an immediate GET request like the plugin does. So this actually gives you an opportunity to log back in using a separate tab, and then simply hit "reload" on the first tab. Your browser will warn you that you're resubmitting a form via POST and ask if you really want to resubmit the data. You say yes, and the exact same POST request will be replayed, with your original intent fulfilled and no loss of data.

Attachments (0)

Change History (5)

comment:1 Changed 2 years ago by ejucovy

UnwashedMeme outlined a few possible solutions:

  1. keeping the POST data in the server's session memory
  2. sticking it in a cookie
  3. trying to create querystring parameters out of everything posted.

Another class of solutions that occurs to me:

  1. only issuing the redirect when the unauthorized request is HTTP GET/HEAD; if it's a POST, do something else that gives the user a chance to save his data -- such as
    1. re-raise the original exception (fall back to Trac default behavior with no auto-login screen)
    2. re-raise the original exception, but with a different response body that explains what's going on and warns the user to open a new tab to log in
    3. render a login form directly in the response to the POST request, instead of returning an HTTP redirect; including the original POST data as hidden fields in the login form; and catching the subsequent POST to dispatch it first to the login handler and then to the "real" endpoint

I think (1), (2), (4a) and (4c) are worth investigating, but all of these approaches are pretty ugly in one way or another.

comment:2 Changed 2 years ago by ejucovy

The session-based approach is problematic because as soon as the user logs in, he's likely to have a different session than the one he had as an anonymous user.

comment:3 Changed 2 years ago by ejucovy

(4c) is problematic because there isn't necessarily a login form (in the case of Trac core HTTP auth)

comment:4 Changed 2 years ago by ejucovy

Hybrid idea:

  1. In post_process_request, if method==POST, set a (path-scoped?) "saved_post" cookie containing the POST body
  2. Add javascript to all pages such that on window.load
    1. Check if "saved_post" cookie is present; if it is, save its value to a Javascript variable and delete the cookie
    2. Then pop up a modal dialog: "You have unsaved data that you tried to submit earlier. Do you want to resubmit it?"
    3. If user clicks "Yes", use Javascript to fire a synchronous POST request using the exact request body from the cookie

(This is, obviously, getting pretty convoluted, but it's a fun problem to think about.)

comment:5 Changed 5 months ago by hasienda

I remember dealing with proper re-direct in AccountManagerPlugin. IMHO salvaging POST date after redirect could (should?) be done inside AccountManager, since it already messes with Trac's LoginModule.

Regarding propagating session attributes two pre-existing methods come to my mind to possibly start from:

trac.web.session.Session.promote_session()
it promotes all anonymous session attributes to an authenticated session, if there is no preexisting session data for that user name, meaning on first successful login only - one could aim at still copying, but only non-existant session attributes
functions in datasaverplugin/trunk/datasaver/htdocs/datasaver.js
with the issue of session-based approach, at least of no localStorage is available in user's browser - form restore from localStorage might even solve the issue right-away (untested)

Add Comment

Modify Ticket

Action
as new The owner will remain ejucovy.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.