Authorizing users so they can only view their own objects - ruby-on-rails

I have a user model and users have many patients. A user should only be able to view their own patients.
What's the best way to implement this?
Here is my original patients show action. This allows any logged in user to view any patient:
def show
#patient = Patient.find(params[:id])
#assessments = #patient.assessments
end
I can switch it to below, but then I get an error page (patient id not found) and I'm not sure if this is the best way to handle it:
def show
#patient = current_user.patients.find(params[:id])
#assessments = #patient.assessments
end
What's the best approach for handling simple authorizations like this? Nothing fancy, just want users to only be able to view and edit their own stuff.
Edit
Here is the error I get when trying to access another user's patient:
Couldn't find Patient with 'id'=27 [WHERE "patients"."user_id" = ?]
Instead, I'd prefer to show the user a flash message saying they're not authorized.

def show
#patient = Patient.where(id: params[:id],user_id: current_user.id)
#assessments = #patient.assessments
end
here #pateint will give all the patient of the current login users & #assessments will give asssessments related to patients.

You could use find_by, which does not throw an error if nothing is found.
def show
#patient = Patient.find_by(id: params[:id])
if #patient && #patient.user == current_user
#assessments = #patient.assessments
elsif #patient.user != current_user
flash.now[:danger] = 'You are not authorized!'
else
flash.now[:danger] = 'The patient was not found'
end
end
Then you should test in your view if the patient exists.
That looks a little messy and repetitive, however, so you could refactor, for example by making a new function. Put this in the model patient.rb, and if current_user comes from application_helper, you may have to include ApplicationHelper.
authorized?
self.id == current_user.id
end
Now the conditional of the first code example could be like this:
if #patient
#assessments = #patient.assessments
unless #patient.authorized?
flash[:danger] = 'You are not authorized!'
# here, redirect the user away from the show page
end
else
flash.now[:danger] = 'The patient was not found'
end

Related

ERROR - Couldn't find User with 'id'=2 - unable to solve

im using rails to make a basic chair selling website. At the moment someone can post a new chair and people can look at each individual show page. However im having issues with the show page when trying to display the email of who posted the chair.
If i click on the first chair, it works fine, however if i click on the other it says 'Couldn't find User with 'id'=2'. I have made all the chairs under 1 user.
thanks. let me know if you need anymore info.
class ChairsController < ApplicationController
def index
#chairs = Chair.all
end
def show
#chair = Chair.find(params[:id])
#user = User.find(params[:id])
end
def new
#chair = Chair.new
#user = current_user
end
def create
#user = current_user
#chair = Chair.new(chair_params)
#chair.user = #user
if #chair.save
redirect_to chairs_path
end
end
private
def chair_params
params.require(:chair).permit(:name, :description)
end
end
<h1><%= #chair.name %></h1>
posted by <%= #user.email %>
At the moment you try to load the user by the same params[:id] as the chair your show method. Change your show method to:
def show
#chair = Chair.find(params[:id])
#user = #chair.user
end

How I Pass parameters from View to Controller In Ruby

#app/controllers/sessions_controller.rb
class SessionController < ApplicationController
def new
#session = Session.new
end
def fetch
##user = User.session(params [:user])
redirect_to "http://www.google.com"
end
def create
emai = params[:email]
puts emai
user = User.find_by(:email => session[:emai])
#user = User.find_by (params [:email])
#user = User.find_by email: 'abc#xyz.com'
#user = User.find_by(params[:Email])
#if (session[:Email] = user.email)
if (user)
redirect_to "http://www.yahoo.com"
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
redirect_to "http://www.google.com"
end
#redirect_to "http://www.yahoo.com"
end
end
every time i execute my view i get redirected to google.com even though i pass the parameters.
Edit by R Peck:
My logic should send people to Yahoo if the params are set, but still sends to Google, how can I fix this?
Try:
user = User.find_by(:email => params[:sessions][:emai])
You are not getting the value of email if you only call params[:email] you should call parent first before calling the child params[:sessions][:email].
Several things wrong with your code.
Here's what I'd write:
#app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
#session = Session.new
end
def create
email = params[:sessions][:email]
user = User.find_by email: email
url = user ? "google" : "yahoo"
colour = user ? "valid" : "invalid"
notice = user ? "You signed up successfully" : "Your form is invalid"
redirect_to "http://#{url}.com", notice: notice, color: colour
end
private
def session_params
params.require(:session).permit(:session, :params)
end
end
OOP
I think this may be a little advanced but I'll write it anyway, for my own benefit.
Rails is object orientated (it's built on Ruby which is an OOP language). This means that each time you create/call a controller, it should be centered around objects.
A good example for you would be the Devise controllers.
This has a sessions_controller which essentially allows you to CRUD (Create Read Update Destroy) a session. This is the correct way to use a controller.
Your implementation seems to be dealing with a user, rather than a session, and as such you'd be best using a users_controller to fix it:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new
#user.save
end
end
Having said that, it does seem that you're probably going to resolve the issue to make it so that you can use the User to build a new session.
I guess it's best to remember that you have to ensure you're able to appreciate a good structure for your application

Redirect Loop Rails 4

I've been beating my head against the wall on this one - I have code that I use to test if a user is trying to access an account they're associated with and if not redirects them to their own account. Basically it's to stop people from accessing via URL routes accounts other than theirs.
This code works without issue on other sites I've built but for some reason it's not working on a new project.
The code in question:
def set_company
if params[:id].nil?
#company = Company.find(current_contact.company_id)
else
#company = Company.find(params[:id])
if #company.id != current_contact.company_id
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' )
end
end
end
In theory when the if statement returns true, it should redirect to the company_path with the params ID set from the current user. On the second run of this code it should find the company based on the params id and then return false when checking if the company ID doesn't match the current user's company id, but instead I get a "this page has a redirect loop" error.
Instead of hitting the db with the find method - I think you'll be better validating against pure values, and only then performing the find method:
def set_company
if params[:id].nil?
#company = Company.find(current_contact.company_id)
else
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' ) if params[:id] != current_contact.company_id
#company = Company.find params[:id]
end
end
try this..using present
def set_company
if params[:id].present?
#company = Company.find(current_contact.company_id)
else
#company = Company.find(params[:id])
if #company.id != current_contact.company_id
redirect_to(company_path(current_contact.company_id), alert: 'You can\'t access that account This is not your account!!! This one is.' )
end
end
end

How do I restrict access to edit action and through URL entry?

I have a relationship user ("devise") that has many events.
I want to prevent users from editing events that do not belong to them and stop users from accessing the edit action by entering something like 'http://localhost:3000/events/65/edit' into the browser.
I also want to redirect the user back to the page they were on when clicking on the edit event link.
I tried the following two methods without success:
def edit
if current_user == #event.user_id
#event = Event.find(params[:id])
else
redirect_to events_path
end
def edit
#event = Event.find(params[:id])
unless session[:id] == #event.user_id
redirect_to events_path
return
end
end
If you only need this kind of authorization logic in this controller, something like this would be possible:
class User < ActiveRecord::Base
has_many :events
end
class EventsController < ApplicationController
def edit
#event = current_user.events.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to events_path, notice: "You cannot edit this event."
end
end
The rescue-block is really optional. If you remove it, the user will get a 404 Not found error message if she visits the edit URL for an event she didn't create,
If you expect to use authorization other places in your application, I would advise you to look into CanCan. It's a gem that sentralizes rules for authorization and access in an Ability class.
Try adding a before filter (it can be used for other actions as well if needed):
before_filter :check_user, :only => [:edit]
def check_user
#event = Event.find(params[:id])
unless current_user.id == #event.user_id
redirect_to (request.referrer || root_path)
return
end
end
The idea behind your first method is fine, but that comparison will always fail. current_user is a User object; #event.user_id is an integer (or possibly some form of UUID).
You need to either compare a User object to a User object:
if current_user == #event.user
Or an ID to an ID:
if current_user.id == #event.user_id

Wants to create a new instance of books instead of update

What I want is to create a profile pages, where i can view the previous book field and if the customer change the text then it would create a new books.
I have the following models with has_many relationship
Customer -- ID, First, Last, Email
Book -- ID, Description
Book_Managers -- ID, Customer_id, Book_id, Visible
Right now what i have is a customer edit which allow me to see multiple form by rendering from many more models like books, phones, etc...
Here my customer Controller
def edit
#customer = Customer.find(params[:id])
if #customer.books.any?
#book = #customer.books.order("created_at DESC").first
else
#book = #customer.books.build
end
end
What i would like to see is if i created a new instance when going to book form i should see the last and able to modify "The JavaScript Bible" to something "The Java Bible" and it would not update it but just create a new version. Right now when going to the form book i see nothing. And if i do for some odd reason it was only allowing me to update.
class BooksController < ApplicationController
def create
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Created"
redirect_to root_url
else
render 'customer/edit'
end
end
def index
#books = Book.all
end
def destroy
#book.destroy
redirect_to root_url
end
end
ADDED THIS
def update
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Updated"
redirect_to root_url
else
render 'customer/edit'
end
end
To my book controller, the only problem right now is my association, i can't seem to find any book with the current customer. is there somethign wrong with my query?
There is some gems for versioning. This that : https://www.ruby-toolbox.com/categories/Active_Record_Versioning
You can do something like this :
def update
params = params[:book].merge(:previous_version => params[:id])
#book = current_customer.books.create(params[:book])
end
It will create a new book on each update. The last version will be the book without "previous_version".

Resources