fix(api): prevent API auth from inheriting impersonation (#2405)

Build fresh API session contexts instead of reusing persisted web sessions that may carry impersonation state.

Reject deactivated report export API key owners and strengthen regression coverage for API key, OAuth, and report export authentication paths.
This commit is contained in:
Orange🍊
2026-06-19 23:14:43 +08:00
committed by GitHub
parent 318c7476e1
commit 6e5f35b306
4 changed files with 153 additions and 22 deletions

View File

@@ -125,6 +125,68 @@ class Api::V1::ChatsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
test "API key authentication should not inherit web session impersonation" do
support_user = users(:sure_support_staff)
support_user.update!(ai_enabled: true)
support_user.api_keys.active.destroy_all
support_chat = support_user.chats.create!(title: "Support Chat")
token_value = ApiKey.generate_secure_key
credential = support_user.api_keys.build(
name: "Impersonation Guard Credential",
scopes: [ "read" ]
)
credential.key = token_value
credential.save!
impersonation_session = impersonation_sessions(:in_progress)
impersonated_chat = impersonation_session.impersonated.chats.create!(title: "Impersonated Chat")
support_user.sessions.destroy_all
support_user.sessions.create!(
user_agent: "Browser session",
ip_address: "127.0.0.1",
active_impersonator_session: impersonation_session
)
get "/api/v1/chats", headers: { "X-Api-Key" => token_value }
assert_response :success
response_body = JSON.parse(response.body)
chat_ids = response_body["chats"].pluck("id")
assert_includes chat_ids, support_chat.id
assert_not_includes chat_ids, impersonated_chat.id
end
test "OAuth authentication should not inherit web session impersonation" do
support_user = users(:sure_support_staff)
support_user.update!(ai_enabled: true)
support_chat = support_user.chats.create!(title: "Support Chat")
support_token = Doorkeeper::AccessToken.create!(
application: @oauth_app,
resource_owner_id: support_user.id,
scopes: "read"
)
impersonation_session = impersonation_sessions(:in_progress)
impersonated_chat = impersonation_session.impersonated.chats.create!(title: "Impersonated Chat")
support_user.sessions.destroy_all
support_user.sessions.create!(
user_agent: "Browser session",
ip_address: "127.0.0.1",
active_impersonator_session: impersonation_session
)
get "/api/v1/chats", headers: bearer_auth_header(support_token)
assert_response :success
response_body = JSON.parse(response.body)
chat_ids = response_body["chats"].pluck("id")
assert_includes chat_ids, support_chat.id
assert_not_includes chat_ids, impersonated_chat.id
end
private
def bearer_auth_header(token)