Allow user access to protected page using URL token - ruby-on-rails

I am using Clearance gem for authentication. I have one page in the app to which I want to permit anyone to come provided they have a secure token in the URL / Session. They need not be users in the system.
Is this doable with Clearance. Any hints on how I should proceed. Initially I thought I should override require_login and current_user. Is that a good approach?

Should the secure token page also allow access to people who are signed in, or must everyone have the secure token?
If you must have the secure token, regardless of whether you are signed in with Clearance or not, then I would avoid Clearance for this controller all-together by not calling require_login at all (or calling skip_before_action :require_login if the filter is already in your controller's inheritance tree). Then you could implement your own before_action that checks the token and does whatever you'd like. You could also implement your own current_user for this controller if desired.
If the page should also allow signed in users then I would still skip the Clearance before action and instead use something like this:
def require_login_or_secret_token
unless params["super_secret_security_token"] == TOKEN
require_login
else
end
Then you'd need to override current_user to return a guest object rather than nil if you want to use current_user in this action/view:
def current_user
super || Guest.new
end

Related

Changing Devise sign-in parameters before authentication

I am in a situation where I need to edit the credentials that users input on the login page before calling Devise's authenticate_user! method. In my UserSessions controller, I just override the login method and edit the sign-in parameters accordingly:
def login
params[:user][:email] = fix_email # Some method that returns the correct email address, which may be different from the initially entered one
authenticate_user!
# ...
end
The authentication fails here if the email ends up being changed as it seems that Devise does not actually use the params for authentication. However, after studying both Devise's and Warden's source, I still haven't been able to figure out where exactly I need to write the correct email to in order for Devise to actually authenticate with it. Can anybody clear this up for me?
Hey – Interesting problem. This looks like a good job for the before_filter methods. You should be able to modify parameters before strong parameters come into play
before_filter :modify_email, only: %i[login]
def login
authenticate!
end
....
private
def modify_email
params[:user][:email] = fix_email
end
Ideally, this should solve the issue for you. Kindly let me know!

RESTful routing best practice when referencing current_user from route?

I have typical RESTful routes for a user:
/user/:id
/user/:id/edit
/user/:id/newsfeed
However the /user/:id/edit route can only be accessed when the id equals the current_user's id. As I only want the current_user to have access to edit its profile. I don't want other users able to edit profiles that don't belong to them.
What is typically the best practice to handle this situation?
Should I leave the route as is, and thrw an error if the current_user.id != param[:id], forcing the front end client calling the api to track the logged in user's id?
Should I make a special route /user/self/edit and in the controller check to see if param[:id] == 'self'?
I would've added special routes for current user profile actions, in this case you don't have to check anything. Just load and display the data of current user. For example:
/my-profile/edit
/my-profile/newsfeed
It's not that RESTful but you don't have to put extra checks keeping your code clean.
If you still have to have (or want to have) a strict RESTful routes then I would use a before_filter and check if the id = current_user.id. If not then return 401 or 403.
I only want the current_user to have access to edit its profile. I
don't want other users able to edit profiles that don't belong to
them.
What I suggest is to use some authorization gems like pundit
Sample code:
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
def edit?
#current_user == #user
end
end
Also with an authentication gem like Devise, only the current_user(the users who logged in) can only access and edit their profiles
I would say that you are doing it correctly, just keep your current route as it is right now. And what you should do is to add a restriction in your controller instead. I would assume that you are using Rails, and working on users_controller.
class UsersController < ApplicationController::Base
def edit
if current_user.id == params[:id]
# do your work
else
render :404
end
end
end
Or you could clean up your controller by moving the restriction into a callback instead:
class UsersController < ApplicationController::Base
before_filter :restrict_user, only: [:edit]
def edit
# do your work
end
private
def restrict_user
render :404 unless current_user.id == params[:id]
end
end
You can add the gem "cancancan" and after the initialize....
class Ability
include CanCan::Ability
def initialize(user)
can :update, User do |user|
user.id == params[:id]
end
end
end
Then add this authorize! :edit, #user to your update action
You're going to need to add authorization code in all the user_controller methods as another comment suggested. Usually what I do in apps where a user is only supposed to edit their own profile I add a /profile route for a user to edit their own profile and then on the main /users/:id/* routes I add logic to prevent non-admin users from accessing those routes.
User is able to view his profile /users/1 or edit his profile /users/1/edit. From users perspective this URLs are absolutely fine.
There is no links which may lead user to edit the another user. You are trying to cover the different situation: when someone manually trying to craft the URL and get access to another account. I would not call them hackers, but technically they are – users who are trying to exploit your website to pass the restrictions.
You don't have to worry about "hackers" convenience. I'm always use current_user in edit action so nobody can edit wrong profile whatever his profile is.
def edit
#user = current_user
end
Also, I need to mention that you should also cover update action with such checks. With edit you may only get data (and probably only wide-public open data, unless you put billing information or plain-text-passwords inside your edit template). But with update you can actually change the data, which may be more destructive.
Because it seems that the only available user resource should be the authenticated user, I think the best way to solve this is
GET /user
PUT /user
GET /user/newsfeed
If you like to extend the api usage in future so that one user could have access to other user resources, than you need a solution that includes the user ids. Here it makes sense to introduce the routes for "self", too. But then you also have to implement an access check on server side.
GET /user/id/:id
PUT /user/id/:id
GET /user/id/:id/newsfeed
GET /user/self
PUT /user/self
GET /user/self/newsfeed
But I think you should keep it as simple as possible
For further investigations I would propose books like http://apigee.com/about/resources/ebooks/web-api-design which give a good introduction into API design
Since you only care to provide RESTful endpoints only for the currently authenticated user, which is available in your controllers as current_user, i say you don't need the id identifier parameter. I suggest using the following routes:
GET /user => users#show
PUT/PATCH /user => users#update
GET /user/edit => users#edit
You should keep the url as it is. Authentication and Authorization are separate concerns. 'current_user' refers to the user who is authenticated to access the apis. The id in the url identifies the resource on which 'current_user' is working, so does he have access to that resource or not is the concern of authorization. So you should add current_user.id != param[:id] (as you mentioned) in your api permissions and throw 403 status code in response.
You should use this route:
PUT /user/me
Note that there is no need for "edit": you should use the PUT method instead.
Also, you should explicitly define the route I've written above, instead of checking if id == 'self'.

Rails Devise guest users tokens

There is a Rails hotel list app with Devise/CanCan set up
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:show]
load_and_authorize_resource
1) What is the most elegant way to let owners edit their hotel info without registering them as users?
2) What if we create a unique token for every hotel and email something like the following link to the corresponding owner:
http://myapp.io/hotels/10010/edit?token=AAAABBBBCCCCDDD
..how to configure Devise/CanCan so they authenticate the user and allow them edit the corresponding record in the Hotels table?
Thank you in advance!
Regards,
Serge.
A quick and dirty solution would be to override the authenticate_user! method and add a check for the token:
# application_controller.rb
def authenticate_user!
if (token = params[:token] || session[:token])
#current_user = User.find_by_token token
session[:token] = token if #current_user
else
super
end
end
First you'd have to add the token column to the user table and you'd still need to create a user for them. Generate a token and email them the link with their token. When they first arrive at the site with the token in the URL, the above method will check if there's a matching record in the db and set it as the current_user and save the token to the session so that subsequent requests will still work. Don't forget to clear that session later (logout or expires).
The reason you still need to create a user record is that you don't want to start overriding your cancan logic and you need a place to store the token anyway.
There's also some gems that will add token authentication:
https://github.com/gonzalo-bulnes/simple_token_authentication
And a simple Rails built in solution:
http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

How to perform a security authentication check in a Rails server

I would like to use a web server based on Rails.
But I have no idea about how to check a user's identification.
For example, a user named Guest could only perform actions like GET and UPDATE on certain tables, while another user named Admin could perform all possible actions such as POST.
I am new to this area, but I heard there are some technicals like SQL injection could threaten the security of the web server.
So, could you tell me how to check the authentication and how to encrypt password entered by the user?
What you seem to be wanting is authentication and authorization.
For authentication:
https://github.com/plataformatec/devise
https://github.com/vpereira/authlogic
https://github.com/thoughtbot/clearance
For authorization:
https://github.com/be9/acl9
https://github.com/ryanb/cancan
This is strictly speaking out of my personal experience. I have tried all of the suggested authentication and authorization gems mentioned above, but I always came to the conclusion that its not more or less work to just write it yourself, especially when your requirements a very simple. Consider this:
class ApplicationController < ActionController::Base
before_filter :authentication
def authentication
redirect_to '/authentication_form' unless session[:logged_in]
end
def authentication_form
... render the form
end
def login
if params[:username] == 'adam' && params[:password] == 'eva'
session[:logged_in] = true
redirect_to '/restricted_area'
else
render :action => 'authentication_form'
end
end
end
class RestrictedController < ApplicationController
def index
... this action is now restricted
end
end
This is not complete, of course but it demonstrates how easy authentication can be with rails. Instead of checking users and passwords through controller code, you could query the database like this:
if User.find_by_name_and_password(params[:username], params[:password])
session[:logged_in] = true
...
For authorization you would have to save the users identity within the session hash which allows you to restrict access from within every action (provided the controller is a derived from ApplicationController)
I hope, this helps.

Best way to restrict actions (edit/delete) with Ruby on Rails and Devise

I am fairly new to Ruby On Rails and right now I am doing a simple app. In this app a user can create many items and I use devise for authentication. Ofcourse I want to make sure that you are the owner in order to delete items (Teams, Players etc) and the way I do it now is:
def destroy
#team = Team.find(params[:id])
if current_user.id == #team.user_id
#team.destroy
redirect_to(teams_url, :notice => 'The team was deleted.')
else
redirect_to root_path
end
end
Is this the best way? I was thinking about putting a method in the model but I am not sure I can access current_user from there. I was also thinking about a before_filer, something like:
before_filter :check_ownership, :only => [:destroy, :update]
I that case and if I want to code only one method for all objects (all objects this relates to have a "user_id"-field)
In my application controller I put:
before_filter :authorize
def authorize
false # Or use code here to check if user is admin or not
end
Then I override the authorize method in my actual controller to allow access to various actions.
You're looking for an authorization solution on top of your authentication (devise)
You can't access current user in the model no. I've had a fair amount of success using Makandra's Aegis for authorization. It allows you to create roles specify permissions attributed to each role. The docs are pretty good and I know it works fine with Devise, it's pretty agnostic that way, I've also used it with Clearance. It also passes an implicit "current_user" to your permissions so you can specify and check that your current user can take appropriate actions.

Resources