Skip to content

Commit

Permalink
Merge pull request #1109 from texpert/fix-updated-ajax
Browse files Browse the repository at this point in the history
Fix updated_ajax action to permit only legit params
  • Loading branch information
texpert authored Jan 14, 2025
2 parents 4b5ce85 + d9036f0 commit 179fd6b
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 2 deletions.
9 changes: 7 additions & 2 deletions app/controllers/camaleon_cms/admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,15 @@ def update
def updated_ajax
@user = current_site.users.find(params[:user_id])
update_session = current_user_is?(@user)
@user.update(params.require(:password).permit!)
render inline: @user.errors.full_messages.join(', ')
attrs = params.require(:password).permit(%i[password password_confirmation])
@user.update(password: attrs.require(:password), password_confirmation: attrs.require(:password_confirmation))

return render inline: @user.errors.full_messages.join(', '), status: :unprocessable_entity if @user.errors.any?

# keep user logged in when changing their own password
update_auth_token_in_cookie @user.auth_token if update_session && @user.saved_change_to_password_digest?
rescue ActionController::ParameterMissing => e
render inline: "ERROR: #{e.class.name}, #{e.message}", status: :bad_request
end

def update_auth_token_in_cookie(token)
Expand Down
87 changes: 87 additions & 0 deletions spec/requests/admin/users_controller/updated_ajax_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'updated_ajax request', type: :request do
init_site

let(:current_site) { Cama::Site.first.decorate }
let(:current_user) { create(:user_admin, site: current_site, password: 'secret', password_confirmation: 'secret') }

before do
allow_any_instance_of(CamaleonCms::AdminController).to receive(:cama_authenticate)
allow_any_instance_of(CamaleonCms::Admin::UsersController).to receive(:validate_role).and_return(true)
end

context 'when receiving correct params' do
it "updates user's password" do
expect(current_user.authenticate('secret')).to be_truthy
expect(current_user.authenticate('new password')).to be_falsey

patch "/admin/users/#{current_user.id}/updated_ajax",
params: { password: { password: 'new password', password_confirmation: 'new password' } }

expect(response.status).to eql(204)
expect(response.body).to eql('')
expect(current_user.reload.authenticate('secret')).to be_falsey
expect(current_user.reload.authenticate('new password')).to be_truthy
end
end

context 'when receiving incorrect params' do
context 'when wrong password confirmation' do
it "doesn't update user's password and return error" do
expect(current_user.authenticate('secret')).to be_truthy

patch "/admin/users/#{current_user.id}/updated_ajax",
params: { password: { password: 'new password', password_confirmation: 'old password' } }

expect(response.status).to eql(422)
expect(response.body).to eql("Password confirmation doesn't match Password")
expect(current_user.reload.authenticate('secret')).to be_truthy
end
end

context 'when missing password confirmation' do
it "doesn't update user's password" do
expect(current_user.authenticate('secret')).to be_truthy

patch "/admin/users/#{current_user.id}/updated_ajax", params: { password: { password: 'new password' } }

expect(response.status).to eql(400)
expect(response.body).to start_with(
'ERROR: ActionController::ParameterMissing, param is missing or the value is empty'
)
expect(response.body).to include('password_confirmation')
expect(current_user.reload.authenticate('secret')).to be_truthy
expect(current_user.reload.authenticate('new password')).to be_falsey
end
end

context 'when passing unpermitted params' do
it 'ignores the unpermitted param' do
expect(current_user.authenticate('secret')).to be_truthy

# Changing this to false, because the receiver is not only yielded to the blocks, but also passed as an
# unexpected additional argument to the `originall.call`
RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = false

allow_any_instance_of(CamaleonCms::User).to receive(:update).and_call_original

expect_any_instance_of(CamaleonCms::User)
.to receive(:update).with(password: 'new password', password_confirmation: 'new password')

patch "/admin/users/#{current_user.id}/updated_ajax",
params: { password: { password: 'new password', password_confirmation: 'new password', role: 'admin' } }

expect(response.status).to eql(204)
expect(response.body).to eql('')
expect(current_user.reload.authenticate('secret')).to be_falsey
expect(current_user.reload.authenticate('new password')).to be_truthy

# returning the default configuration
RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = false
end
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ENV['RAILS_ENV'] ||= 'test'

require 'pathname'
CAMALEON_CMS_ROOT = Pathname.new(__FILE__).join('../..')

require File.expand_path('dummy/config/environment.rb', __dir__)
Expand Down

0 comments on commit 179fd6b

Please sign in to comment.