Strange Symphonies The best way to predict the future is to invent it

11Nov/090

SWFUpload on Rails

SWFUpload

Note: This has been tested with SWFUpload v2.2.0.1 and Ruby on Rails v2.3.4

Note: This is used together with restful-authentication.

SWFUpload is a Flash uploader, that enhances the experience of uploading. Some examples include the ability to select multiple files at once, and queue them up for upload. For more features, visit the SWFUpload website. This is a great tool if your website requires you to upload multiple files.

But there are documented issues SWFUpload and using it with Ruby on Rails.

By default, when using SWFUpload with Ruby on Rails, SWFUpload works great. Everything works as documented, well at least as far as I can tell. By default I mean:

  • You are not using authentication (for example login)
  • If you are using authentication, but uploading to a page that doesn't require login

Problems with SWFUpload and Ruby on Rails

SWFUpload breaks on Ruby on Rails when you using authentication, and when you have to upload to a page that requires login. The result of doing so is that Rails will think your user is not currently logged in, and will deny the upload request possibly by redirecting the request to the login page. This bug is caused Flash not being able to use the same session as your web browser.

Now when you Google, you will notice multiple methods of solving this problem. Notably a fix to rack and the middleware. Now I don't want to hack my rack, or middleware. Plus I found all the documentation rather confusing.

swfupload.cookies.js

Instead what I wanted to focus on, was in the tarball for SWFUpload. Inside I noticed a file called swfupload.cookies.js. The file describes itself as:

Cookie Plug-in

This plug in automatically gets all the cookies for this site and adds them to the post_params.
Cookies are loaded only on initialization. The refreshCookies function can be called to update the post_params.
The cookies will override any other post params with the same name.

When you include swfupload.cookies.js into your application, your params will include the keys and values from your cookies. For example, if you had a cookie called "auth_token", with a value of "d2f423cd5559645eb8e75230b9ce2648d59a7ad8", it would appear in your params as params[:auth_token] = 'd2f423cd5559645eb8e75230b9ce2648d59a7ad8'.

This was the perfect tool I needed.

Getting SWFUpload to work with Rails


Note: Once again, this tutorial is for use with restful-authentication. We will assume that your User model is called "User", and your Session controller is called "SessionsController". We will also assume that the swfupload files are in /public/javascripts

Using swfupload.cookies.js

In your Rails app, include swfupload.cookies.js. I decided to put it after my swfupload.js.

  <%= javascript_include_tag 'swfupload.js' %>
  <%= javascript_include_tag 'swfupload.cookies.js' %>

Modify Your Sessions Controller

If you open up your Sessions controller ( /app/controllers/sessions_controller File ) you will see a create method. This is where your User is authenticated, and their session established.

  def create
    logout_keeping_session!
    user = User.authenticate(params[:login], params[:password])
    if user
      # Protects against session fixation attacks, causes request forgery
      # protection if user resubmits an earlier form using back
      # button. Uncomment if you understand the tradeoffs.
      # reset_session
      self.current_user = user
      new_cookie_flag = (params[:remember_me] == '1')
      handle_remember_cookie! new_cookie_flag
      flash[:notice] = 'Logged in successfully'
    else
      note_failed_signin
      @login       = params[:login]
      @remember_me = params[:remember_me]
    end
    redirect_back_or_default('/')
  end

Now we need to change lines 10-11.
From:

      new_cookie_flag = (params[:remember_me] == '1')
      handle_remember_cookie! new_cookie_flag

To:

      handle_remember_cookie! true

Yes, we are going to make the User remember the cookie every time he logs in.

Modifying lib/authenticated_system.rb

Now for all this to work we need to edit lib/authenticated_system.rb.

Look for the current_user.

    def current_user
      @current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie) unless @current_user == false
    end

Now we are going to add a new method call to that, and change the method current_user to:

    def current_user
      @current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || login_from_params) unless @current_user == false
    end

Now we create a new method called login_from_params.

    # Created specifically for swfupload, as the cookie can't be passed via flash, and is instead passed via a post param
    def login_from_params
      binder = params[:auth_token] && User.find_by_remember_token(params[:auth_token])
      if user && user.remember_token?
        self.current_user = binder
      end
    end

This is where the magic happens. The user will be logged in based on their :auth_token.

Testing

Make sure you log out the user, and login again as we have to create the cookie.

Conclusion

There you have it! A working SWFUpload with Rails. Enjoy.

Related posts

Tags

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.