Easy access control to Rails app? - ruby-on-rails

I need to control access to my whole Ruby on Rails app, allowing only some selected users to see it while it's under development.
I'm not looking into a full authentication system, but something much simpler, where I can add an email address to a restricted-users list, send an email to it and let that user see the app, rejecting any other user.
I have tried to use the WWWhisper add-on in Heroku (the app is hosted there) as it's just what I would need, but it doesn't seem to be available outside of the US.
I think I will have to build it myself, but, before, I wanted to make sure that there isn't a simpler solution already available. Any ideas? Thanks in advance.

If your access requirement is "having got an invitation email" then i think your simplest option is to have something like this
#in application.rb
before_filter :require_invite
protected
def require_invite
session[:invite_token] ||= params[:invite_token]
unless session[:invite_token] == "<SOME SECRET KEY STRING>"
redirect_to external_home_path and return
end
end
end
where external_home_path is some holding page for people who haven't got an invite, which says "Sorry you need an invite token blah blah". You'll need to add this to the controller which handles the external home page, to avoid a circular redirect.
skip_before_filter :require_invite, :only => [:external_home]
Then you just need to send people an invite with a link like
http://example.com?invite_token=<SOME SECRET KEY STRING>
Obviously this isn't very secure but it's as secure as the "have i seen the email" criterion.
When you go live you can take this before_filter out.

Related

Rails 5 Geolocation with Clearance

I'm currently using Clearance for authentication. As part of the login process I want to ensure that I have a location for the user (stored in the session).
My question is how to do this in the context of clearance? Ideally I only want to perform the lookup on login (to save network traffic / API calls). If I can't locate a user then I'll deny login.
I was thinking of using a guard but I don't seem to have access to request.ip or session which is a bit of a deal breaker. I was also trying to avoid redirecting to a URL that only does the geolocation and then redirects again.
Anybody have ideas on a nice model on how to make this work? Thanks!
Sign in guards are mostly intended for processes that will prevent or allow sign in. I don't think they are a good fit here. For this use case, I would suggest overriding sign_in, which is generally mixed in to ApplicationController
def sign_in(user, &block)
super
if signed_in?
UserGeocoder.call(current_user)
end
end

rails authentication for an API

I'm currently working on an application that in addition to the usual visual web application goop also will expose a few RESTful API services for use by outside applications. I am using Devise to manage user authentication but I'm struggling with how to "manually" authenticate a user given certain input.
The case I have is that I want a user of the API to log in w/o actually going to the visual log in screen etc. I want them to submit a username and password and then authenticate and sign them in in my API services.
I know that you can sign a user in using the sign_in method that Devise provides, but that seems to ignore authentication entirely. here's what I wanted to do explained in a bit more detail:
Assume a GET route called connect in the user controller. the controller is replacing entirely the Devise registrations controller, but not the session one. The URL to my service would be:
<server>/users/connect
and it would expect 'email', 'password' parameters in addition to some service specific and unimportant to my question goop.
What I want to know is how to implement something that is equivalent to the following pseudocode:
def connect
user = User.find_by_email(params[:email])
password = params[:password]
# here is the part I'm pseudo coding out
if user.is_valid_password(password)
...do my stuff...
end
render :json ...etc...
end
I have been unable to find a method in the Devise source to do this--it's so generalized in so many ways that I'm likely just missing it.
Anyone have any ideas? I'm hoping not to a) have to implement my own thing and b) not have to move away from Devise. It provides me with so much for the non-API services...
thanks!
I've left out th
Devise's token_authenticatable is the way to go for this. We've successfully used it many times to do api-based logins.
In config/initializers/devise.rb
config.token_authentication_key = :nameofyourapikeyhere
In user.rb:
devise … token_authenticatable, ...
In the above, you can name the api key anything and then have your route as /users/connect?apikey=whatever (using apikey as an example). In the database, it'll be authentication_token, but it'll work fine.
To clarify, if the user has an authentication_token and it's sent in the params (or it's alias- in the above example: apikey), they'll login.

devise gem way to auth for a notification api

My app is using the devise gem for authentication. I have this video conversion service that can emit notification to a url of my choice, presumably so that the app knows the conversion is complete, basically, the notification is the same as someone clicking a button that says "conversion complete" but done programmatically.
I have created in the routes.rb file a route to this action which will mark a video as being successfully converted
#routes.rb
match "video/set_complete", "videos#set_complete"
#videos controller
def set_complete
video = Video.find_by_conv_job_id(params[:encoding_id])
video.set_complete
end
That api call from that video conversion service naturally carries a few other params to further identify the video. But I don't think it's that relevant.
Now here's the question, by the way, thanks for reading my post, I am forever indebted to you--- I mean, simply put, I can't just let anyone access that dns.com/videos/set_complete, that's why it is using Devise to authenticate, but I don't want to use user:pass#dns.com/videos/set_complete, what do I know, I just feel that the user/pass is in plain sight in a plain http (not s) request is quite dangerous. So I thought, well, assuming that the requests always come from a certain ip or domain name, can I make devise always authenticate these ip's requests without doing any embedded user/pass or the use of single access token?
Thanks again!
You can do something like this in your videos controller:
before_filter :check_ip, :only => [:set_complete]
def check_ip
if request.remote_ip != '[ip address]'
authenticate_user!
end
end
This will let signed in users make that request unless they come from whatever IP address you've specified. If you want to ONLY allow requests to this action from that IP, change authenticate_user! to something like:
sign_out_and_redirect
However, since they don't care about the response, someone could just spoof the correct IP and be able to set things complete. I don't know how much you care about that though, and this is probably still sufficient for what you're doing.

How to create password protected RSS feed in rails

Creating RSS feed in rails is easy. I need a simple way to password protect the RSS feed. I am thinking http basic authentication.
I googled but could not find any article about creating password protected RSS.
I have this in my ApplicationController
def xml_authorize
if request.format == Mime::XML
authenticate_or_request_with_http_basic do |username, password|
username == 'foo' && password == 'bar'
end
end
end
Then I just apply a before_filter :xml_authorize to the actions that I want to password protect for XML requests only, but still want to serve normally with html.
Here's where I got the inspiration from.
Just use whatever auth system you use on your regular controllers. If the user is logged, and session is alive he will be able to download the feed.
How is HTTP authentication any different on RSS feeds than other controller actions? (This is not necessarily a rhetorical question - is it actually different?)
Have you tried just using the Rails HTTP Basic Authentication methods?
Feeds are meant to be fetched in regular intervals without user interaction.
RSS-Feeds can be consumed by something different than a browser. For example,
I have a phone where I can create a widget for the start screen from a rss-feed-link. Great function. Unfortunately authentication does not work for this widget.
And there is no user interface to put in username and password. So the authentication need to be part of the url, with all the bad security implications...
On the other hand you don't need a session to answer a feed-request.
So the solution is a create a special key for the user, and store it in the user table.
Then use it when you display the link to the rss-feed. In the feed-method, you use this key to retrieve the user, but you don't create a session. This way, even when a hacker somehow got a valid key, the hacker can only view the rss-feed, but not access the rest of your application.
If you already use some library for authentication, there may already some solution implemented for this. In Authlogic, is is the class SingleAccessToken, and you need to add a column 'single_access_token' of type string to your user table. Then authlogic creates some cryptic key when are saving the user record. You than add this key as the GET-Parameter 'user_credentials' to the url of the private rss-feed
Like knoopx said, If you use an authentication system like authlogic you should be able to specify the authentication type in the controller. You can then specify http basic authenication. Then you can, if you choose, include the authentication in the URL for the RSS Feed.
e.g:
http://username:password#example.com/rss
(sorry to break the URI up like that, but I don't have enough reputation points to post multiple links :( )

Destroy cookies in rails, facebook connect, facebooker plugin

So I have sort of a weird situation going on. I am using the Facebooker plugin for rails where I want users to be able to login and logout with their facebook profiles. However, once users logout, if I refresh the page, it logs them back in. This is only when users log in with facebook connect.
I think the problem is that a rogue cookie is just re-instantiating the session and thus my best guess is to manually destroy the cookies but to be honest I'm not entirely sure of how to do this. I printed out my cookie list (from request.cookies) both before and after I click logout. After logout is clicked, I still have this cookie lingering...but don't know how to delete it.
fbsetting_0b78c8f2c95ce671470bdcb1c19e5070 {"connectState":1,"oneLineStorySetting":1,"shortStorySetting":1,"inFacebook":false}
After playing around with it a little more, that cookie isn't even there...but upon refreshing the page I am logged in again.
I'm doing this all on localhost...not sure if that should cause a problem or not.
Any ideas?
This is what I have in users controller
def logout_facebook
clear_facebook_session_information
redirect_to root_url
end
and this is how it is triggered
<%= fb_logout_link("Logout out", "#{root_url}users/logout_facebook")%>
This is how I got it to remove the cookies on the local side.
Note: don't forget to add a route to the logout_facebook method in your routes.
Destroying local cookies isn't enough to terminate a Facebook Connect session. The connect JS library will recreate destroyed cookies as long as you still have an active session on the facebook.com domain -- and those cookies are inaccessible to you.
All log-outs must be handled by calling the logout function in the connect library.
e.g.,
<script>$H.fbconnect.logout();</script>
To delete a cookie, you need to set the cookie again with an expiration date in the past.
a million thanks for the clean and full-proof solution to such a frazzled functionality. I've been literally going Blank over last 2 weeks since I came across the fact true to I believe almost all NEW-facebooker users, "facebook connect logging out completely". Or maybe just it was just a lack of documentation or some code excerpt I wasn't able to find till date in facebooker cover docs.
Anyway away from all that, moving to the solution which made me achieve it... Following to #James B method above.
Obviously I am assuming you all have read facebooker installation, configuration & usage instructions http://github.com/mmangino/facebooker.
I also have used a plugin "authlogic_facebook_connect" which you can find at github.com page of kalasjocke/authlogic_facebook_connect.
Now I assume you already configured your application to work with facebook connect by using fb_login_button or authlogic_facebook_login_button. Clicking it you'd see a popup to log yourself into both your facebook account and into the local account of your app. Once you login you'd be getting a facebook_session to handle saving a new user in your DB (only if you wish to).
By default you'd not be getting birthday and email address of users in the facebook_session as response from facebook. To get them you need something like this in your initialization javascript in the body tag of the rhtml page...
<%= fb_connect_javascript_tag %> <%=
init_fb_connect "XFBML",{
:app_settings=>" {
permsToRequestOnConnect :
'email,user_birthday' }"} %>
<%= authlogic_facebook_login_button %>
Now finally what I did for getting logged out of both Facebook and my site.
Inside the header of your application or wherever the LOGIN, LOGOUT, loggedin users's name etc will display...
:delete
%>
The two logout buttons are for different kind of users.. a. who registered directly on your site, b. who registerd to your site via facebook
Route for logout_both in routes.rb
map.connect "/logout_both",
:controller=>"users",
:action=>"logout_both"
The action for logout_both inside users_controller.rb
def logout_both
current_user_session.destroy
#clear_facebook_session_information
flash[:notice] = "Logout successful!"
redirect_to root_path end #End of method logout_both
You need to make sure that there are no filters defined in the controller which could restrict logout_both action to be executed without a session.
Oh yes and if you're wondering why that "clear_facebook_session_information" is commented in the action. Then don't worry, you're already logged out of facebook before entering this action, this line is no longer needed. Boss we're using "fb_logout_link" which first logs you out and then redirects you to this new action.
Well, that's about it. If this doesn't do it........ get your hands dirty like I am doing.. dig in.. solve it yourself. Facebooker is deep but has a definite END!!!
Again many thanks to mangino & kalasjocke for making facebook connect almost no painful for rails.
One clarification people... in my Answer above.. There is a hick, which is.. After logout, if you refresh the page.. it would again show you logged in on your site.. however you're successfully logged out of Facebook.
I was wrong, ONE BIG CLARIFICATION...... As a matter of fact you DO NEED THESE TWO LINES IN YOUR "logout_both" action
def logout_both <br/>
current_user_session.destroy<br/>
clear_facebook_session_information #MANDATORY TO COMPLETELY CLEAR COOKIES<br/>
reset_session # TO BE 100% sure you can use it optionally<br/>
flash[:notice] = "Logout successful!"<br/>
redirect_to root_path <br/>
end

Resources