has_many association rails - ruby-on-rails-3

I have a User model and a Job_Category model, the Job_Category model
belongs_to :user
The User model
has_many :job_categories, :dependent => :destroy
I have a Dashboard Controller and am trying to display all the Job_Categories for a specific logged in User.
class DashboardController < ApplicationController
before_filter :authenticate_user!
def index
#user = User.find(params[:id])
#job_categories = #user.job_categories
##job_categories = JobCategory.all
#respond_to do |format|
# format.html # index.html.erb
# format.xml { render :xml => #job_categories }
# end
end
However, when I am trying to display this I get the error 'Couldn't find User without an ID' . I see this in the log:
Processing by DashboardController#index as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE ("users"."id" = 2) LIMIT 1
Completed in 22ms
ActiveRecord::RecordNotFound (Couldn't find User without an ID):
app/controllers/dashboard_controller.rb:9:in `index'

First of all, you cannot try to find by params in console, because there ain't such thing.
Try this in console:
User.find(2)
(..default column where find method looks the value is primary key which I assume is id as usual)
Then another thing, if you're already authenticating user in before_filter, you might already have #current_user or something set, are you using some authentication plugin like devise or something? So there's no need to pass current users id to dashboard controller. Application controller already knows who this user is and your dashboard controller is a subclass of it.
And ensure your job_category table has user_id column too.

Related

Rails - Show action for nested resource is deleting the record?

I am building a simple web app with a typical User model & Profile model. The user model has_one profile & the profile model belongs_to user. All is going pretty well as I am basically following Michael Hartl's tutorial (except he uses has_many for microposts).
So here's the deal. I create a user, then create a profile. I know both records exist b/c I verify their presence in my sqlite3 db browser. However, as soon as I try to render the show view by visiting -> localhost/3000/profiles/1, I get the error shown below. But the even weirder part is that now when I check my db, the profile record is gone. Please help!
Note: I have a feeling it has something to do with dependent destroy (b/c removing it will eliminate this issue), but I have no idea why. Furthermore, I think I want dependent destroy anyway. I don't want any stray profile records if there are no corresponding users, right?
Routes
resources :users
resources :profiles
Models
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
class Profile < ActiveRecord::Base
belongs_to :user
ProfilesController
def new
#profile = current_user.build_profile
end
def create
#profile = current_user.build_profile(params[:profile])
if #profile.save
flash[:success] = "Profile created!"
redirect_to root_path
else
render 'new'
end
end
def show
#profile = Profile.find(params[:id])
end
views/profiles/show.html.erb
<p>Display Name: <%= #profile.display_name %></p>
This is the error message I get when I try to visit -> localhost/3000/profiles/1
ActiveRecord::RecordNotFound in ProfilesController#show
Couldn't find Profile without an ID

Applying cancan to custom actions

I'm working on a polling app and use Devise and Cancan for authentication and authorization. I have a User model generated by Devise and a Poll model. A poll belongs to a user. My problem is that I have a custom action in my Polls controller and I can't get Cancan to work with that custom action. This is what I tried to do in my code:
config/routes.rb:
match 'users/:user_id/polls' => 'polls#show_user'
Ability.rb:
def initialize(user)
user ||= User.new
if user.is? :admin
can :manage, :all
else # default
can :read, :all
can :manage, Poll, :user_id => user.id
can :show_user, Poll, :user_id => user.id
end # if else admin
end
polls_controller.rb:
class PollsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource :except => :show_user
def show_user
authorize! :show_user, user
#polls = Poll.find_all_by_user_id(params[:user_id])
render "index"
end
<...>
end
The idea is that a user's polls can be viewed only when the owner of the poll is signed in. However, with this code, when a poll's owner is signed in, that user gets kicked out of that page with a message that says authorization failed. If I remove the line authorize! :show_user, user, then a user who's signed in can view all other user's polls (the authorization doesn't work at all).
Can anyone see what I might be missing? Thanks in advance!
In abiltity.rb, you're verb/noun combination is :show_user and Poll, but in your controller you're using :show_user and user--you would need to use a Poll instead.
If, instead you want to allow the user to view all their own Polls, you might go with something like:
ability.rb:
can :show_polls_for, User, :id => user.id
polls_controller.rb:
def show_user
authorize! :show_polls_for, user
...
end

Limit users to only access their profiles using Devise

I have currently set my app so that on successful sign in the app redirects the user to their profile at localhost:3000/users/id however if I am the first user id => 1 and type users/2 I have full access to this profile. I have been trying to find how to stop this using devise. I'm pretty new to rails so I'm sure I'm missing something simple, I have used the before_filter :authenticate_user! but this is obviously just checking if a user is signed in, but doesn't limit access to other users' profiles. I have read a bit on CanCan but this seems a bit overkill for what I am trying to achieve. Any pointers much appreciated.
users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
before_filter :user_authorization
def index
#users = User.all
end
def show
#user = User.find(current_user[:id])
end
private
def user_authorization
redirect_to(root_url) unless current_user.id == params[:id]
end
end
This is being reported from the server:
Started GET "/users/2" for 127.0.0.1 at 2012-06-24 13:00:38 +0200
Processing by UsersController#show as HTML
Parameters: {"id"=>"2"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
Redirected to http://localhost:3000/
Filter chain halted as :user_authorization rendered or redirected
Completed 302 Found in 20ms (ActiveRecord: 0.8ms)
In your controller:
class UsersController < ApplicationController
before_filter :validate_user, :only => :show
def show
#user = User.find(params[:id]
end
def validate_user
redirect_to courses_path unless current_user.id.to_s == params[:id]
end
end
Notice the current_user.id.to_s since current_user.id is an integer and params[:id] is a string.
In general, I'd say there are two approaches to solving this kind of problem:
Rolling your own code and implementing checks in your controllers (or potentially in your model classes), and
using a gem that enforces rules for you.
If you want to role-your-own, the simplest way would be to simply put checks in your controller that makes sure they get redirected if they try to look at a profile that isn't theirs. One way to do this using a before filter is this, though you'd want to adapt it for the behavior that makes sense for your app.
before_filter :validate_user
def validate_user
redirect_to home_path unless current_user and current_user.id == params[:id]
end
If you want to use a gem, then I'd recommend cancan as you've mentioned or another gem called Acts as Tenant. I've seen it used for similar things. But if all you want is to lock down the user profile, adding code to the controller probably works fine.
And voilĂ :
before_filter :user_authorization
private
def user_authorization
redirect_to(root_url) unless current_user.id == params[:id]
end
current_user is an helper that contains current logged user.

Writing spec for devise RegistrationsController that will make sure users can't change their role - cryptic error message

I have a rails 3.1 app using Devise and CanCan to mange users and roles. I want to make sure that users can update their password, but not their roles. (So ordinary users can't give themselves an admin role, basically). I have overridden the Devise class "RegistrationsController" with some code like this:
def update
# this is my attempt to stop people from updating their roles
# and giving themselves "Admin" priveledges.
params.delete("role_ids")
super
end
I'm hoping this will prevent hackers from updating the "role_ids" field in the user to change their priviledges. (If there is a better way to achieve this, please say!) My problem is I can't seem to write a spec that will test that this code works. My spec looks like this:
require 'spec_helper'
describe RegistrationsController do
before (:each) do
#user = Factory(:user)
sign_in #user
end
it "should update the user attributes but not the roles" do
user_params = {"name" => "new_name", "role_ids" => ["2"],}
put :update, { :id => #user.id, :user => user_params}
#user = User.find(#user.id)
#user.name.should == "new_name"
#user.roles.should be_empty
end
end
The trouble is this test doesn't execute. I get an error message like this:
Failures:
1) RegistrationsController should update the user attributes but not the roles
Failure/Error: put :update, { :id => #user.id, :user => user_params}
AbstractController::ActionNotFound:
Could not find devise mapping for path "/user?id=29&user%5Bname%5D=new_name&user%5Brole_ids%5D%5B%5D=2".
Maybe you forgot to wrap your route inside the scope block? For example:
devise_scope :user do
match "/some/route" => "some_devise_controller"
end
# ./spec/controllers/registrations_controller_spec.rb:13:in `block (2 levels) in <top (required)>'
I don't understand what the error message is asking me to do. My routes seem fine and my application seems to work otherwise. Can anyone help?
Try this in your setup
#request.env["devise.mapping"] = Devise.mappings[:user]
For details see How To: Controllers and Views tests with Rails 3

Rails & Devise: How to authenticate specific user?

I'm using Devise for the first time with rails, and I'm having trouble with one thing:
I used the provided authenticate_user! method in my user's controller to restrict access to pages like so:
before_filter :authenticate_user!, :only => [:edit, :show, :update, :create, :destroy]
But this allows any signed in user to access any other users :edit action, which I want to restrict to only that user. How would I do that?
In your edit method, you need to do a check to see if the user owns the record:
def edit
#record = Record.find(params[:id])
if #record.user == current_user
#record.update_attributes(params[:record])
else
redirect_to root_path
end
end
You should look into Authorization such as CanCan. Or alternatively create a new method like so:
# Create an admin boolean column for your user table.
def authenticate_admin!
authenticate_user! and current_user.admin?
end