Add tracking of certain important user actions. These events include: * Logins * Logouts * Failed login attempts * Account creations * Account deletions * Password reset requests * Password changes * Email address changes This is similar to the mod actions log, except for account activity related to a single user. The information tracked includes the user, the event type (login, logout, etc), the timestamp, the user's IP address, IP geolocation information, the user's browser user agent, and the user's session ID from their session cookie. This information is visible to mods only. This is done with three models. The UserEvent model tracks the event type (login, logout, password change, etc) and the user. The UserEvent is tied to a UserSession, which contains the user's IP address and browser metadata. Finally, the IpGeolocation model contains the geolocation information for IPs, including the city, country, ISP, and whether the IP is a proxy. This tracking will be used for a few purposes: * Letting users view their account history, to detect things like logins from unrecognized IPs, failed logins attempts, password changes, etc. * Rate limiting failed login attempts. * Detecting sockpuppet accounts using their login history. * Detecting unauthorized account sharing.
230 lines
8.1 KiB
Ruby
230 lines
8.1 KiB
Ruby
require "test_helper"
|
|
|
|
class EmailsControllerTest < ActionDispatch::IntegrationTest
|
|
include UsersHelper
|
|
|
|
context "in all cases" do
|
|
setup do
|
|
@user = create(:user, email_address: build(:email_address, { address: "bob@ogres.net", is_verified: false }))
|
|
@other_user = create(:user, email_address: build(:email_address, { address: "alice@ogres.net", is_verified: false }))
|
|
@restricted_user = create(:restricted_user, email_address: build(:email_address, { is_verified: false }))
|
|
end
|
|
|
|
context "#index" do
|
|
should "not let regular users see emails belonging to other users" do
|
|
get_auth emails_path, @user
|
|
assert_response 403
|
|
end
|
|
|
|
should "let mods see emails belonging to themselves and all users below mod level" do
|
|
@mod1 = create(:moderator_user, email_address: build(:email_address))
|
|
@mod2 = create(:moderator_user, email_address: build(:email_address))
|
|
|
|
get_auth emails_path, @mod1
|
|
|
|
assert_response :success
|
|
assert_select "#email-address-#{@user.email_address.id}", count: 1
|
|
assert_select "#email-address-#{@other_user.email_address.id}", count: 1
|
|
assert_select "#email-address-#{@mod1.email_address.id}", count: 1
|
|
assert_select "#email-address-#{@mod2.email_address.id}", count: 0
|
|
end
|
|
end
|
|
|
|
context "#show" do
|
|
should "render" do
|
|
get_auth user_email_path(@user), @user, as: :json
|
|
assert_response :success
|
|
end
|
|
|
|
should "not show email addresses to other users" do
|
|
get_auth user_email_path(@user), @other_user, as: :json
|
|
assert_response 403
|
|
end
|
|
end
|
|
|
|
context "#edit" do
|
|
context "for a user with an email address" do
|
|
should "render" do
|
|
get_auth edit_user_email_path(@user), @user
|
|
assert_equal true, @user.email_address.present?
|
|
assert_response :success
|
|
end
|
|
end
|
|
|
|
context "for a user without an email address" do
|
|
should "render" do
|
|
@user.email_address.destroy!
|
|
@user.reload_email_address
|
|
get_auth edit_user_email_path(@user), @user
|
|
|
|
assert_equal false, @user.email_address.present?
|
|
assert_response :success
|
|
assert_select "h1", text: "Add Email"
|
|
end
|
|
end
|
|
|
|
context "for a restricted user" do
|
|
should "render" do
|
|
get_auth edit_user_email_path(@restricted_user), @restricted_user
|
|
assert_response :success
|
|
end
|
|
end
|
|
|
|
context "for an unauthorized user" do
|
|
should "render" do
|
|
get_auth edit_user_email_path(@user), @other_user
|
|
assert_response 403
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#update" do
|
|
context "with the correct password" do
|
|
should "update an existing address" do
|
|
assert_difference("EmailAddress.count", 0) do
|
|
put_auth user_email_path(@user), @user, params: { user: { password: "password", email: "abc@ogres.net" }}
|
|
end
|
|
|
|
assert_redirected_to(settings_path)
|
|
assert_equal("abc@ogres.net", @user.reload.email_address.address)
|
|
assert_equal(false, @user.email_address.is_verified)
|
|
assert_enqueued_email_with UserMailer, :email_change_confirmation, args: [@user], queue: "default"
|
|
assert_equal(true, @user.user_events.email_change.exists?)
|
|
end
|
|
|
|
should "create a new address" do
|
|
@user.email_address.destroy
|
|
|
|
assert_difference("EmailAddress.count", 1) do
|
|
put_auth user_email_path(@user), @user, params: { user: { password: "password", email: "abc@ogres.net" }}
|
|
end
|
|
|
|
assert_redirected_to(settings_path)
|
|
assert_equal("abc@ogres.net", @user.reload.email_address.address)
|
|
assert_equal(false, @user.reload.email_address.is_verified)
|
|
assert_enqueued_email_with UserMailer, :email_change_confirmation, args: [@user], queue: "default"
|
|
assert_equal(true, @user.user_events.email_change.exists?)
|
|
end
|
|
end
|
|
|
|
context "with the incorrect password" do
|
|
should "not work" do
|
|
put_auth user_email_path(@user), @user, params: { user: { password: "passwordx", email: "abc@ogres.net" }}
|
|
|
|
assert_response :success
|
|
assert_equal("bob@ogres.net", @user.reload.email_address.address)
|
|
assert_no_emails
|
|
assert_equal(false, @user.user_events.email_change.exists?)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#verify" do
|
|
context "with a correct verification key" do
|
|
should "mark the email address as verified" do
|
|
assert_equal(false, @user.reload.email_address.is_verified)
|
|
get email_verification_url(@user)
|
|
|
|
assert_redirected_to @user
|
|
assert_equal(true, @user.reload.email_address.is_verified)
|
|
end
|
|
end
|
|
|
|
context "with an incorrect verification key" do
|
|
should "not mark the email address as verified" do
|
|
get verify_user_email_path(@user, email_verification_key: @other_user.email_address.verification_key)
|
|
|
|
assert_response 403
|
|
assert_equal(false, @user.reload.email_address.is_verified)
|
|
end
|
|
end
|
|
|
|
context "for a Restricted user" do
|
|
context "with a nondisposable email address" do
|
|
should "unrestrict the user's account" do
|
|
Danbooru.config.stubs(:email_domain_verification_list).returns(["gmail.com"])
|
|
@restricted_user.email_address.update!(address: "test@gmail.com")
|
|
|
|
get email_verification_url(@restricted_user)
|
|
|
|
assert_redirected_to @restricted_user
|
|
assert_equal(true, @restricted_user.reload.email_address.is_verified)
|
|
assert_equal(false, @restricted_user.is_restricted?)
|
|
assert_equal(true, @restricted_user.is_member?)
|
|
end
|
|
end
|
|
|
|
context "with a disposable email address" do
|
|
should "leave the user's account restricted" do
|
|
Danbooru.config.stubs(:email_domain_verification_list).returns(["gmail.com"])
|
|
@restricted_user.email_address.update!(address: "test@mailinator.com")
|
|
|
|
get email_verification_url(@restricted_user)
|
|
|
|
assert_redirected_to @restricted_user
|
|
assert_equal(true, @restricted_user.reload.email_address.is_verified)
|
|
assert_equal(true, @restricted_user.is_restricted?)
|
|
assert_equal(false, @restricted_user.is_member?)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "for a Gold user" do
|
|
should "not change the user's level" do
|
|
@user = create(:gold_user, email_address: build(:email_address, { address: "test@gmail.com", is_verified: false }))
|
|
Danbooru.config.stubs(:email_domain_verification_list).returns(["gmail.com"])
|
|
|
|
get email_verification_url(@user)
|
|
|
|
assert_redirected_to @user
|
|
assert_equal(true, @user.reload.email_address.is_verified)
|
|
assert_equal(false, @user.is_restricted?)
|
|
assert_equal(true, @user.is_gold?)
|
|
end
|
|
end
|
|
|
|
context "for a user without an email address" do
|
|
should "redirect to the add email page" do
|
|
@user.email_address.destroy!
|
|
get_auth verify_user_email_path(@user), @user
|
|
assert_redirected_to edit_user_email_path(@user)
|
|
end
|
|
end
|
|
|
|
context "for a user with an unverified email address" do
|
|
should "show the resend confirmation email page" do
|
|
get_auth verify_user_email_path(@user), @user
|
|
assert_response :success
|
|
end
|
|
end
|
|
|
|
context "for an unauthorized user" do
|
|
should "fail" do
|
|
get_auth verify_user_email_path(@user), @other_user
|
|
assert_response 403
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#send_confirmation" do
|
|
context "for an authorized user" do
|
|
should "resend the confirmation email" do
|
|
post_auth send_confirmation_user_email_path(@user), @user
|
|
|
|
assert_redirected_to @user
|
|
assert_enqueued_emails 1
|
|
end
|
|
end
|
|
|
|
context "for an unauthorized user" do
|
|
should "fail" do
|
|
post_auth send_confirmation_user_email_path(@user), @other_user
|
|
|
|
assert_response 403
|
|
assert_no_enqueued_emails
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|