mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-09-10 10:41:34 +09:00
9dd19d0c95
Action Cable authorizes a Connection once at the WebSocket handshake and never re-checks it. Destroying the session record refuses future handshakes and HTTP requests bearing the cookie, but a socket opened before sign out keeps its handshake-time current_user and keeps authorizing new subscriptions and delivering frames as the signed-out user. Reset the user's remote connections when the session is terminated. Clients tear down and reconnect: the signed-out device carries a destroyed session and cleared cookie and is rejected at the fresh handshake, while the user's other devices with still-valid sessions reconnect and stay live. This reuses the existing reset_remote_connections primitive already used on membership removal, for the same reason. Run the disconnect last and best-effort, after the session record and cookie are already gone, so sign out completes even when the realtime service is unreachable.
94 lines
2.9 KiB
Ruby
94 lines
2.9 KiB
Ruby
require "test_helper"
|
|
|
|
class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|
ALLOWED_BROWSER = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15"
|
|
DISALLOWED_BROWSER = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0"
|
|
|
|
test "new" do
|
|
get new_session_url
|
|
assert_response :success
|
|
end
|
|
|
|
test "new redirects to first run when no users exist" do
|
|
User.destroy_all
|
|
|
|
get new_session_url
|
|
|
|
assert_redirected_to first_run_url
|
|
end
|
|
|
|
test "new denied with incompatible browser" do
|
|
get new_session_url, env: { "HTTP_USER_AGENT" => DISALLOWED_BROWSER }
|
|
assert_select "h1", /Upgrade to a supported web browser/
|
|
end
|
|
|
|
test "new allowed with compatible browser" do
|
|
get new_session_url, env: { "HTTP_USER_AGENT" => ALLOWED_BROWSER }
|
|
assert_select "h1", text: /Upgrade to a supported web browser/, count: 0
|
|
end
|
|
|
|
test "create with valid credentials" do
|
|
assert_difference -> { Session.count }, +1 do
|
|
post session_url, params: { email_address: "david@37signals.com", password: "secret123456" }
|
|
end
|
|
|
|
assert_redirected_to root_url
|
|
assert parsed_cookies.signed[:session_token]
|
|
end
|
|
|
|
test "create with invalid credentials" do
|
|
post session_url, params: { email_address: "david@37signals.com", password: "wrong" }
|
|
|
|
assert_response :unauthorized
|
|
assert_nil parsed_cookies.signed[:session_token]
|
|
end
|
|
|
|
test "destroy" do
|
|
sign_in :david
|
|
session = users(:david).sessions.last
|
|
|
|
assert_difference -> { Session.count }, -1 do
|
|
delete session_url
|
|
end
|
|
|
|
assert_redirected_to root_url
|
|
assert_not cookies[:session_token].present?
|
|
assert_nil Session.find_by(id: session.id)
|
|
end
|
|
|
|
test "destroy closes the signed-out user's live connections" do
|
|
sign_in :david
|
|
|
|
remote_connections = mock
|
|
remote_connections.expects(:disconnect).with(reconnect: true)
|
|
ActionCable.server.stubs(:remote_connections).returns(mock.tap { |m| m.expects(:where).with(current_user: users(:david)).returns(remote_connections) })
|
|
|
|
delete session_url
|
|
|
|
assert_redirected_to root_url
|
|
end
|
|
|
|
test "destroy still signs out when the realtime service is unreachable" do
|
|
sign_in :david
|
|
session = users(:david).sessions.last
|
|
ActionCable.server.stubs(:remote_connections).raises(RuntimeError.new("cable down"))
|
|
|
|
delete session_url
|
|
|
|
assert_redirected_to root_url
|
|
assert_not cookies[:session_token].present?
|
|
assert_nil Session.find_by(id: session.id)
|
|
end
|
|
|
|
test "destroy removes the push subscription for the device" do
|
|
sign_in :david
|
|
|
|
assert_difference -> { users(:david).push_subscriptions.count }, -1 do
|
|
delete session_url, params: { push_subscription_endpoint: push_subscriptions(:david_chrome).endpoint }
|
|
end
|
|
|
|
assert_redirected_to root_url
|
|
assert_not cookies[:session_token].present?
|
|
end
|
|
end
|