Users seeing other users content w/ Devise - ruby-on-rails

I'm building something simple on Rails and I have Devise installed. Users log in and create links - I have create one account, made a few links and then when I create another account I can go to /links/5 and still see the content that another user has posted.
I'm looking to make this App as personal as possible, any ideas on how to solve this?

One option is to use a simple before_filter in the LinksController. Something like:
def check_authorization
if current_user != link.user
redirect_to ... (Redirect as appropriate for your app)
end
end
This assumes there's a link method that will get the current link. Something like
def link
#link ||= Link.find(params[:id])
end
As an earlier commenter mentioned, Cancan is also a viable option.

Related

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'.

Security - Checking if current user exists before executing code

In one of my controllers I have this method:
def method_name
if current_user
#model = Model.find(params[:id])
if #model.destroy
flash.alert = 'Model deleted successfully'
redirect_to models_path
end
end
end
I check if there is a current_user assigned by devise before giving the ability for the #model to be deleted. Is this safe and sufficient in terms of security?
What I really do is just checking if current_user exists. So is there a way that somebody can "trick" the system that current_user does exist and as a result be able to trigger the commands included in the method?
You will get a spectrum of answers in this. But if you want the user to be logged in then just do this at the top of your controller:
before_filter :authenticate_user!
That is provided by devise and ensures that there is a logged in user before allowing any controller actions.
If you have simple authorization then yes, most likely though you are going to want to make sure that the user has the authorization to delete the object. You can do that several ways. My favorite one right now is the Pundit gem.
You could also just check that the user owns the object in order to be able to delete it. That code would look something like this
#model = Model.find(params[:id)
if current_user.id == #model.user_id
# Rest of your destroy code
end

rails 4 user authentication

I'm using devise with my rails 4 app to handle the authentication, and no problems there.
However, I want to make sure a logged in user can only view / edit (via the show and update actions) the items that his user owns (that are linked to his user_id).
I think I could hack something to make this all work by checking the current_user.id, but many users in Stackoverflow and other places say to use cancan-- however it appears cancan is dead and gone, and there's a replacement called cancancan, which may be ok, but I don't know.
Is there a standard way to do this in Rails 4, or is the best route to still use a third party gem like cancancan? Is there a better gem?
I've been using Pundit instead of Cancan for the last few projects I've done. It is lightweight, flexible and easy to use. Here's the link: https://github.com/elabs/pundit
In regards to your question, you will create policies for each model. For each action you define a method. It's super simple and explained on the link I've attached. Here as an example you have update in your model (models/post.rb):
def update
#post = Post.find(params[:id])
authorize #post
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
Call authorize to define permissions.
In your policies/post.rb:
class PostPolicy < Struct.new(:user, :post)
def update?
user.admin? or not post.published?
end
end
That returns true or false. In your case if you want to check if the user is a owner you can place the following if statement:
if user.admin? || user.owner_of?(post)
You get the idea. You can also define scopes, etc.
I don't think there's a standard, per se, but rather it's based on what you need. For Rails 4, cancancan brings a lot to the table and is built off of a gem that has been used regularly by the Rails community.
The only other alternatives I'm familiar with are protector and pundit - maybe check those out.
However, if cancancan and protector don't fit your needs, you could always roll your own authorization solution, but to me, why reinvent the wheel if cancancan will satisfy your needs.
I'd recommend Action Access, it's much simpler and straightforward. It boils down to this:
class ArticlesController < ApplicationController
let :user, :all
let :guest, [:show, :index]
# ...
def edit
not_authorized! unless #article.user == current_user
# ...
end
# ...
end
First of this will automatically lock the controller, allowing only users to access every action, guests can only show or index articles. Then not_authorized! will reject and redirect with an alert any user other than the owner of the article.
What's good about this is that it makes controllers to be self contained, everything related to the controller is within the controller. This also makes it very modular and avoids leaving forgotten trash anywhere else when you refactor.
It's completely independent of the authentication system (so no problem with Devise) but it bundles a set of handy model additions that allow to do things like:
<% if current_user.can? :edit, :article %>
<%= link_to 'Edit article', edit_article_path(#article) %>
<% end %>
Here :article refers to ArticlesController, so the link will only be displayed if the current user is authorized to access the edit action in ArticlesController. It supports namespaces too.
You can lock controllers by default, customize the redirection path and the alert message, etc. Checkout the documentation for more.

How to allow only Admin (or some user) to edit the pages in Rails?

I have a scaffold Finances and I just realized that it can be edited by any logged in user by going to /finances/1/edit
I have installed activ_admin gem but I don't think it is what I need. How to make sure other than admin (or may be some users) no one can edit finances resource type- I
EDIT - I found https://github.com/EppO/rolify, is this best option or I still can do something better as it may be overkill ?
EDIT 1 - I went through this https://github.com/EppO/rolify/wiki/Tutorial and have assigned role "admin" to user = User.find(1), everything went well upto "ability.can? :manage, :all" in console, which shows TRUE for user 1 and false for other users. Now I am not able to figure out what to do ? I can still see all users being able to edit the page even though I have added "resourcify" in the finance.rb model. Any help ?
Well, I personally use rolify for my project and love it.. but to be honest this is super easy to achieve by simply adding a column "admin" to your User model and having it default to false. When you want a user to be an admin update the attribute to true and then require the User.admin==true to access the finances edit action... You can do this by redirecting the non-admin user from the controller (within the finances edit action)
By the way if you're using devise for auth check out Devise before_filter authenticate_admin?
I'm not sure how your models are set up, but lets say your User model has an admin column, you can do the following:
FinancesController < ApplicationController
before_filter :must_be_admin, only: :edit
def edit
...
end
private
def must_be_admin
unless current_user && current_user.admin?
redirect_to root_path, notice: "Some message"
end
end
end
You can add any actions needed to the before filter, e.g. before_filter :must_be_admin, only: [:edit, :destroy]
If you're looking to add sensible user authorization without rolling your own solution, definitely check out CanCan. Also helpful is this screencast by its author, Ryan Bates.

Rails 3, how to secure and protect controllers and urls

I'm sort of new to rails, what I want to to is protect users profile
what I mean is if user 1 login and go to edit his profile he can, but also if he change on the url to user to # 2 they can also change their information
localhost:3000/users/2/edit
I'm a little lost, any help will be greatly appreciated, or suggestions of good books/blogs
As part of authentication add a session variable, session[:user_id] = User.Authenticate(params[:user][:username], params[:user][:password) (this is the general pattern, you need to add another resource for authentication).
Then add a before_filter function to your controller and check if session[:user_id] == params[:id]. Look at it here: before_filter
The Rails Security Guide is probably a good place to start
Just in case this is useful to someone, something that I came across when testing my app was although users that hadn't signed in couldn't access restricted content, once a user was signed in, they could change the url to a another users id, eg.
/users/3 and it would then show that users home page. So any user could look at any other user, which wasn't what I wanted.
To get around this, I changed the user controller to secure against this:
class UsersController < ApplicationController
#first call the correct_user function before allowing the show action to proceed
before_filter :correct_user, only: [:show]
...
def show
#do whatever here
end
...
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end

Resources