Reputation: 313
I am going through Michael Hartl's Tutorial on RoR 4.0 and am currently doing the first exercise in chapter 9.
I'm supposed to write a test that confirms that it's not possible to issue a PATCH request to edit the admin attribute on a user. To prevent mass-assignment, the tutorial introduces a user_params function that only permits certain attributes.
In the test, I am issueing
patch user_path(user), params
where params is a hash containing an a true value for admin (see below). After this request, I expect the attribute on the user to still be false.
Here's the problem
While the test (correctly) succeeds with my current code, it also (incorrectly) succeeds when I add the admin attribute to the list of allowed attributes in the user_params function.
Using curl to issue a PATCH request to /users/:id gives an error page that I don't quite understand yet. I've tried replacing PATCH by PUT, having read somewhere that support for this method is fairly new and thinking that I might not have the correct version for each and every gem. I'm using ruby 1.9.3, while the tutorial uses 2.x.
Is there anything I'm missing here? (Any hints on how to simulate these requests in the rails console would be helpful as well!)
I pasted the relevant parts of the code I am using below:
app/controllers/users_controller.rb
class UsersController < ApplicationController before_action :signed_in_user, only: [:edit, :update, :index, :destroy] before_action :correct_user, only: [:edit, :update] before_action :admin_user, only: :destroy ... def update @user = User.find(params[:id]) if @user.update_attributes(user_params) sign_in @user flash[:success] = "Profile updated" redirect_to @user else render 'edit' end end ... private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation, :admin) end ...
spec/features/users_pages_spec.rb
...
describe "edit page" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit edit_user_path(user)
end
...
describe "directly patch forbidden admin attribute" do
let(:params) do
{ user: { admin: true, password: user.password, password_confirmation: user.password } }
end
before {
patch user_path(user), params
}
specify { expect(user.reload).not_to be_admin }
end
...
app/models/user.rb
class User < ActiveRecord::Base
before_save { email.downcase! }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
has_secure_password
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end
end
Upvotes: 1
Views: 477
Reputation: 301
I've had the same problem the other day. As shown in the log
Redirected to http://www.example.com/signin
Filter chain halted as :signed_in_user rendered or redirected
Completed 302 Found in 3ms (ActiveRecord: 0.7ms)
the reason for not getting the tests to red is due to the signed_in_user before filter defined in user controller. Therefore, signing in the user fixes the 'getting the test to red/green' problem you encountered.
describe "forbidden attributes" do
let(:params) do
{ user: { admin: true, password: user.password,
password_confirmation: user.password } }
end
before {
sign_in user, no_capybara: true
patch user_path(user), params
}
specify { expect(user.reload).not_to be_admin }
end
After inserting the sign_in your tests should be red/green depending on the strong params defined in the controller.
Hope that helps to get you started.
Best, Ben.
P.S.: However, I did not have put much effort into investigating why the mentioned before_filter arrives at the conclusion that the user is not signed in ...
Upvotes: 3