discourse/spec/lib/second_factor/actions/grant_admin_spec.rb
David Taylor 1bfccdd4f2
DEV: Allow run_second_factor! to be used before login (#25420)
In a handful of situations, we need to verify a user's 2fa credentials before `current_user` is assigned. For example: login, email_login and change-email confirmation. This commit adds an explicit `target_user:` parameter to the centralized 2fa system so that it can be used for those situations.

For safety and clarity, this new parameter only works for anon. If some user is logged in, and target_user is set to a different user, an exception will be raised.
2024-01-29 12:28:47 +00:00

87 lines
2.9 KiB
Ruby

# frozen_string_literal: true
RSpec.describe SecondFactor::Actions::GrantAdmin do
fab!(:admin)
fab!(:user)
def cleanup_admin_confirmation_redis_keys
keys = Discourse.redis.keys("admin-confirmation:*")
keys += Discourse.redis.keys("admin-confirmation-token:*")
Discourse.redis.del(keys)
end
after { cleanup_admin_confirmation_redis_keys }
def params(hash)
ActionController::Parameters.new(hash)
end
def create_request(request_method: "GET", path: "/")
ActionDispatch::TestRequest.create({ "REQUEST_METHOD" => request_method, "PATH_INFO" => path })
end
def create_instance(user, request = nil)
request ||= create_request
SecondFactor::Actions::GrantAdmin.new(Guardian.new(user), request, target_user: user)
end
describe "#no_second_factors_enabled!" do
it "sends new admin confirmation email" do
instance = create_instance(admin)
expect { instance.no_second_factors_enabled!(params({ user_id: user.id })) }.to change {
AdminConfirmation.exists_for?(user.id)
}.from(false).to(true)
end
it "ensures the acting user is admin" do
instance = create_instance(user)
expect { instance.no_second_factors_enabled!(params({ user_id: user.id })) }.to raise_error(
Discourse::InvalidAccess,
)
expect(AdminConfirmation.exists_for?(user.id)).to eq(false)
end
end
describe "#second_factor_auth_required!" do
it "returns a hash with callback_params, redirect_url and a description" do
instance = create_instance(admin)
hash = instance.second_factor_auth_required!(params({ user_id: user.id }))
expect(hash[:callback_params]).to eq({ user_id: user.id })
expect(hash[:redirect_url]).to eq("/admin/users/#{user.id}/#{user.username}")
expect(hash[:description]).to eq(
I18n.t("second_factor_auth.actions.grant_admin.description", username: "@#{user.username}"),
)
end
it "ensures the acting user is admin" do
instance = create_instance(user)
expect { instance.second_factor_auth_required!(params({ user_id: user.id })) }.to raise_error(
Discourse::InvalidAccess,
)
end
end
describe "#second_factor_auth_completed!" do
it "grants the target user admin access and logs to staff action logs" do
instance = create_instance(admin)
expect { instance.second_factor_auth_completed!(user_id: user.id) }.to change {
user.reload.admin
}.from(false).to(true)
expect(
UserHistory.exists?(
acting_user_id: admin.id,
target_user_id: user.id,
action: UserHistory.actions[:grant_admin],
),
).to eq(true)
end
it "ensures the acting user is admin" do
instance = create_instance(user)
expect { instance.second_factor_auth_completed!(user_id: user.id) }.to raise_error(
Discourse::InvalidAccess,
)
end
end
end