Devise/Warden custom single-sign-on strategy - ruby-on-rails-3

I want my SSO custom strategy to be the only strategy that devise uses. I'm doing this by:
config.warden do |manager|
manager.default_strategies :my_sso_strategy
end
This is where I get stuck. I want to invoke the strategy, and I thought this would be done for me by:
app/controllers/devise/sessions_controller.rb
But, it doesn't look like my strategy gets invoked.
I took away the :authenticatable / :database_authenticatable from the devise declaration in my User model thinking that may be causing the problem.
After making the sign-in/out routes manually (because :authenticatable makes them), I get an AbstractController::ActionNotFound error.
At this point I'm at a loss how to continue.
My views should be setup correctly to auto-submit my credentials I get from the SSO application (it worked in authlogic)
Has anyone done something similar to this?

I can't say for sure unless you share how you coded the strategy. The simple answer is that you need a valid? method defined to specify when to use the strategy. Since you only want to use one strategy then I would expect your valid method to be like this..
Warden::Strategies.add(:my_sso_strategy) do
def valid?
true
end
def authenticate!
#do authentication
end
end

I have seen others use the ":user" scope and noticed you did not have it in your excerpt. Perhaps that is causing the original error.
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :fb_database_authenticatable
end

Related

Validations using simple_form: validated object lost after hitting create

We have a form created by several controllers's new actions, which we reuse via render :new in the create action to display validation error messages. I believe this is the way to go for simple_form and validations. Correct me, if I'm wrong here.
We also have a general language switching mechanic, that redirects to the current_url, with a different locale.
The problem:
After a failed validation and the second rendering of the new form, the language selection throws an error (which would be very misleading to post here). The problem is that the create action expects the validated object, which our language selection does not pass to the current url again.
How would you tackle this problem?
We could try to teach our language switcher about "create" and have it send another post request with the same params, but this seems awful. There would have to be a lot of logic in our little helper and where would we store the objects (at least one kind of them is not persisted at all)?
Someone mentioned (ab-)using a flash message to recreate the object, but it's a huge form with up to 50 validations and this get's uglier with size, I guess.
Storing the object in the session in these cases and have the helper post the object again, if it exists might work. I like this one the most, but it's far from feeling right as well.
We could try to have simple_form use the "new" action instead of just rendering "new", but this seems really bad.
We could disable language switching for create actions altogether, with an alert saying this one step has to be finished in the chosen language.
Do you have any opinions, other suggestions? I'd be very grateful.
Thanks,
Andy
So we changed the language helper to send the same post request again, if it is on a page created by a POST. It ended up looking like this. Not a lot of code added:
def language_link(language)
url_options = { locale: language }
if request.request_method == 'POST'
link_to(language, url_for(params.merge(url_options)), method: :post)
else
link_to(language, url_for(url_options))
end
end
We were carefully making sure we don't end up sending valid data a second time. Creating a second payment, or a second order would be quite bad here for example. We need to keep this in mind in the future as well, when we're creating new post routes accessible on a part of our application where language is changeable. That's the main problem here.
It does not consider PUT requests now because we don't have any edit/update functionality on the part of the app where language is selectable.
We can live with this version in our code. So I post this as an answer. But I'd still be happy to see a better (less dangerous) version, our any thoughts on this at all.
Cheers,
Andy

Rails, A method to be called only in production

I remember seeing this somewhere online but I know have trouble finding information about it. In Rails 3.1, I have a method at the beginning of a session controller, force_ssl, I only want it called in a production environment, how do I do that?
To clarify, the code looks something like this
class SessionsController < ApplicationController
force_ssl
end
I had a similar problem. force_ssl caused problems by testing with capybara and selenium. This here solved my problems and the tests are now running:
force_ssl if Rails.env.production?
I am using the line above in some Controllers. For example in the SessionController and UserController.
Rails.env.production? returns true if the current environment is 'production'. More generally, Rails.env.somestring? returns true if Rails.env == "somestring". From there you should be good.
** EDIT **
Well actually, there's an easier way to use ssl only in production. Check out this article

logging info with rails

Moving over from django / python, I am having a little trouble getting the rails logger to log all the information I want. I am wondering how/if the following can be achieved:
Having in the log format(ter) include the specific file, function name and line where the logging statement itself was found. Essentially the equivalent of LOG_MSG_FORMAT = '%(asctime)s %(levelname)s %(filename)s:%(funcName)s(%(lineno)d) : %(message)s' in python logger?
Being able to log all requests, via something similar to a django request logging middleware. Particularly, being able to log the username (if logged in) of every request.
Am I missing something obvious? or does this require (lots of) custom code?
I just found this railtie gem that might help although I imagine it will take some "custom code" to append username to logs. See the Readme section on logging specific controllers and models.
logging-rails railtie
I don't know about getting the file, function, and line number, but it's pretty easy to log from application_controller:
class ApplicationController < ActionController::Base
before_filter :log_user
def log_user
if current_user
Rails.logger.info "Processing Request for #{current_user.name}"
end
end
end
Just to add a quick note in case this is useful for someone:
The lograge gem makes rails logs much similar to django's, plus allows very neat customization, adding parameters such as remote ip address, current_user etc.
It also reduces verbosity of rendered layouts, which I anyway found unnecessary for production. It also plays nicely with logging-rails railtie mentioned by #lukewendling.
Sadly, I still couldn't find anything that shows the file/function/line number like you can easily do with django, but I guess that's just too much to ask.

How to access current_user from a Rails Notification?

I'm building an audit trail that needs to know which user is currently making the request. My audit trail is built using ActiveSupport::Notifications to receive an even that needs to be audited.
What I would like to do is use an ActiveSupport::Concern to encapsulate the logic for my audit needs, so that I can easily add auditing to any model in my system.
In general this is easy to do. I even blogged about it a while back. However, I'm having a hard time figuring out how to get the current user that making the request to the web server, so that I can log who is making what changes in my audit trail.
I know there are a ton of questions about "how do I get current_user in my model" but I'm not asking about doing it in a model, so I'm hoping there is a better set of answers. Since my audit code is infrastructure related, I am hoping that there is some way I can tap into the current request that is being processed, or something else that would definitively tell me who is currently logged in / making the request.
I've read a lot of "answers" that say to use thread storage and put the current_user in there. I don't like this answer for many of the reasons that others don't - there is no guarantee that thread storage is safe. it could bleed across multiple requests if the server uses the same thread to process multiple requests, etc.
so... given that I am not trying to access current_user from my model, but rather from either an ActiveSupport::Concern or ActiveSupport::Notifications event subscription, are there any good options for me to know who the current user is?
Update
I'm using devise for authentication, which uses Warden on the back end. devise retrieves the current_user by calling request.env['warden'].authenticate(:scope => :user) (assuming i use a "User" model for authentication).
Is there a way for me to access the current request object from within my concern or notification subscription? Back in my .NET days, I would have been able to call HttpContext.Current.Request and all would be good. What's the equivalent in Rails?
Rails' ActionController::Instrumentation has explicit support for this, using append_info_to_payload.
Add a method to your ApplicationController:
def append_info_to_payload(payload)
super
payload[:current_user_id] = current_user.try(&:id)
end
now, when your observer is called back, the information will be in the event.payload:
ActiveSupport::Notifications.subscribe /process_action.action_controller/ do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
current_user_id = event.payload[:current_user_id]
# do something interesting with current_user_id here
end
You already have the answer, what you're doing is the same as when people are accessing the request in models. The current_user is just a method defined on your ApplicationController. When you're not in a controller or other class that inherits from it, you can't access that method.
HttpContext.Current.Request << I would bet a lot that this uses thread storage. Any other solution we find will also be thread storage at some level or another.
Either pull out what you need from the request in the controller and pass it down as parameters, or use thread storage -- but this is inherently dangerous anyway. What if you start using delayed job to do the notifications or something?

Rails authorization? check in Model vs controller

I am doing a check on a user model to determine whether s/he has one or more task_list, if she has more than one task_list only then she is allowed to delete it, otherwise an exception is thrown. I basically have an method called delete_list in the user model to allow for short hand deletions such as user1.delete_list(list1)
I am debating whether to put the check in CanCan where it would be apply as a before filter on the controller or whether to have it in the user model as well . What is the recommended practice?
I think a good DRY approach to this would be to create a method in your model that tests whether a delete is allowed. Then use that method from your controller or from ability.rb. IMHO I think having complicated permission/business logic decoupled from CanCan is better when there is a chance you might change to a different permission system in the future.
In your model:
def can_destroy_list(list)
... Do check here ....
end
In ability.rb
can :destroy, List do |list|
user.can_destroy_list(list)
end
Your controller and views can then also use can_destroy_list directly on the model instance if nessary or use: if can? :destroy, #list