Rails nested resource join security best practices - sql

I've started a new project which requires accounts to be able to view their candidates and related tasks.
account.rb
has_many :candidates
candidate.rb
has_many :tasks
belongs_to :account
task.rb
belongs_to :candidate
Routes are set up to allow /candidates/4/tasks/3 where account X has access to candidate 4 which has task 3.
tasks_controller.rb is currently like:
def show
#task = Task.find params[:id]
end
QUESTION: What is the best practice approach to ensure that other accounts don't have access to task 3?
One idea might be something like this but seems very messy:
def show
#task = Account.find(current_account).candidates.find(params[:candidate_id]).tasks.find(params[:id)
end
So if the join fails, you don't have access.
Another way might be done using scopes. Where you make sure all tasks queried are joined with candidates and current_account.
I could also do a before_filter to do a standalone query on candidates table to check that the account has access. This will add an extra query so not ideal.
I'm waffling here... but would love to know how others go about this?

Do it by rails way...
current_account = Account.find params[:id]
#tasks = current_account.candidates.collect(&:tasks)
This will do your job.

Related

Storing user_id in the paper_trail versions table

I am using paper trail to audit changes to data and would like to store the user_id of the current user in addition to the "whodunnit" column that paper_trail stores by default.
I had no trouble modifying the versions migration to add the user_id column. But I haven't figured out an easy way to set that column from the various models in my app.
It seems like this should work:
has_paper_trail :meta => { :user_id => current_user.id
}
And, I think it might work if I had access to the current_user in my models. But I don't. After researching how to get access to the current_user in my model, I see there is a philosophical debate here. That's not my question though.
So I'm thinking of using a gem like sentient_user or sentient_model to give me access to the current_user in my models so I can set it with something like the code above.
However, adding these gems seems complicated for the little thing I'm trying to do here. I'm wondering if there is an easier way.
What is the easiest way to add the user_id of the person who took the action to the versions table?
The current_user don't exists in models by itself, it appears from controller. So, standard approach is applicable:
class ApplicationController < ActionController::Base
def user_for_paper_trail
current_user if user_signed_in?
end
def info_for_paper_trail
{ user_id: current_user.id } if user_signed_in?
end
end
# config/initializers/paper_trail.rb
class Version < ActiveRecord::Base
attr_accessible :user_id
end

How to retrieve both "associated" records and "associated through" records in a performant way?

I am using Ruby on Rails 3.1 and I am trying to improve an SQL query in order to retrieve both "associated" records and "associated through" records (ActiveRecord::Associations) in a performant way so to avoid the "N + 1 query problem". That is, I have:
class Article < ActiveRecord::Base
has_many :category_relationships
has_many :categories,
:through => :category_relationships
end
class Category < ActiveRecord::Base
has_many :article_relationships
has_many :articles,
:through => :article_relationships
end
In a couple of SQL queries (that is, in a "performant way", maybe by using the Ruby on Rails includes() method) I would like to retrieve both categories and category_relationships, or both articles and article_relationships.
How can I make that?
P.S.: I am improve queries like the followings:
#category = Category.first
articles = #category.articles.where(:user_id => #current_user.id)
articles.each do |article|
# Note: In this example the 'inspect' method is just a method to "trigger" the
# "eager loading" functionalities
article.category_relationships.inspect
end
You can do
Article.includes(:category_relationships => :categories).find(1)
Which will reduce this to 3 queries (1 for each table). For performance, also make sure your foreign keys have an index.
But in general, I'm curious why the "category_relationships" entity exists at all, and why this isn't a has_and_belongs_to sort of situation?
Updated
As per your changed question, you can still do
Category.includes(:article_relationships => :articles).first
If you watch the console (or tail log/development) you'll see that when you call the associations, it'll hit the cached values and you're golden.
But I am still curious why you're not using a Has and Belongs To Many association.

Has_and_belongs_to_many in Rails 3

I've tried to look through stackoverflow to remedy my situation but unfortunately I've just made myself thoroughly confused.
I have two has_many to has_many relationships: Users has_and_belongs_to_many Roles, and Roles has_and_belongs_to_many Pods. I've already set up the correct tables and everything (i.e. the role_users and pod_roles tables): I know it's at least partially working because I'm able to do #user.roles and get all of that user's roles.
However, I want to take it one step further: I want to be able to do something akin to #user.roles.pods to get all of the pods pertaining to that user. After #user.roles.pods didn't work: here's what I tried to do:
#current_user_roles = current_user.roles
#pods = Array.new
#current_user_roles.each do |role|
#pods.push(role.pods)
end
#pods.each do |pod|
puts(pod.name+"-----------------")
end
Didn't work. This is just to try to get something super simple -- to try to get all of the pods' names to appear to I can tell if it's working.
Here's something you could try:
#pods = current_user.roles.collect { |role| role.pods }.flatten

How to make these rails routs

I'm trying to make some routes to view and manage missions in an app.
The missions belong to organizations.
My idea is to do something like this:
organizations/1/missons #(index) list of missions of the organization
organizations/1/missions/1 #show a mission that belongs to an organization
organizations/1/admin/missions #list of missions in a new view that has the commands to admin the missions
organizations/1/admin/missions/1/edit #edit the mission
organizations/1/admin/missions/1/destroy #destroy the mission
missions #all missions of all organizations
missions/1 #show mission page
I don't really know if this is a good way to rout a system like this or if it's overkill in a way.
For now I have the standard routing working:
resources :organizations do
resources :missions
end
But I wish to have some more views.
What's the best way to route this, and how many controllers to keep them lean and with few actions?
As always there are several ways to accomplish this. I would probably go with two different controllers for the regular missions, perhaps like this:
resources :organizations do
resources :missions, :only => [:index, :show]
resources :admin_missions
end
resources :missions, :only => [:index, :show]
This would of course result in "...admin_missions..." instead of "...admin/missions..." but I think it is the easiest way to do it. This will use three controllers where MissionsController is called from two different locations but you can check what to display by checking if the params[:organization_id] is present or not.
Edit
If you decide to use the same controller for all missions and for organizations, here is an example for how the index action could start:
def index
if params[:organization_id]
#organization = Organization.find(params[:organization_id])
#missions = #organization.missions.all
else
#missions = Mission.all
end
...
end
But if you want to have different views for them, then it might be better to separate to two controllers.

Constructing a has-and-belongs-to-many query

I have a rails app (running on version 2.2.2) that has a model called Product. Product is in a has-and-belongs-to-many relationship with Feature. The problem is that I need have search functionality for the products. So I need to be able to search for products that have a similar name, and some other attributes. The tricky part is that the search must also return products that have the exact set of features indicated in the search form (this is represented by a bunch of checkboxes). The following code works, but it strikes me as rather inefficient:
#products = Product.find(:all, :conditions=>["home=? AND name LIKE ? AND made_by LIKE ? AND supplier LIKE ? AND ins LIKE ?",hme,'%'+opts[0]+'%','%'+opts[1]+'%','%'+opts[3]+'%','%'+opts[4]+'%'])
#see if any of these products have the correct features
if !params[:feature_ids].nil?
f = params[:feature_ids].collect{|i| i.to_i}
#products.delete_if {|x| x.feature_ids!=f}
end
I'm sorry that my grasp of rails/sql is so weak, but does anyone have any suggestions about how to improve the above code? Thanks so much!
First, i would recommend you to manually write a FeatureProduct model (and not use the default 'has_and_belongs_to_many')
EG
class FeatureProduct
belongs_to :feature
belongs_to :product
end
class Product
has_many :feature_products
has_many :features, :through => :feature_products
end
class Feature
has_many :feature_products
has_many :products, :through => :feature_products
end
For the search: You may find the gem SearchLogic to be exactly what you need. It has support for 'LIKE' conditions (it means that you can write in a more 'Rails way' your query). It also has support for performing a search with conditions on a related model (on your Feature model, to be more precise).
The solution would be something like:
search = Product.search
search.name_like = opt[0]
search.made_by_like = opt[1]
...
search.feature_products_id_equals = your_feature_ids
..
#product_list = search.all
There is also an excellent screencast explaining the use of this gem.
Good luck :)