discourse/spec/requests/admin/impersonate_controller_spec.rb
Ted Johansson b24a3d81ed
DEV: Allow impersonation without session swapping (#34213)
The current impersonation feature works by signing you in as the user you are impersonating. This has the side effect of invalidating your own session and forcing you to log out and in again.

In this experimental implementation you keep your existing session, but DefaultCurrentUserProvider returns the user being impersonated, allowing you to see the site from their perspective.
2025-08-21 14:18:15 +08:00

151 lines
4.5 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Admin::ImpersonateController do
fab!(:admin)
fab!(:moderator)
fab!(:user)
fab!(:another_admin, :admin)
describe "#index" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "returns success" do
get "/admin/impersonate.json"
expect(response.status).to eq(200)
end
end
shared_examples "impersonation inaccessible" do
it "denies access with a 404 response" do
get "/admin/impersonate.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "impersonation inaccessible"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "impersonation inaccessible"
end
end
describe "#create" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "requires a username_or_email parameter" do
post "/admin/impersonate.json"
expect(response.status).to eq(400)
expect(session[:current_user_id]).to eq(admin.id)
end
it "returns 404 when that user does not exist" do
post "/admin/impersonate.json", params: { username_or_email: "hedonismbot" }
expect(response.status).to eq(404)
expect(session[:current_user_id]).to eq(admin.id)
end
it "raises an invalid access error if the user can't be impersonated" do
post "/admin/impersonate.json", params: { username_or_email: another_admin.email }
expect(response.status).to eq(403)
expect(session[:current_user_id]).to eq(admin.id)
end
context "when using experimental impersonation feature" do
it "raises an invalid access error if admin is already impersonating someone" do
SiteSetting.experimental_impersonation = true
User.any_instance.stubs(:is_impersonating).returns(true)
post "/admin/impersonate.json", params: { username_or_email: user.username }
expect(response.status).to eq(403)
expect(session[:current_user_id]).to eq(admin.id)
end
end
context "with success" do
it "succeeds and logs the impersonation" do
expect do
post "/admin/impersonate.json", params: { username_or_email: user.username }
end.to change { UserHistory.where(action: UserHistory.actions[:impersonate]).count }.by(1)
expect(response.status).to eq(200)
expect(session[:current_user_id]).to eq(user.id)
end
it "also works with an email address" do
post "/admin/impersonate.json", params: { username_or_email: user.email }
expect(response.status).to eq(200)
expect(session[:current_user_id]).to eq(user.id)
end
end
end
shared_examples "impersonation not allowed" do
it "prevents impersonation with a with 404 response" do
expect do
post "/admin/impersonate.json", params: { username_or_email: user.username }
end.not_to change { UserHistory.where(action: UserHistory.actions[:impersonate]).count }
expect(response.status).to eq(404)
expect(session[:current_user_id]).to eq(current_user.id)
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "impersonation not allowed" do
let(:current_user) { moderator }
end
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "impersonation not allowed" do
let(:current_user) { user }
end
end
end
describe "#destroy" do
before { sign_in(admin) }
it "raises a not found error when experimental impersonation is disabled" do
SiteSetting.experimental_impersonation = false
delete "/admin/impersonate.json"
expect(response.status).to eq(404)
end
it "does not pass routing constraint when current user is not impersonating" do
SiteSetting.experimental_impersonation = true
User.any_instance.stubs(:is_impersonating).returns(false)
delete "/admin/impersonate.json"
expect(response.status).to eq(404)
end
it "stops impersonating" do
SiteSetting.experimental_impersonation = true
User.any_instance.stubs(:is_impersonating).returns(true)
delete "/admin/impersonate.json"
expect(response.status).to eq(200)
end
end
end