From a04596c9256df9687e613c42decb619b229b7b01 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 16:34:46 +0200 Subject: [PATCH] test: cover test_authenticate_with_declared_on_a_base_controller_sees... --- test/test_authenticatable.rb | 159 +++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/test/test_authenticatable.rb b/test/test_authenticatable.rb index a386b78..0816ee5 100644 --- a/test/test_authenticatable.rb +++ b/test/test_authenticatable.rb @@ -317,3 +317,162 @@ class AuthenticatableTest < Minitest::Test assert_equal '/sign_in', headers['Location'] end + def test_authenticate_with_declared_on_a_base_controller_sees_current_user_in_a_subclass + cookie = sign_in_cookie + status, _, body = call('GET', '/inherited/me', cookies: cookie) + assert_equal 200, status + assert_equal 'hello foksiu@example.com', body.join + end + + def test_signing_in_without_remember_sets_no_remember_cookie + _, headers, = call('POST', '/remember/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }, + body: 'email=foksiu@example.com&password=hunter2') + assert_nil cookie_named(headers, 'remember_token') + end + + def test_remember_cookie_alone_survives_a_lost_session_cookie + _, sign_in_headers, = call('POST', '/remember/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }, + body: 'email=foksiu@example.com&password=hunter2&remember=1') + remember_cookie = cookie_named(sign_in_headers, 'remember_token') + refute_nil remember_cookie + + # No session cookie at all here - simulates a brand new browser session + # (browser restart) that only still has the persistent cookie. + status, dashboard_headers, body = call('GET', '/remember/dashboard', cookies: remember_cookie) + assert_equal 200, status + assert_equal 'hello foksiu@example.com', body.join + # The fast path (session) is repopulated for the next request on this + # same browser, so it does not need to re-verify the cookie every time. + refute_nil session_cookie_from(dashboard_headers) + end + + def test_dashboard_rejects_a_request_with_no_cookies_at_all + status, = call('GET', '/remember/dashboard') + assert_equal 302, status + end + + def test_dashboard_rejects_a_tampered_remember_cookie + _, sign_in_headers, = call('POST', '/remember/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }, + body: 'email=foksiu@example.com&password=hunter2&remember=1') + remember_cookie = cookie_named(sign_in_headers, 'remember_token') + tampered = remember_cookie.sub('remember_token=', 'remember_token=tampered') + + status, = call('GET', '/remember/dashboard', cookies: tampered) + assert_equal 302, status + end + + def test_sign_out_rotates_the_token_so_the_old_remember_cookie_stops_working + _, sign_in_headers, = call('POST', '/remember/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }, + body: 'email=foksiu@example.com&password=hunter2&remember=1') + remember_cookie = cookie_named(sign_in_headers, 'remember_token') + session_cookie = session_cookie_from(sign_in_headers) + + call('POST', '/remember/sign_out', cookies: session_cookie) + + status, = call('GET', '/remember/dashboard', cookies: remember_cookie) + assert_equal 302, status + end + + def multi_sign_in(remember: true) + _, headers, = call('POST', '/multi/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }, + body: "email=foksiu@example.com&password=hunter2&remember=#{remember ? '1' : '0'}") + headers + end + + def test_remember_sessions_rejects_both_options_at_once + error = assert_raises(ArgumentError) do + Class.new(Prism::Controller) do + include Prism::Authenticatable + authenticate_with model: :AuthUser, remember_cookie: :remember_token, remember_sessions: :AuthLoginSession + end + end + assert_match(/either remember_cookie: or remember_sessions:/, error.message) + end + + def test_remember_sessions_creates_its_own_row_per_sign_in + assert_equal 0, AuthLoginSession.count + multi_sign_in + assert_equal 1, AuthLoginSession.count + assert_equal AuthUser.find_by(email: 'foksiu@example.com').id, AuthLoginSession.first.user_id + end + + def test_remember_sessions_captures_user_agent_and_ip_when_the_model_has_those_columns + env = env_for('POST', '/multi/sign_in', + headers: { 'Content-Type' => 'application/x-www-form-urlencoded', 'User-Agent' => 'DeviceScanner/9.0' }, + body: 'email=foksiu@example.com&password=hunter2&remember=1') + env['REMOTE_ADDR'] = '198.51.100.42' + AuthenticatableApp.call(env) + + login_session = AuthLoginSession.first + assert_equal 'DeviceScanner/9.0', login_session.user_agent + assert_equal '198.51.100.42', login_session.ip_address + end + + def test_remember_session_cookie_alone_survives_a_lost_session_cookie + headers = multi_sign_in + remember_cookie = cookie_named(headers, 'remember_session_token') + refute_nil remember_cookie + + status, dashboard_headers, body = call('GET', '/multi/dashboard', cookies: remember_cookie) + assert_equal 200, status + assert_equal 'hello foksiu@example.com', body.join + refute_nil session_cookie_from(dashboard_headers) + end + + def test_two_remembered_browsers_are_independent_login_sessions + browser_a = cookie_named(multi_sign_in, 'remember_session_token') + browser_b = cookie_named(multi_sign_in, 'remember_session_token') + refute_equal browser_a, browser_b + assert_equal 2, AuthLoginSession.count + + status_a, = call('GET', '/multi/dashboard', cookies: browser_a) + status_b, = call('GET', '/multi/dashboard', cookies: browser_b) + assert_equal 200, status_a + assert_equal 200, status_b + end + + def test_signing_out_of_one_browser_leaves_the_other_remembered_browser_signed_in + headers_a = multi_sign_in + browser_a_remember = cookie_named(headers_a, 'remember_session_token') + browser_a_session = session_cookie_from(headers_a) + browser_b_remember = cookie_named(multi_sign_in, 'remember_session_token') + + call('POST', '/multi/sign_out', cookies: browser_a_session) + + status_a, = call('GET', '/multi/dashboard', cookies: browser_a_remember) + assert_equal 302, status_a + + status_b, = call('GET', '/multi/dashboard', cookies: browser_b_remember) + assert_equal 200, status_b + end + + def test_other_login_sessions_excludes_the_current_browser + browser_a_remember = cookie_named(multi_sign_in, 'remember_session_token') + cookie_named(multi_sign_in, 'remember_session_token') + cookie_named(multi_sign_in, 'remember_session_token') + + status, _, body = call('GET', '/multi/other_devices', cookies: browser_a_remember) + assert_equal 200, status + assert_equal '2', body.join + end + + def test_sign_out_all_other_sessions_leaves_only_the_current_browser + browser_a_remember = cookie_named(multi_sign_in, 'remember_session_token') + browser_b_remember = cookie_named(multi_sign_in, 'remember_session_token') + + status, _, body = call('POST', '/multi/sign_out_others', cookies: browser_a_remember) + assert_equal 200, status + assert_equal '0', body.join + assert_equal 1, AuthLoginSession.count + + status_b, = call('GET', '/multi/dashboard', cookies: browser_b_remember) + assert_equal 302, status_b + status_a, = call('GET', '/multi/dashboard', cookies: browser_a_remember) + assert_equal 200, status_a + end +