2014-03-26 11:09:39 +00:00
|
|
|
|
# == Schema Information
|
|
|
|
|
#
|
|
|
|
|
# Table name: users
|
|
|
|
|
#
|
2020-04-02 00:08:19 +00:00
|
|
|
|
# id :integer not null, primary key
|
|
|
|
|
# birthdate :date
|
|
|
|
|
# country :string(255)
|
|
|
|
|
# email :string(255)
|
|
|
|
|
# firstname :string(255)
|
|
|
|
|
# lastip :string(255)
|
|
|
|
|
# lastname :string(255)
|
|
|
|
|
# lastvisit :datetime
|
|
|
|
|
# password :string(255)
|
|
|
|
|
# password_hash :integer default(0)
|
|
|
|
|
# public_email :boolean default(FALSE), not null
|
|
|
|
|
# steamid :string(255)
|
|
|
|
|
# time_zone :string(255)
|
|
|
|
|
# username :string(255)
|
|
|
|
|
# version :integer
|
|
|
|
|
# created_at :datetime
|
|
|
|
|
# updated_at :datetime
|
|
|
|
|
# team_id :integer
|
2020-03-18 03:38:17 +00:00
|
|
|
|
#
|
|
|
|
|
# Indexes
|
|
|
|
|
#
|
|
|
|
|
# index_users_on_lastvisit (lastvisit)
|
|
|
|
|
# index_users_on_team_id (team_id)
|
2014-03-26 11:09:39 +00:00
|
|
|
|
#
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
require 'digest/md5'
|
2020-04-10 15:32:18 +00:00
|
|
|
|
require 'steamid'
|
2020-04-02 00:08:19 +00:00
|
|
|
|
require "scrypt"
|
2020-04-10 15:32:18 +00:00
|
|
|
|
require 'securerandom'
|
2014-03-23 00:22:25 +00:00
|
|
|
|
|
2020-03-18 21:36:21 +00:00
|
|
|
|
class SteamIdValidator < ActiveModel::Validator
|
|
|
|
|
def validate(record)
|
|
|
|
|
record.errors.add :steamid unless \
|
|
|
|
|
record.steamid.nil? ||
|
|
|
|
|
(m = record.steamid.match(/\A([01]):([01]):(\d{1,10})\Z/)) &&
|
|
|
|
|
(id = m[3].to_i) &&
|
|
|
|
|
id >= 1 && id <= 2147483647
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
class User < ActiveRecord::Base
|
|
|
|
|
include Extra
|
2019-11-12 22:27:54 +00:00
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
VERIFICATION_TIME = 604800
|
|
|
|
|
|
2020-04-02 00:08:19 +00:00
|
|
|
|
PASSWORD_SCRYPT = 0
|
|
|
|
|
PASSWORD_MD5 = 1
|
|
|
|
|
PASSWORD_MD5_SCRYPT = 2
|
|
|
|
|
|
2020-04-10 17:46:52 +00:00
|
|
|
|
# TODO: move this to a file
|
|
|
|
|
PASSWORD_MESSAGE = \
|
|
|
|
|
"Hello %s, \n" + \
|
|
|
|
|
"Your new password is: %s \n \n \n" + \
|
|
|
|
|
"(Make sure you copy all characters and no whitespace when using copy-paste)\n" + \
|
|
|
|
|
"(Security information: your password is stored with hash %s)\n"
|
|
|
|
|
|
2020-03-16 23:57:47 +00:00
|
|
|
|
#attr_protected :id, :created_at, :updated_at, :lastvisit, :lastip, :password, :version
|
2020-04-10 17:46:52 +00:00
|
|
|
|
attr_accessor :raw_password, :password_updated, :password_force, :fullname, :random_password
|
2014-03-23 00:22:25 +00:00
|
|
|
|
|
2020-03-25 01:13:38 +00:00
|
|
|
|
attribute :lastvisit, :datetime, default: Time.now.utc
|
2020-04-02 00:08:19 +00:00
|
|
|
|
attribute :password_hash, :integer, default: PASSWORD_SCRYPT
|
2020-03-15 13:33:58 +00:00
|
|
|
|
|
2020-03-26 18:50:45 +00:00
|
|
|
|
belongs_to :team, :optional => true
|
2014-03-23 00:22:25 +00:00
|
|
|
|
has_one :profile, :dependent => :destroy
|
|
|
|
|
has_many :bans, :dependent => :destroy
|
|
|
|
|
has_many :articles, :dependent => :destroy
|
|
|
|
|
has_many :movies, :dependent => :destroy
|
|
|
|
|
has_many :servers, :dependent => :destroy
|
|
|
|
|
has_many :votes, :dependent => :destroy
|
|
|
|
|
has_many :gatherers, :dependent => :destroy
|
|
|
|
|
has_many :gathers, :through => :gatherers
|
|
|
|
|
has_many :groupers, :dependent => :destroy
|
|
|
|
|
has_many :posts, :dependent => :destroy
|
|
|
|
|
has_many :groups, :through => :groupers
|
|
|
|
|
has_many :shoutmsgs, :dependent => :destroy
|
|
|
|
|
has_many :issues, :foreign_key => "author_id", :dependent => :destroy
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :assigned_issues, :class_name => "Issue", :foreign_key => "assigned_id"
|
2014-03-23 00:22:25 +00:00
|
|
|
|
has_many :posted_comments, :dependent => :destroy, :class_name => "Comment"
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :comments, -> { order("created_at ASC") }, :class_name => "Comment", :as => :commentable, :dependent => :destroy
|
2014-03-23 00:22:25 +00:00
|
|
|
|
has_many :teamers, :dependent => :destroy
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :active_teams, -> { where("teamers.rank >= ? AND teams.active = ?", Teamer::RANK_MEMBER, true) }, \
|
|
|
|
|
:through => :teamers, :source => "team"
|
2020-03-28 20:39:41 +00:00
|
|
|
|
has_many :lead_teams, -> { where("teamers.rank >= ? AND teams.active = ?", Teamer::RANK_DEPUTEE, true) }, \
|
|
|
|
|
:through => :teamers, :source => "team"
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :active_contesters, -> { where("contesters.active = ?", true) }, \
|
|
|
|
|
:through => :active_teams, :source => "contesters"
|
|
|
|
|
has_many :active_contests, -> { where("contests.status != ?", Contest::STATUS_CLOSED) }, \
|
|
|
|
|
:through => :active_contesters, :source => "contest"
|
2014-03-23 00:22:25 +00:00
|
|
|
|
has_many :matchers, :dependent => :destroy
|
|
|
|
|
has_many :matches, :through => :matchers
|
|
|
|
|
has_many :predictions, :dependent => :destroy
|
|
|
|
|
has_many :challenges_received, :through => :active_contesters, :source => "challenges_received"
|
|
|
|
|
has_many :challenges_sent, :through => :active_contesters, :source => "challenges_sent"
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :upcoming_team_matches, -> { where("match_time > UTC_TIMESTAMP()") },
|
|
|
|
|
:through => :active_teams, :source => "matches"
|
|
|
|
|
has_many :upcoming_ref_matches, -> { where("match_time > UTC_TIMESTAMP()") },
|
|
|
|
|
:class_name => "Match", :foreign_key => "referee_id"
|
|
|
|
|
has_many :past_team_matches, -> { where("match_time < UTC_TIMESTAMP()") },
|
|
|
|
|
:through => :active_contesters, :source => "matches"
|
|
|
|
|
has_many :past_ref_matches, -> { where("match_time < UTC_TIMESTAMP()") },
|
|
|
|
|
:class_name => "Match", :foreign_key => "referee_id"
|
2014-03-23 00:22:25 +00:00
|
|
|
|
has_many :received_personal_messages, :class_name => "Message", :as => "recipient", :dependent => :destroy
|
|
|
|
|
has_many :sent_personal_messages, :class_name => "Message", :as => "sender", :dependent => :destroy
|
|
|
|
|
has_many :sent_team_messages, :through => :active_teams, :source => :sent_messages
|
2019-06-02 01:26:36 +00:00
|
|
|
|
has_many :match_teams, :through => :matchers, :source => :teams
|
2014-03-23 00:22:25 +00:00
|
|
|
|
|
2019-06-01 10:56:09 +00:00
|
|
|
|
scope :active, -> { where(banned: false) }
|
2019-06-02 01:26:36 +00:00
|
|
|
|
scope :with_age, -> {
|
|
|
|
|
where("DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthdate)), '%Y')+0 AS aged, COUNT(*) as num, username")
|
|
|
|
|
.group("aged")
|
|
|
|
|
.having("num > 8 AND aged > 0") }
|
|
|
|
|
scope :country_stats, -> {
|
|
|
|
|
select("country, COUNT(*) as num")
|
|
|
|
|
.where("country is not null and country != '' and country != '--'")
|
|
|
|
|
.group("country")
|
|
|
|
|
.having("num > 15")
|
|
|
|
|
.order("num DESC") }
|
|
|
|
|
scope :posts_stats, ->Â {
|
|
|
|
|
select("users.id, username, COUNT(posts.id) as num")
|
|
|
|
|
.joins("LEFT JOIN posts ON posts.user_id = users.id")
|
|
|
|
|
.group("users.id")
|
2019-06-01 10:56:09 +00:00
|
|
|
|
.order("num DESC") }
|
2019-06-03 19:38:24 +00:00
|
|
|
|
scope :banned, -> {
|
|
|
|
|
joins("LEFT JOIN bans ON bans.user_id = users.id AND expiry > UTC_TIMESTAMP()")
|
2020-03-23 02:22:12 +00:00
|
|
|
|
.where("bans.id IS NOT NULL") }
|
2019-06-03 19:38:24 +00:00
|
|
|
|
scope :idle, -> {
|
2020-03-15 18:31:46 +00:00
|
|
|
|
where("lastvisit < ?", 30.minutes.ago.utc) }
|
|
|
|
|
scope :lately, -> {
|
|
|
|
|
where("lastvisit > ?", 30.days.ago.utc) }
|
2014-03-23 00:22:25 +00:00
|
|
|
|
|
2020-03-17 19:31:57 +00:00
|
|
|
|
before_validation :update_password
|
|
|
|
|
|
2020-11-15 03:11:41 +00:00
|
|
|
|
validates_uniqueness_of :username, :email, :steamid, :case_sensitive => false
|
2014-03-23 00:22:25 +00:00
|
|
|
|
validates_length_of :firstname, :in => 1..15, :allow_blank => true
|
|
|
|
|
validates_length_of :lastname, :in => 1..25, :allow_blank => true
|
2020-04-02 00:08:19 +00:00
|
|
|
|
validates_length_of :username, :in => 1..30
|
|
|
|
|
validates_format_of :username, :with => /\A[A-Za-z0-9_\-\+]{1,30}\Z/
|
2019-06-03 19:38:24 +00:00
|
|
|
|
validates_presence_of :raw_password, :on => :create
|
2014-03-23 00:22:25 +00:00
|
|
|
|
validates_length_of :email, :maximum => 50
|
|
|
|
|
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
|
|
|
|
validates_length_of :steamid, :maximum => 30
|
2020-03-18 21:36:21 +00:00
|
|
|
|
validates_with SteamIdValidator
|
|
|
|
|
# validates_format_of :steamid, :with => /\A(STEAM_)?[0-5]:[01]:\d+\Z/
|
2019-06-03 19:38:24 +00:00
|
|
|
|
validates_length_of :time_zone, :maximum => 100, :allow_blank => true, :allow_nil => true
|
2014-03-23 00:22:25 +00:00
|
|
|
|
validates_inclusion_of [:public_email], :in => [true, false], :allow_nil => true
|
2020-04-02 00:08:19 +00:00
|
|
|
|
# validates_inclusion_of :password_hash, in: => [User::PASSWORD_SCRYPT, User::PASSWORD_MD5, User::PASSWORD_MD5_SCRYPT]
|
2014-03-23 00:22:25 +00:00
|
|
|
|
validate :validate_team
|
|
|
|
|
|
2020-04-10 15:32:18 +00:00
|
|
|
|
before_validation :set_name
|
2020-04-10 17:46:52 +00:00
|
|
|
|
before_validation :init_variables, on: :create
|
2020-04-10 15:32:18 +00:00
|
|
|
|
after_create :create_profile
|
2020-04-10 17:46:52 +00:00
|
|
|
|
after_create :send_new_password, if: Proc.new{ random_password == true }
|
2015-09-24 00:35:30 +00:00
|
|
|
|
before_save :correct_steamid_universe
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
accepts_nested_attributes_for :profile
|
|
|
|
|
|
2020-03-15 20:59:08 +00:00
|
|
|
|
acts_as_reader
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
acts_as_versioned
|
|
|
|
|
non_versioned_columns << 'firstname'
|
|
|
|
|
non_versioned_columns << 'lastname'
|
|
|
|
|
non_versioned_columns << 'email'
|
|
|
|
|
non_versioned_columns << 'password'
|
|
|
|
|
non_versioned_columns << 'team_id'
|
|
|
|
|
non_versioned_columns << 'lastvisit'
|
|
|
|
|
non_versioned_columns << 'team_id'
|
|
|
|
|
non_versioned_columns << 'country'
|
|
|
|
|
non_versioned_columns << 'birthdate'
|
|
|
|
|
non_versioned_columns << 'time_zone'
|
|
|
|
|
non_versioned_columns << 'public_email'
|
2020-04-02 00:08:19 +00:00
|
|
|
|
non_versioned_columns << 'password_hash'
|
2014-03-23 00:22:25 +00:00
|
|
|
|
non_versioned_columns << 'created_at'
|
|
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
|
username
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-10 15:32:18 +00:00
|
|
|
|
def set_name
|
|
|
|
|
return unless fullname
|
|
|
|
|
if fullname.include?(" ")
|
|
|
|
|
# TODO: check this
|
|
|
|
|
self.firstname = fullname.match(/(?:^|(?:\.\s))(\w+)/)[1]
|
|
|
|
|
self.surname = fullname.match(/\s(\w+)$/)[1]
|
|
|
|
|
else
|
|
|
|
|
self.firstname = fullname
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-02 00:08:19 +00:00
|
|
|
|
def password_hash_s
|
|
|
|
|
case self.password_hash
|
|
|
|
|
when User::PASSWORD_MD5
|
|
|
|
|
"MD5"
|
|
|
|
|
when User::PASSWORD_SCRYPT
|
|
|
|
|
"Scrypt"
|
|
|
|
|
when User::PASSWORD_MD5_SCRYPT
|
|
|
|
|
"Scrypt+MD5"
|
|
|
|
|
else
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def email_s
|
|
|
|
|
email.gsub /@/, " (at) "
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def country_s
|
2019-09-24 19:55:34 +00:00
|
|
|
|
country_object = ISO3166::Country[country]
|
2020-03-16 01:12:22 +00:00
|
|
|
|
if country_object
|
|
|
|
|
country_object.translations[I18n.locale.to_s] || country_object.name
|
|
|
|
|
else
|
|
|
|
|
"Unknown"
|
|
|
|
|
end
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def realname
|
|
|
|
|
if firstname and lastname
|
|
|
|
|
"#{firstname} #{lastname}"
|
|
|
|
|
elsif firstname
|
|
|
|
|
firstname
|
|
|
|
|
elsif lastname
|
|
|
|
|
lastname
|
|
|
|
|
else
|
|
|
|
|
""
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def from
|
2014-04-15 09:59:52 +00:00
|
|
|
|
if profile.town && profile.town.length > 0
|
2014-04-13 11:16:51 +00:00
|
|
|
|
"#{profile.town}, #{country_s}"
|
|
|
|
|
else
|
|
|
|
|
"#{country_s}"
|
|
|
|
|
end
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def age
|
|
|
|
|
return 0 unless birthdate
|
2020-03-25 01:13:38 +00:00
|
|
|
|
a = Time.zone.today.year - birthdate.year
|
|
|
|
|
a-= 1 if Time.zone.today < birthdate + a.years
|
2014-03-23 00:22:25 +00:00
|
|
|
|
a
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-18 03:38:17 +00:00
|
|
|
|
def idle
|
2020-03-25 01:13:38 +00:00
|
|
|
|
"%d m" % [TimeDifference.between(Time.now.utc, lastvisit).in_minutes.floor]
|
2020-03-18 03:38:17 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-08-10 22:56:18 +00:00
|
|
|
|
def current_layout
|
|
|
|
|
profile.layout || 'default'
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def joined
|
|
|
|
|
created_at.strftime("%d %b %y")
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-23 02:10:31 +00:00
|
|
|
|
def current_teamer
|
|
|
|
|
team ? teamers.active.of_team(team).first : nil
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-13 22:42:12 +00:00
|
|
|
|
def preformat
|
2020-04-18 05:01:32 +00:00
|
|
|
|
self.email = "" if self.email&.include?("@ensl.org")
|
2020-04-13 22:42:12 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def banned? type = Ban::TYPE_SITE
|
2020-03-25 01:13:38 +00:00
|
|
|
|
bans.effective.where(ban_type: type).count > 0
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def admin?
|
2017-06-24 18:43:42 +00:00
|
|
|
|
groups.exists? id: Group::ADMINS
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def ref?
|
2017-06-24 18:43:42 +00:00
|
|
|
|
groups.exists? id: Group::REFEREES
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-11 13:37:47 +00:00
|
|
|
|
def staff?
|
2017-06-24 18:43:42 +00:00
|
|
|
|
groups.exists? id: Group::STAFF
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-05-10 08:44:48 +00:00
|
|
|
|
def caster?
|
2017-06-24 18:43:42 +00:00
|
|
|
|
groups.exists? id: Group::CASTERS
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# might seem redundant but allows for later extensions like forum moderators
|
|
|
|
|
def moderator?
|
|
|
|
|
groups.exists? id: Group::GATHER_MODERATORS
|
2014-05-10 08:44:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-10-20 17:04:50 +00:00
|
|
|
|
def gather_moderator?
|
|
|
|
|
groups.exists? id: Group::GATHER_MODERATORS
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-12 22:27:54 +00:00
|
|
|
|
def contributor?
|
|
|
|
|
groups.exists? id: Group::CONTRIBUTORS
|
|
|
|
|
end
|
|
|
|
|
|
2017-03-06 01:33:12 +00:00
|
|
|
|
def allowed_to_ban?
|
2017-06-24 18:43:42 +00:00
|
|
|
|
admin? or moderator?
|
2014-05-10 08:44:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def verified?
|
|
|
|
|
true
|
|
|
|
|
end
|
|
|
|
|
|
2019-06-02 01:26:36 +00:00
|
|
|
|
def has_access? groups
|
2014-03-23 00:22:25 +00:00
|
|
|
|
admin? or groups.exists?(:id => group)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def new_messages
|
2019-10-10 19:12:17 +00:00
|
|
|
|
received_personal_messages.union(received_team_messages).unread_by(self)
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def received_messages
|
2019-09-24 19:55:34 +00:00
|
|
|
|
received_personal_messages.union(received_team_messages)
|
2017-12-06 21:20:55 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def received_team_messages
|
|
|
|
|
Message.where(recipient_id: team_id, recipient_type: 'Team' )
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def sent_messages
|
2019-09-24 19:55:34 +00:00
|
|
|
|
sent_personal_messages.union(sent_team_messages)
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def upcoming_matches
|
|
|
|
|
upcoming_team_matches.ordered | upcoming_ref_matches.ordered
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def past_matches
|
|
|
|
|
past_team_matches.unfinished.ordered | past_ref_matches.unfinished.ordered
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def unread_issues
|
|
|
|
|
issues.unread_by(self)
|
|
|
|
|
end
|
|
|
|
|
|
2020-11-17 02:15:07 +00:00
|
|
|
|
def duplicates
|
|
|
|
|
# TODO: user arel
|
|
|
|
|
User.where('lower(username) = ? AND users.id != ?', username.downcase, id)
|
|
|
|
|
end
|
|
|
|
|
|
2015-09-24 00:35:30 +00:00
|
|
|
|
def correct_steamid_universe
|
2015-09-24 09:19:00 +00:00
|
|
|
|
if steamid.present?
|
|
|
|
|
steamid[0] = "0"
|
2015-09-24 00:35:30 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-26 01:52:13 +00:00
|
|
|
|
# FIXME: if team has been removed
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def validate_team
|
|
|
|
|
if team and !active_teams.exists?({:id => team.id})
|
2020-11-17 02:15:07 +00:00
|
|
|
|
# Attempts to fix team, gracefully
|
2020-03-26 01:52:13 +00:00
|
|
|
|
self.team = nil
|
2020-11-17 02:15:07 +00:00
|
|
|
|
self.save
|
2014-03-23 00:22:25 +00:00
|
|
|
|
errors.add :team
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def init_variables
|
|
|
|
|
self.public_email = false
|
|
|
|
|
self.time_zone = "Amsterdam"
|
2020-04-10 17:46:52 +00:00
|
|
|
|
if !raw_password and new_record?
|
|
|
|
|
generate_password
|
|
|
|
|
end
|
|
|
|
|
unless profile&.present?
|
2020-04-13 22:42:12 +00:00
|
|
|
|
self.build_profile
|
2020-04-10 17:46:52 +00:00
|
|
|
|
end
|
2020-04-13 22:46:39 +00:00
|
|
|
|
self.email = "%s@ensl.org" % cleanup_string(username) if email.blank?
|
2020-04-10 17:46:52 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def generate_password
|
|
|
|
|
self.raw_password = SecureRandom.alphanumeric(24)
|
|
|
|
|
self.password_hash = User::PASSWORD_SCRYPT
|
|
|
|
|
self.random_password = true
|
2020-04-10 15:32:18 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def create_profile
|
|
|
|
|
if profile
|
2020-04-10 17:46:52 +00:00
|
|
|
|
profile.user_id = self.id
|
2020-04-10 15:32:18 +00:00
|
|
|
|
profile.save
|
|
|
|
|
end
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-04-02 00:08:19 +00:00
|
|
|
|
# NOTE: function does not call save
|
|
|
|
|
# Maybe it should return to not waste save?
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def update_password
|
2020-04-06 20:41:42 +00:00
|
|
|
|
# Standard logic for saving password
|
2020-04-02 00:08:19 +00:00
|
|
|
|
if raw_password and raw_password.length > 0
|
2020-04-06 20:41:42 +00:00
|
|
|
|
# Allow old hash too
|
|
|
|
|
if password_hash == User::PASSWORD_MD5 and password_force
|
|
|
|
|
self.password = Digest::MD5.hexdigest(raw_password)
|
|
|
|
|
else
|
|
|
|
|
self.password_hash = User::PASSWORD_SCRYPT
|
|
|
|
|
self.password = SCrypt::Password.create(raw_password)
|
|
|
|
|
end
|
|
|
|
|
# Update MD5 to MD5+Scrypt
|
|
|
|
|
elsif password_hash == User::PASSWORD_MD5 and !password_force
|
2020-04-02 00:08:19 +00:00
|
|
|
|
# Scrypt(Md5(passsword))
|
|
|
|
|
self.password_hash = User::PASSWORD_MD5_SCRYPT
|
2020-04-06 20:41:42 +00:00
|
|
|
|
self.password = SCrypt::Password.create(password)
|
2020-04-02 00:08:19 +00:00
|
|
|
|
end
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-04-10 17:46:52 +00:00
|
|
|
|
# This serves multiple functions
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def send_new_password
|
2020-04-13 16:01:48 +00:00
|
|
|
|
generate_password unless self.raw_password&.length.to_i > 0
|
2020-04-10 17:46:52 +00:00
|
|
|
|
self.save!
|
|
|
|
|
|
|
|
|
|
# TODO: consider moving these two to callbacks
|
|
|
|
|
self.send_password_message
|
|
|
|
|
Notifications.password(self, raw_password).deliver
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def send_password_message(text = User::PASSWORD_MESSAGE)
|
|
|
|
|
msg = Message.new
|
|
|
|
|
msg.title = "New password for ENSL website"
|
|
|
|
|
msg.text = text % [username, raw_password, password_hash_s]
|
|
|
|
|
msg.sender_type = 'System'
|
|
|
|
|
msg.recipient_type = 'User'
|
|
|
|
|
msg.recipient = self
|
|
|
|
|
msg.save
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def can_play?
|
2020-03-23 02:10:31 +00:00
|
|
|
|
(gathers.where("gathers.status > ?", Gather::STATE_RUNNING).count > 0) or created_at < 2.years.ago
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def can_create? cuser
|
|
|
|
|
true
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-10 15:32:18 +00:00
|
|
|
|
def fix_attributes
|
|
|
|
|
if errors[:username]
|
|
|
|
|
i = 2
|
|
|
|
|
loop do
|
|
|
|
|
new_username = "%s%d" % [username, i]
|
|
|
|
|
i+=1
|
2020-04-10 17:46:52 +00:00
|
|
|
|
if User.where(username: new_username).count == 0 or i > 50
|
|
|
|
|
self.username = new_username
|
|
|
|
|
break
|
|
|
|
|
end
|
2020-04-10 15:32:18 +00:00
|
|
|
|
end
|
2020-04-13 15:34:12 +00:00
|
|
|
|
|
2020-04-10 17:46:52 +00:00
|
|
|
|
end
|
|
|
|
|
if errors[:email]
|
|
|
|
|
self.email = "%s@ensl.org" % cleanup_string(username)
|
2020-04-10 15:32:18 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def can_update? cuser
|
|
|
|
|
cuser and (self == cuser or cuser.admin?)
|
|
|
|
|
end
|
|
|
|
|
|
2015-06-27 12:23:37 +00:00
|
|
|
|
def can_change_name? cuser
|
|
|
|
|
cuser and cuser.admin?
|
|
|
|
|
end
|
|
|
|
|
|
2014-03-23 00:22:25 +00:00
|
|
|
|
def can_destroy? cuser
|
|
|
|
|
cuser and cuser.admin?
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-21 20:33:44 +00:00
|
|
|
|
def self.authenticate(login)
|
2020-11-15 03:11:41 +00:00
|
|
|
|
user = where('lower(username) = ?', login[:username].downcase).first
|
2020-04-13 15:34:12 +00:00
|
|
|
|
return nil unless user
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
case user.password_hash
|
|
|
|
|
when User::PASSWORD_SCRYPT
|
|
|
|
|
# FIXME: If exception occurs here, user cannot log in
|
2021-08-05 17:01:40 +00:00
|
|
|
|
begin
|
|
|
|
|
pass = SCrypt::Password.new(user.password)
|
|
|
|
|
rescue
|
|
|
|
|
logger.error "User (%s) password hash is invalid."
|
|
|
|
|
flash[:error] = "Password hash is invalid, please use forget password functionality or contact admin."
|
|
|
|
|
return nil
|
|
|
|
|
end
|
2020-04-13 15:34:12 +00:00
|
|
|
|
return user if pass == login[:password]
|
|
|
|
|
when User::PASSWORD_MD5_SCRYPT
|
|
|
|
|
pass = SCrypt::Password.new(user.password)
|
|
|
|
|
# Match to Scrypt(Md5(password))
|
|
|
|
|
if pass == Digest::MD5.hexdigest(login[:password])
|
|
|
|
|
user.raw_password = login[:password]
|
|
|
|
|
user.update_password
|
|
|
|
|
user.save!
|
|
|
|
|
return user
|
|
|
|
|
end
|
|
|
|
|
# when User::PASSWORD_MD5
|
|
|
|
|
else
|
|
|
|
|
if user.password == Digest::MD5.hexdigest(login[:password])
|
|
|
|
|
user.raw_password = login[:password]
|
|
|
|
|
user.update_password
|
|
|
|
|
user.save!
|
|
|
|
|
return user
|
2020-04-02 00:08:19 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-04-13 15:34:12 +00:00
|
|
|
|
# TODO: controller needs to handle this
|
|
|
|
|
#rescue Exception => ex
|
|
|
|
|
# user.errors.add(:password, "%s (%s)" % [I18n.t(:password_corrupt), ex.class.to_s])
|
|
|
|
|
# return nil
|
2020-04-02 00:08:19 +00:00
|
|
|
|
end
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-04-10 15:32:18 +00:00
|
|
|
|
def self.get(id)
|
|
|
|
|
id ? User.find(id) : ""
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.historic steamid
|
|
|
|
|
if u = User.find_by_sql(["SELECT * FROM user_versions WHERE steamid = ? ORDER BY updated_at", steamid]) and u.length > 0
|
|
|
|
|
User.find u[0]['user_id']
|
|
|
|
|
else
|
|
|
|
|
nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.search(search)
|
2019-06-03 19:38:24 +00:00
|
|
|
|
search ? where("LOWER(username) LIKE LOWER(?) OR steamid LIKE ?", "%#{search}%", "%#{search}%") : all
|
2014-03-23 00:22:25 +00:00
|
|
|
|
end
|
2014-05-10 10:02:57 +00:00
|
|
|
|
|
|
|
|
|
def self.refadmins
|
|
|
|
|
Group.find(Group::REFEREES).users.order(:username) + Group.find(Group::ADMINS).users.order(:username)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.casters
|
|
|
|
|
Group.find(Group::CASTERS).users.order(:username)
|
|
|
|
|
end
|
2020-03-18 03:38:17 +00:00
|
|
|
|
|
2020-03-18 21:23:30 +00:00
|
|
|
|
def self.params(params, cuser, operation)
|
|
|
|
|
profile_attrs ||= cuser.profile.attributes.keys - ["id", "created_at", "updated_at"] if cuser
|
|
|
|
|
allowed = [:raw_password, :firstname, :lastname, :email, :steamid, :country, \
|
|
|
|
|
:birthdate, :timezone, :public_email, :filter, :time_zone, :team_id, \
|
|
|
|
|
profile_attributes: [profile_attrs]]
|
|
|
|
|
allowed << :username if cuser&.admin? || operation == 'create'
|
2020-03-18 20:03:56 +00:00
|
|
|
|
params.require(:user).permit(*allowed)
|
2020-03-18 03:38:17 +00:00
|
|
|
|
end
|
2020-04-10 15:32:18 +00:00
|
|
|
|
|
2020-04-13 22:42:12 +00:00
|
|
|
|
def self.find_or_build(auth_hash, lastip)
|
2020-04-10 15:32:18 +00:00
|
|
|
|
return nil unless auth_hash&.include?(:provider)
|
|
|
|
|
case auth_hash[:provider]
|
|
|
|
|
when 'steam'
|
|
|
|
|
return nil unless auth_hash&.include?(:uid)
|
|
|
|
|
steamid = SteamID::from_steamID64(auth_hash[:uid])
|
|
|
|
|
user = User.where("LOWER(steamid) = LOWER(?)", steamid).first
|
|
|
|
|
unless user
|
|
|
|
|
user = User.new(username: auth_hash[:info][:nickname], lastip: lastip, fullname: auth_hash[:info][:name], steamid: steamid)
|
|
|
|
|
user.fix_attributes
|
2020-04-13 22:42:12 +00:00
|
|
|
|
user.build_profile
|
2020-04-10 15:32:18 +00:00
|
|
|
|
# TODO: user make valid by force
|
|
|
|
|
# user.profile.country
|
|
|
|
|
# get profile picture, :image
|
|
|
|
|
# This really shouldn't fail.
|
|
|
|
|
end
|
|
|
|
|
return user
|
|
|
|
|
end
|
|
|
|
|
return nil
|
|
|
|
|
end
|
2020-04-13 15:34:12 +00:00
|
|
|
|
end
|