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.
60 lines
1.3 KiB
Ruby
60 lines
1.3 KiB
Ruby
# Checks whether a new account seems suspicious and should require email verification.
|
|
|
|
class UserVerifier
|
|
extend Memoist
|
|
|
|
attr_reader :current_user, :request
|
|
|
|
# current_user is the user creating the new account, not the new account itself.
|
|
def initialize(current_user, request)
|
|
@current_user, @request = current_user, request
|
|
end
|
|
|
|
def requires_verification?
|
|
return false if !Danbooru.config.new_user_verification?
|
|
return false if is_local_ip?
|
|
|
|
# we check for IP bans first to make sure we bump the IP ban hit count
|
|
is_ip_banned? || is_logged_in? || is_recent_signup? || is_proxy?
|
|
end
|
|
|
|
def initial_level
|
|
if requires_verification?
|
|
User::Levels::RESTRICTED
|
|
else
|
|
User::Levels::MEMBER
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def ip_address
|
|
@ip_address ||= IPAddress.parse(request.remote_ip)
|
|
end
|
|
|
|
def is_local_ip?
|
|
IpLookup.new(ip_address).is_local?
|
|
end
|
|
|
|
def is_logged_in?
|
|
!current_user.is_anonymous?
|
|
end
|
|
|
|
def is_recent_signup?(age: 24.hours)
|
|
subnet_len = ip_address.ipv4? ? 24 : 64
|
|
subnet = "#{ip_address}/#{subnet_len}"
|
|
|
|
User.where("last_ip_addr <<= ?", subnet).where("created_at > ?", age.ago).exists?
|
|
end
|
|
|
|
def is_ip_banned?
|
|
IpBan.hit!(:partial, ip_address.to_s)
|
|
end
|
|
|
|
def is_proxy?
|
|
IpLookup.new(ip_address).is_proxy?
|
|
end
|
|
|
|
memoize :is_ip_banned?, :is_proxy?, :is_recent_signup?
|
|
end
|