CanCan load_and_authorize_resource triggers Forbidden Attributes - ruby-on-rails

I have a standard RESTful controller that uses strong parameters.
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(safe_params)
if #user.save
redirect_to #user, notice: t('users.controller.create.success')
else
render :new
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(safe_params)
redirect_to #user, notice: t('users.controller.update.success')
else
render :edit
end
end
def destroy
#user = User.find(params[:id])
if current_user != #user
#user.destroy
else
flash[:error] = t('users.controller.destroy.prevent_self_destroy')
end
redirect_to users_url
end
private
def safe_params
safe_attributes =
[
:first_name,
:last_name,
:email,
:password,
:password_confirmation,
]
if current_user.is?(:admin)
safe_attributes += [:role_ids]
end
params.require(:user).permit(*safe_attributes)
end
end
In my config/initializers I have the file strong_parameters.rb
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
When I add a simple call to CanCan's load_and_authorize_resource I get
1) UsersController POST create with invalid params re-renders the 'new' template
Failure/Error: post :create, user: #attr
ActiveModel::ForbiddenAttributes:
ActiveModel::ForbiddenAttributes
# ./spec/controllers/users_controller_spec.rb:128:in `block (4 levels) in <top (required)>'
Where #attr in the test is defined as
before(:each) do
#attr =
{
first_name: "John",
last_name: "Doe",
email: "user#example.com",
password: "foobar",
password_confirmation: "foobar"
}
end
In the tests I have it all setup properly to login the user and give them the necessary roles for being an administrator so I know it's not that. I don't know why this is causing ForbiddenAttributes to trigger. I'm sure it's something simple I've overlooked. Has anyone else encountered this problem and found a solution to it?

I believe this is because CanCan will use its own getter method for the requested resource if you don't pre-load it with a before_filter. So you could add this to the controller and it should work:
class UsersController < ApplicationController
before_filter :new_user, :only => [:new, :create]
load_and_authorize_resource
def new_user
#user = User.new(safe_params)
end
end
(And then do the same for the edit/update actions.)

before_filter do
params[:user] = safe_params
end
load_and_authorize_resource

Related

ruby tutorial Listing 10.61: Action-level tests for admin access control failed

I've been following Rails Tutorial from Michael Hartl, https://www.railstutorial.org/book/updating_and_deleting_users.
I am having an error to pass one of the action level tests for admin access control test.
The test failed is as follows:
def setup
#user = users(:jessie) #admin
#other_user = users(:brenda) #non_admin
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to root_url
end
The error that the terminal gives me is :
FAIL["test_should_redirect_destroy_when_logged_in_as_a_non-admin", UsersControllerTest, 1.6799899999750778]
test_should_redirect_destroy_when_logged_in_as_a_non-admin#UsersControllerTest (1.68s)
Expected response to be a redirect to root_url but was a redirect to login_url.
Expected root_url to be === login_url.
test/controllers/users_controller_test.rb:65:in `block in '
My users_controller.rb file is:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def show
#user=User.find(params[:id])
end
def new
#user= User.new
end
def create
#user=User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def index
#users = User.paginate(page: params[:page])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
#Confirms a logged_in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the current user.
def correct_user
#user = User.find(params[:id])
recirect_to(root_url) unless current_user?(#user)
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Confirms an admin user
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
As I've been following the tutorial strictly and I am still very new to ruby on rails, it's very hard for me to identify where went wrong. I've been googling for solutions for a few hours, but in vain. Any help would be greatly appreciated. Thank you :)

"NoMethodError: undefined method `admin?' for nil:NilClass" - Test Error

I am currently in the process of learning Ruby on Rails through Michael Hartl's Rails Tutorial (Chapter 12). I am suddenly getting the below error.
UsersControllerTest#test_should_redirect_destroy_when_not_logged_in:
NoMethodError: undefined method admin?' for nil:NilClass
app/controllers/users_controller.rb:92:inadmin_user'
test/controllers/users_controller_test.rb:48:in block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:47:inblock in '
Here is my test code:
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete :destroy, id: #user
end
assert_redirected_to login_url
end
And the rest of my code:
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
Can someone please have a look and tell me where I am going wrong? The code has basically been copied from the tutorial so I'm at a loss.
Thanks.
I had this same error in the Hartl tutorial. The problem was a missed :destroy action in listing 9.53 in users_controller.rb. Tests were back to green once I found that missing bit.
That said, it would be great if someone could explain precisely why this is the case.
The correct code:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
Change admin_user method like this:-
def admin_user
redirect_to(root_url) unless current_user.present? && current_user.admin?
end
This will check first that current_user is present or not then check second condition(current_user.admin?). If current_user.present? will false then it will not check second condition.
You need to update your admin_user method to check if user is even logged in.
# Confirms an admin user.
def admin_user
redirect_to(root_url) if current_user.nil? || !current_user.admin?
end

Multi user in devise + Rails

I want to create multi user application. Admin user can create new users. how can i do this using devise. Because when after login as admin user i want add new user devise show error that "you are already signed in". How i do this using devise.
I was able to create the Admin User and logged in
User-Controller
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def user_params
params.require(:user).permit(:email, :username, :password, :password_confirmation,:propic)
end
end
Admin Controller
class ClientsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:index, :new, :create]
def new
#client = Client.new
#client.build_owner
render layout: 'sign'
end
def index
#clients = Client.all
render layout: 'welcome'
end
def create
#client = Client.new(client_params)
if #client.valid? then
Apartment::Tenant.create(#client.subdomain)
Apartment::Tenant.switch(#client.subdomain)
#client.save
redirect_to new_user_session_url(subdomain: #client.subdomain)
else
render action: 'new'
end
end
private
def client_params
params.require(:client).permit(:name, :subdomain, owner_attributes: [:email, :username, :password, :password_confirmation,:propic])
end
end
Application Controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :load_tenant
before_filter :authenticate_user!
#rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
def record_not_found
render 'record_not_found'
end
def load_tenant
Apartment::Tenant.switch(nil)
return unless request.subdomain.present?
client = Client.find_by(subdomain: request.subdomain)
if client then
Apartment::Tenant.switch(request.subdomain)
else
redirect_to root_url(subdomain: false)
end
end
def after_signout_path_for(resource_or_scope)
new_user_session_path
end
end
Anyone? I am super new to Ruby on Rails. All the code is the result of hefty trial and errors.

RoR - NoMethodError, undefined method

I have created an app with simple login authentication, it is actually a twitter clone. The user logs in and access the pages, etc.
But when the user posts something from there profile. It gives an error
NoMethodError in RibbitsController#create
undefined method `id=' for nil:NilClass
The error is around line 5:
class RibbitsController < ApplicationController
def create
#ribbit = Ribbit.create(user_ribbits)
#ribbit.userid = current_user.id
if #ribbit.save
redirect_to current_user
else
flash[:error] = "Problem!"
redirect_to current_user
end
end
private
def user_ribbits
params.require(:ribbit).permit(:content, :userid)
end
end
The request given to app:
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"dwVmjDNO4GOowphGFgChMDBxBfvka+M/xSUHvJMECzwxtv4NF6OuWtiaX74NLz91OwQJ9T9+wm7yMiPQ0BLpGA==",
"ribbit"=>{"content"=>"hi. test.\r\n"},
"commit"=>"Ribbit!"}
The sessions controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
session[:userid] = user.id
redirect_to rooturl, notice: "Logged in!"
else
flash[:error] = "Wrong Username or Password."
redirect_to root_url
end
end
def destroy
session[:userid] = nil
redirect_to root_url, notice: "Logged out."
end
end
The users controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user, notice: "Thank you for signing up!"
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#ribbit = Ribbit.new
end
private
def user_params
params.require(:user).permit(:name, :username, :email, :password, :password_confirmation, :avatar_url)
end
end
And the application controller
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
I would really appreciate it if you guys would help!
Thanks.
You're trying to assign current_user.idto #ribbit.userid without ensuring that current_user is set. 'current_user' would be set only if a user has been previously saved before.
Therefore, you need either to make sure that an authenticated user is trying to create a Ribbit, or if you consider the userid as a non mandatory field, you can simply change your line 5 by:
#ribbit.userid = current_user.id unless current_user.blank?
If you only want authenticated user to create Ribbits, then consider using a gem to handle authentication such as Devise. You could then use before_filter :authenticate_user! in your controller to make sure users are properly authenticated.

declerative_authorization on User problem

I am trying to block all default methods except create and update in my users controller using declerative_authorization. But at the time I add filter_resource_access or filter_access_to into my usersController i always get "Couldn't find User without an ID". Anyone care to explain why this could be happening?
class UsersController < ApplicationController
filter_resource_access
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Account registered!"
redirect_to account_url
else
render :action => :new
end
end
def show
#user = #current_user
end
def edit
#user = #current_user
end
def update
#user = #current_user # makes our views "cleaner" and more consistent
if #user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to account_url
else
render :action => :edit
end
end
end
You should set the #user variable before the filter_access_to call with a before_filter as declarative_authorization tries to access #user when you call filter_access_to.
before_filter :set_user
filter_access_to :all
...
protected
def set_user
#user = #current_user
end
Maybe you are setting the attribute_check parameter to true in your filter_access_to call? I have a similar controller and I don't really need the before_filter.
Another thing that might be causing it is a using_access_control call in your User model.

Resources