rails: authlogic passing a session parameter to UserSession.find - ruby-on-rails

I'm passing the user session via a parameter (params['session_key']) for one controller (it's a flash upload script so I have to pass it via a param).
I don't know how to access the user session with authlogic by using the raw param string.
Normally I do this:
#current_user_session = UserSession.find
I thought I could do this:
#current_user_session = UserSession.find(session_key)
But passing the key as a parameter to the method doesn't appear to work.
Any idea how I look up the user session with authlogic when I can't access the session cookie and need to use a param with the session key?

In order to flash to get authenticated you need a "hack" that is quite tricky to get working.
don't know if you're using uploadify but you might need the same solution indicated here:
http://thewebfellas.com/blog/2008/12/22/flash-uploaders-rails-cookie-based-sessions-and-csrf-rack-middleware-to-the-rescue
the only problem is, I never found a 100% working solution on the internet, I started with this post and solved the problem manually with quite a pain here at work.
As I recall, I added exception on protect_from_forgery to my tmp_uploader method
Another necessary thing was on the render for the method where I had to specifically indicate:
render :action => 'tmp_uploader.js.rjs'
There is also a problem with filters using respond_to method that my AppController used, also added the tmp_uploader method to his :except list
Sorry I can't recall exactly what else I had to do, but it really was a big pain.

Related

Override redirect_to in rails

I use an engine in my rails app that logins the user and redirects to a service param (it's a CAS engine). However, from the host app I want to redirect the user (after he/she has logged in) in a different location sometimes depending on the params. At the moment I can't get it work because rails permits only 1 call of redirect_to/render. The engine inherits from my rails app ApplicationController.
How can I override/redefine redirect_to to call it multiple times?
The problem might be solved in other ways but I really don't want them. I have tried some of them but none can compete with the simplicity of just letting the last defined redirect_to take action.
I'm only interested in solutions that involve redefining redirect_to so that I can invoke it multiple times.
Of course you can "override" it. You can redefine any method in any object at any point in Ruby. However, this is a terrible idea. You should fix your problem, which is that you're calling redirect_to twice, instead of hacking apart Rails in order to allow your problem to continue.
If you're still set on "fixing" this the wrong way, find the source code (this was trivially easy to do), copy it into an initializer/library file of your own, and make the modifications.
module ActionController::Redirecting
def redirect_to(options = {}, response_status = {})
raise ActionControllerError.new("Cannot redirect to nil!") unless options
raise AbstractController::DoubleRenderError if response_body
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
self.response_body = "<html><body>You are being redirected.</body></html>"
end
end
If you really want to do this, despite being forewarned that it is the wrong solution to your problem and that you're fundamentally altering behavior of Rails that other things may depend on, comment out the line that raises a DoubleRenderError.
It seems it was much easier than I thought. All you need to do is to explicitly modify the response object.
Thus you could declare the following function in ApplicationController:
def re_redirect_to(location, status = 303)
response.location = location
response.status = status
end
That's it basically. Elegant and simple.
As I said in the first post:
I use an engine in my rails app that logins the user and redirects to a service param (it's a CAS engine). However, from the host app I want to redirect the user (after he/she has logged in) in a different location sometimes depending on the params. At the moment I can't get it work because rails permits only 1 call of redirect_to/render. The engine inherits from my rails app ApplicationController.
So basically I had no other option than override the engine's redirect_to in an after_action in ApplicationController. I believe it's much better and more maintainable solution than modifying the engine's source code.
I would like to note here that it's absolutely good to follow the conventions. Definitely calling redirect_to more than once should be avoided in 99% cases. But it's good to know that there is a way to deal with that other 1%. Conventions are just conventions.
This worked for me, in the application controller, override redirect, do your thing, then call super:
class ApplicationControler < ... #depends on your rails version
def redirect_to(options = {}, response_status = {})
# do your custom thing here
super # and call the default rails redirect
Hope this helps,
Kevin

Passing Flash to redirect_to with Indifferent Access

I'm trying to set flash variables within a redirect_to but having problems.
When I use this, the hash gets passed through, but there is no indifferent access. (ie: I can use 'key' but not :key)
redirect_to root_url, :flash=>params.merge({:error=>'no-results'}.with_indifferent_access)
It's also possible I'm approaching this completely wrong. It's a search form requesting a different action, that redirects back to the search form (on root_url) if there are no results. I'm passing params back so I can set the form fields back to what the user selected.
Thanks!
Edit: the example I had previously posted as "working fine" did not actually work fine.
This is kind of a non-standard use of the flash... which is meant for storing a message to be displayed to the user.
It also seems a bit roundabout way of doing what you really need - which I'm assuming is just a quick place to stash the params while you redirect yes?
Have you considered just using the session directly?
eg:
session[:saved_params] = params.merge :error=>'no-results'
then in the new action:
real_params = session[:saved_params]
What are you trying to look up with indifferent access? The way you have it now the argument to merge is what gets converted. Perhaps you are looking for
redirect_to root_url, :flash=>params.merge({:error=>'no-results'}).with_indifferent_access
Check if root :to in your route is redirecting to another page like root :to => redirect('[PAGE]').
This happened to me and I had to use the resource path to redirect

Passing parameters through OmniAuth

I need to pass some parameters to callback action. Judging from the source code, OmniAuth should add query string to callback URL but strangely it does not. When I open
/auth/facebook?from=partner
...and get redirected to Facebook, return_url is just
/auth/facebook/callback
...without any parameters.
After struggling with all the above answers, I figured out what to do regarding Facebook, which by default does not display the params in request.env["omniauth.auth"].
So -- If you are using a query string for the callback, similar to something like this:
"/auth/facebook?website_id=#{#website.id}"
The only way to get that website_id param is by using request.env["omniauth.params"]. NOTE: MAKE SURE YOU USE omniauth.params and not omniauth.auth -- this one tripped me up for a while.
Then, to test this out, you can inspect it within your controller action (notice the RAISE line...):
def create
raise request.env["omniauth.params"].to_yaml
# the rest of your create action code...
end
You should see your parameter there. Great. Now, go back to your controller and remove that RAISE line. Then, you can access the param as follows in your controller action:
params = request.env["omniauth.params"]
website_id = params["website_id"]
NOTE: in params["website_id"] you need to use quotes and NOT a symbol.
I guess the cookie thing works but why do all that when you can use the state variable as documented here: https://github.com/mkdynamic/omniauth-facebook
This is how I used it:
when creating the url you can just add state in the Query String and it will be available in the callback url as well.
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>
now the callback url will be
http://localhost:3000/users/auth/facebook/callback?state=123&code=ReallyLongCode#_=_
Now in the callback handler you can process the state
You can use the :params options, as in
omniauth_authorize_path(:user, :facebook, var: 'value', var2: 'value2' )
and later in the callback you can access request.env['omniauth.params'] to get the hash! :)
(copied from this answer)
What you want to do is dynamically set your callback to include the partner name in the url (not the url parameters), on a per authentication transaction basis, depending on which partner was involved. This means setting the callback url dynamically, for each authentication request. See this blog post to get started. The callback url automatically drops the url parameters, as you've noticed, so doing this with parameters won't work.
So, if instead of trying to pass the partner name/id in as a parameter (which is dropped), you structured your routes so that the partner_id and OmniAuth provider were part of the callback url, then you'd have something like:
/auth/:omniauth_provider/callback/:partner_id
...where a valid callback would be something like
/auth/facebook/callback/123456
...then you would know that a given callback came in from facebook, with partner id 123456
OmniAuth already has a built-in way to know where the user was, it's called "origin" as documented here:
https://github.com/intridea/omniauth/wiki/Saving-User-Location
You know, I think I might be trying to solve this the hard way.
Cookies might be the answer. I think you can solve this by having your login action store a cookie, and then redirecting to the proper /auth/:provider path for authentication, and when the callback is triggered (in SessionsController#create), you just read the cookie back to know where to redirect them to.
So, right now, your "login with facebook" link (or whatever you have you in your app) probably goes to /auth/facebook. Instead if you created a custom action like
POST /partner_auth
...and called it with the url...
POST example.com/partner_auth?from=partner&provider=facebook
Then you might have a controller like:
class PartnerAuth < ApplicationController
def create
cookies[:from] = params[:from] # creates a cookie storing the "from" value
redirect_to "auth/#{params[:provider]"
end
end
Then in the SessionsController#create action, you would have...
def create
...
destination = cookies[:from]
cookies[:from].delete
redirect_to destination # or whatever the appropriate thing is for your
# app to do with the "from" information
end
I tried to build a demo app to accomplish what I'd outlined in the other answer, but you're right - it was too complicated to try to dynamically inject a custom callback into the OmniAuth code. There is a configuration option to override the default callback, but it doesn't appear to be easy to set it dynamically.
So, it dawned on me that cookies would be way simpler, user-specific, and since you theoretically only need to store this from information for a very short time (between when the user tries to authenticate, and when the callback is triggered), it's no big deal to create a cookie, and then delete it when the callback gets hit.
Use the 'state' Variable. Facebook allows the user to set a STATE variable.
Here is how I did it, I appended the AUTH URL with ?state=providername
http://localhost/users/auth/facebook?state=providername
This param is returned to me at Callback as params['providername']
I devised the solution from the original Omniauth Path Method
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>

redirect_to doesn't work well for RESTful apps?

As a long-time Ruby and Rails user, it never struck me until today to really think about the get-and-redirect pattern in Rails. The typical example of this would be calling a create() action, and then redirecting the user to a show() action to display the newly-created item:
class JournalEntries
def index
#entries = JournalEntry.all
end
def create
#entry = JournalEntry.new( :name => "to-do list" )
#entry.save
redirect_to :action => "index"
end
end
However, this has the inherent disadvantage that you are doubling your network traffic. This both slows down your users' site experience, as well as increasing your bandwidth charges.
So why not just do this instead:
def create
#entry = JournalEntry.new( :name => "to-do list" )
#entry.save
index
Same output, and no extra overhead required. But in addition to this, there is an even more substantial problem: redirect_to can only redirect using GET. This causes major problems for RESTful apps that use four different HTTP methods.
In my case, I wanted a user to be able to call /journals/8 and retrieve the Journal with that ID. If it wasn't found, I wanted to create a new, empty Journal object. In either case, the Journal object would then be sent to the caller.
Note that the create() method in RESTful Rails is routed from "POST /players". But since redirect_to (and the underlying HTTP redirect) can only send GET requests, it actually redirects to "GET /players", which is the index() method. This behavior is clearly wrong.
The only solution I could think of was to simply call create() instead of redirect_to() as in my example above. It seems to work fine.
Any thoughts on why redirect_to is preferred to calling actions directly?
If they do a page refresh they don't get that annoying "Resend data?" popup
It's not just that the popup is annoying (and makes no sense to most users) -- if the user clicks "yes, re-do the POST", he'll end up creating another Journal Entry (or whatever).
Also, it's annoying for the URL to read /posts/create instead of /posts since the user cannot copy / re-use it.
The reason for it is as you point out. You redirect to a GET request, which is correct when it comes to REST (only do updates with POST/PUT, only get data with GET).
A redirect surely gives a little overhead with the redirect, but since no data is actually being sent between the browser and the server except for the POST data and the redirect (which is only sending the new url to the browser) I don't think that the issue of bandwith is of concern.
But on another point, you should not redirect to /journals (by calling redirect_to :index), you should redirect it to the newly created journal entry (by calling redirect_to #entry) which will work if you set up the routes correctly by, for instance map.resources :journals
Update:
I think, for creating the Journal when one doesn't exist, you should ask the user for more input. What is the reason for you to create the entry? The entry should have some text or some other input from the user, so I think from a REST (and rails) perspective you should actually redirect it to the new() method (with a GET request) where the user can input the additional information, that one will then POST the input and create the entry and after redirect to the newly created entry.
If you don't have any extra information that needs to put in, I'm not sure how to do it in a RESTful way, but I would probably have done it by putting the creation logic in a separate method that I would call from the create() and the show() method and then just continue with the show(), not redirecting at all, but also not calling the resource method.
I'm a Python/Django person, but the reasons for the redirect is language agnostic:
If they do a page refresh they don't get that annoying "Resend data?" popup.
This gives you a completely clean, RESTful URL for the page they are looking at. If you used POST it might not matter that much, but if GET was used for the update then you definitely want to get rid of any dangling params.

getting the flash hash to persist through redirects

My basic use case is do some processing, set flash[:notice], and then redirect to a new page. From what I can tell, redirects reset the flash tag (please correct me if I'm wrong). Is there a way to gain persistence? Using sessions isn't an option, and I have hacked around the problem using cookies, but I think there's got to be a better way.
The flash hash persists for exactly one redirect or render. So you should be fine with the default settings.
If you need to keep the flash hash for another request/redirect, you can call flash.keep.
flash.keep # keep the entire flash hash around for an extra request.
flash.keep(:notice) # keep just flash[:notice] for an extra request.
Something to be aware of in at least Rails v3.2.1 is that the flash will persist through a redirect if its not referenced at all through at least 1 redirect and load the same view after. This is a pseudo code of my recent experience:
def some_action
(code that may set a flag to redirect 1 time)
redirect_to action_path if(redirect_flag)
....
end
Running this would result in the flash[:message] being present regardless of the redirect.
def some_action
logger.debug("Flash[:message] #{flash[:message]}")
(code that may set a flag to redirect 1 time)
redirect_to action_path if(redirect_flag)
....
end
During debugging with the logger referencing flash[] it would only show up when the redirect didn't happen. I could see this being problematic if you added a reference to flash before a redirect and lost it down the line for no apparent reason.
See ruby docs here (Instance protected method: Use at the bottom)

Resources