Fix three exploits that allowed one to keep using their account after it was deleted: * It was possible to use session cookies from another computer to login after you deleted your account. * It was possible to use API keys to make API requests after you deleted your account. * It was possible to request a password reset, delete your account, then use the password reset link to change your password and login to your deleted account.
147 lines
5.0 KiB
Ruby
147 lines
5.0 KiB
Ruby
require 'test_helper'
|
|
|
|
class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|
context "the sessions controller" do
|
|
setup do
|
|
@user = create(:user, password: "password")
|
|
end
|
|
|
|
context "new action" do
|
|
should "render" do
|
|
get new_session_path
|
|
assert_response :success
|
|
end
|
|
end
|
|
|
|
context "create action" do
|
|
should "log the user in when given the correct password" do
|
|
post session_path, params: { name: @user.name, password: "password" }
|
|
|
|
assert_redirected_to posts_path
|
|
assert_equal(@user.id, session[:user_id])
|
|
assert_not_nil(@user.reload.last_ip_addr)
|
|
assert_equal(true, @user.user_events.login.exists?)
|
|
end
|
|
|
|
should "not log the user in when given an incorrect password" do
|
|
post session_path, params: { name: @user.name, password: "wrong"}
|
|
|
|
assert_response 401
|
|
assert_nil(nil, session[:user_id])
|
|
assert_equal(true, @user.user_events.failed_login.exists?)
|
|
end
|
|
|
|
should "not log the user in when given an incorrect username" do
|
|
post session_path, params: { name: "dne", password: "password" }
|
|
|
|
assert_response 401
|
|
assert_nil(nil, session[:user_id])
|
|
end
|
|
|
|
should "redirect the user when given an url param" do
|
|
post session_path, params: { name: @user.name, password: "password", url: tags_path }
|
|
assert_redirected_to tags_path
|
|
end
|
|
|
|
should "not allow redirects to protocol-relative URLs" do
|
|
post session_path, params: { name: @user.name, password: "password", url: "//example.com" }
|
|
assert_response 403
|
|
end
|
|
|
|
should "not allow deleted users to login" do
|
|
@user.update!(is_deleted: true)
|
|
post session_path, params: { name: @user.name, password: "password" }
|
|
|
|
assert_response 401
|
|
assert_nil(nil, session[:user_id])
|
|
assert_equal(true, @user.user_events.failed_login.exists?)
|
|
end
|
|
|
|
should "not allow IP banned users to login" do
|
|
@ip_ban = create(:ip_ban, category: :full, ip_addr: "1.2.3.4")
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
|
|
assert_response 403
|
|
assert_not_equal(@user.id, session[:user_id])
|
|
assert_equal(1, @ip_ban.reload.hit_count)
|
|
assert(@ip_ban.last_hit_at > 1.minute.ago)
|
|
end
|
|
|
|
should "allow partial IP banned users to login" do
|
|
@ip_ban = create(:ip_ban, category: :partial, ip_addr: "1.2.3.4")
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
|
|
assert_redirected_to posts_path
|
|
assert_equal(@user.id, session[:user_id])
|
|
assert_equal(0, @ip_ban.reload.hit_count)
|
|
assert_nil(@ip_ban.last_hit_at)
|
|
end
|
|
|
|
should "ignore deleted IP bans when logging in" do
|
|
@ip_ban = create(:ip_ban, is_deleted: true, category: :full, ip_addr: "1.2.3.4")
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
|
|
assert_redirected_to posts_path
|
|
assert_equal(@user.id, session[:user_id])
|
|
assert_equal(0, @ip_ban.reload.hit_count)
|
|
assert_nil(@ip_ban.last_hit_at)
|
|
end
|
|
|
|
should "rate limit logins to 10 per minute per IP" do
|
|
freeze_time
|
|
|
|
11.times do
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
assert_redirected_to posts_path
|
|
assert_equal(@user.id, session[:user_id])
|
|
delete_auth session_path, @user
|
|
end
|
|
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
assert_response 429
|
|
assert_not_equal(@user.id, session[:user_id])
|
|
|
|
travel 59.seconds
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
assert_response 429
|
|
assert_not_equal(@user.id, session[:user_id])
|
|
|
|
travel 10.seconds
|
|
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
|
assert_redirected_to posts_path
|
|
assert_equal(@user.id, session[:user_id])
|
|
end
|
|
end
|
|
|
|
context "destroy action" do
|
|
should "clear the session" do
|
|
delete_auth session_path, @user
|
|
|
|
assert_redirected_to posts_path
|
|
assert_nil(session[:user_id])
|
|
end
|
|
|
|
should "generate a logout event" do
|
|
delete_auth session_path, @user
|
|
|
|
assert_equal(true, @user.user_events.logout.exists?)
|
|
end
|
|
|
|
should "not fail if the user is already logged out" do
|
|
delete session_path
|
|
|
|
assert_redirected_to posts_path
|
|
assert_nil(session[:user_id])
|
|
end
|
|
end
|
|
|
|
context "sign_out action" do
|
|
should "clear the session" do
|
|
get_auth sign_out_session_path, @user
|
|
assert_redirected_to posts_path
|
|
assert_nil(session[:user_id])
|
|
end
|
|
end
|
|
end
|
|
end
|