PageRenderTime 56ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/app/models/user.rb

https://gitlab.com/abner/noosfero
Ruby | 461 lines | 353 code | 79 blank | 29 comment | 35 complexity | 64573b258377370f965a991141f44f4c MD5 | raw file
  1. require 'digest/sha1'
  2. require 'user_activation_job'
  3. require 'securerandom'
  4. # User models the system users, and is generated by the acts_as_authenticated
  5. # Rails generator.
  6. class User < ApplicationRecord
  7. attr_accessible :login, :email, :password, :password_confirmation, :activated_at
  8. N_('Password')
  9. N_('Password confirmation')
  10. N_('Terms accepted')
  11. SEARCHABLE_FIELDS = {
  12. :email => {:label => _('Email'), :weight => 5},
  13. }
  14. # see http://stackoverflow.com/a/2513456/670229
  15. def self.current
  16. Thread.current[:current_user]
  17. end
  18. def self.current=(user)
  19. Thread.current[:current_user] = user
  20. end
  21. def self.[](login)
  22. self.find_by login: login
  23. end
  24. # FIXME ugly workaround
  25. def self.human_attribute_name_with_customization(attrib, options={})
  26. case attrib.to_sym
  27. when :login
  28. return [_('Username'), _('Email')].join(' / ')
  29. when :email
  30. return _('e-Mail')
  31. else _(self.human_attribute_name_without_customization(attrib))
  32. end
  33. end
  34. class << self
  35. alias_method_chain :human_attribute_name, :customization
  36. end
  37. def self.build(user_data, person_data, environment)
  38. user = User.new(user_data)
  39. user.terms_of_use = environment.terms_of_use
  40. user.environment = environment
  41. user.person_data = person_data
  42. user
  43. end
  44. before_create do |user|
  45. if user.environment.nil?
  46. user.environment = Environment.default
  47. end
  48. user.send(:make_activation_code) unless user.environment.enabled?('skip_new_user_email_confirmation')
  49. end
  50. after_create do |user|
  51. unless user.person
  52. p = Person.new
  53. p.attributes = user.person_data
  54. p.identifier = user.login if p.identifier.blank?
  55. p.user = user
  56. p.environment = user.environment
  57. p.name ||= user.name || user.login
  58. p.visible = false unless user.activated?
  59. p.save!
  60. user.person = p
  61. end
  62. if user.environment.enabled?('skip_new_user_email_confirmation')
  63. if user.environment.enabled?('admin_must_approve_new_users')
  64. create_moderate_task
  65. else
  66. user.activate
  67. end
  68. end
  69. end
  70. after_create :deliver_activation_code
  71. after_create :delay_activation_check
  72. attr_writer :person_data
  73. def person_data
  74. @person_data = {} if @person_data.nil?
  75. @person_data
  76. end
  77. def email_domain
  78. self.person.preferred_domain && self.person.preferred_domain.name || self.environment.default_hostname(true)
  79. end
  80. # virtual attribute used to stash which community to join on signup or login
  81. attr_accessor :community_to_join
  82. def signup!
  83. User.transaction do
  84. self.save!
  85. self.person.save!
  86. end
  87. end
  88. # set autosave to false as we do manually when needed and Person syncs with us
  89. has_one :person, dependent: :destroy, autosave: false
  90. belongs_to :environment
  91. has_many :sessions, dependent: :destroy
  92. # holds the current session, see lib/authenticated_system.rb
  93. attr_accessor :session
  94. attr_protected :activated_at
  95. # Virtual attribute for the unencrypted password
  96. attr_accessor :password, :name
  97. validates_presence_of :login, :email
  98. validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?})
  99. validates_presence_of :password, :if => :password_required?
  100. validates_presence_of :password_confirmation, :if => :password_required?
  101. validates_length_of :password, :within => 4..40, :if => :password_required?
  102. validates_confirmation_of :password, :if => :password_required?
  103. validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?})
  104. validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?})
  105. validates_uniqueness_of :login, :email, :case_sensitive => false, :scope => :environment_id
  106. before_save :encrypt_password
  107. before_save :normalize_email, if: proc{ |u| u.email.present? }
  108. before_save :generate_private_token_if_not_exist
  109. validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?})
  110. validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n
  111. scope :has_login?, lambda { |login,email,environment_id|
  112. where('login = ? OR email = ?', login, email).
  113. where(environment_id: environment_id)
  114. }
  115. # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
  116. def self.authenticate(login, password, environment = nil)
  117. environment ||= Environment.default
  118. u = self.has_login?(login, login, environment.id)
  119. u = u.first if u.is_a?(ActiveRecord::Relation)
  120. if u && u.authenticated?(password)
  121. u.generate_private_token_if_not_exist
  122. return u
  123. end
  124. return nil
  125. end
  126. def register_login
  127. self.update_attribute :last_login_at, Time.now
  128. end
  129. def generate_private_token
  130. self.private_token = SecureRandom.hex
  131. self.private_token_generated_at = DateTime.now
  132. end
  133. def generate_private_token!
  134. self.generate_private_token
  135. save(:validate => false)
  136. end
  137. def generate_private_token_if_not_exist
  138. unless self.private_token
  139. self.generate_private_token
  140. end
  141. end
  142. # Activates the user in the database.
  143. def activate
  144. return false unless self.person
  145. self.activated_at = Time.now.utc
  146. self.activation_code = nil
  147. self.person.visible = true
  148. begin
  149. self.person.save! && self.save!
  150. rescue Exception => exception
  151. logger.error(exception.to_s)
  152. false
  153. else
  154. if environment.enabled?('send_welcome_email_to_new_users') && environment.has_signup_welcome_text?
  155. Delayed::Job.enqueue(UserMailer::Job.new(self, :signup_welcome_email))
  156. end
  157. true
  158. end
  159. end
  160. # Deactivates the user in the database.
  161. def deactivate
  162. return false unless self.person
  163. self.activated_at = nil
  164. self.person.visible = false
  165. begin
  166. self.person.save! && self.save!
  167. rescue Exception => exception
  168. logger.error(exception.to_s)
  169. false
  170. else
  171. true
  172. end
  173. end
  174. def create_moderate_task
  175. @task = ModerateUserRegistration.new
  176. @task.user_id = self.id
  177. @task.name = self.name
  178. @task.email = self.email
  179. @task.target = self.environment
  180. @task.requestor = self.person
  181. @task.save
  182. end
  183. def activated?
  184. self.activation_code.nil? && !self.activated_at.nil?
  185. end
  186. class UnsupportedEncryptionType < Exception; end
  187. def self.system_encryption_method
  188. @system_encryption_method || :salted_sha1
  189. end
  190. def self.system_encryption_method=(method)
  191. @system_encryption_method = method
  192. end
  193. # a Hash containing the available encryption methods. Keys are symbols,
  194. # values are Proc objects that contain the actual encryption code.
  195. def self.encryption_methods
  196. @encryption_methods ||= {}
  197. end
  198. # adds a new encryption method.
  199. def self.add_encryption_method(sym, &block)
  200. encryption_methods[sym] = block
  201. end
  202. # the encryption method used for this instance
  203. def encryption_method
  204. (password_type || User.system_encryption_method).to_sym
  205. end
  206. # Encrypts the password using the chosen method
  207. def encrypt(password)
  208. method = self.class.encryption_methods[encryption_method]
  209. if method
  210. method.call(password, salt)
  211. else
  212. raise UnsupportedEncryptionType, "Unsupported encryption type: #{encryption_method}"
  213. end
  214. end
  215. add_encryption_method :salted_sha1 do |password, salt|
  216. Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  217. end
  218. add_encryption_method :md5 do |password, salt|
  219. Digest::MD5.hexdigest(password)
  220. end
  221. add_encryption_method :salted_md5 do |password, salt|
  222. Digest::MD5.hexdigest(password+salt)
  223. end
  224. add_encryption_method :clear do |password, salt|
  225. password
  226. end
  227. add_encryption_method :crypt do |password, salt|
  228. password.crypt(salt)
  229. end
  230. class UserNotActivated < StandardError
  231. attr_reader :user
  232. def initialize(message, user = nil)
  233. @user = user
  234. super(message)
  235. end
  236. end
  237. def authenticated?(password)
  238. unless self.activated?
  239. message = _('The user "%{login}" is not activated! Please check your email to activate your user') % {login: self.login}
  240. raise UserNotActivated.new(message, self)
  241. end
  242. result = (crypted_password == encrypt(password))
  243. if (encryption_method != User.system_encryption_method) && result
  244. self.password_type = User.system_encryption_method.to_s
  245. self.password = password
  246. self.password_confirmation = password
  247. self.save!
  248. end
  249. result
  250. end
  251. def remember_token?
  252. remember_token_expires_at && Time.now.utc < remember_token_expires_at
  253. end
  254. # These create and unset the fields required for remembering users between browser closes
  255. def remember_me
  256. self.remember_token_expires_at = 1.months.from_now.utc
  257. # if the user's email/password changes this won't be valid anymore
  258. self.remember_token = encrypt "#{email}-#{self.crypted_password}-#{remember_token_expires_at}"
  259. save(:validate => false)
  260. end
  261. def forget_me
  262. self.remember_token_expires_at = nil
  263. self.remember_token = nil
  264. save(:validate => false)
  265. end
  266. # Exception thrown when #change_password! is called with a wrong current
  267. # password
  268. class IncorrectPassword < Exception; end
  269. # Changes the password of a user.
  270. #
  271. # * Raises IncorrectPassword if <tt>current</tt> is different from the user's
  272. # current password.
  273. # * Saves the record unless it is a new one.
  274. def change_password!(current, new, confirmation)
  275. begin
  276. unless self.authenticated?(current)
  277. self.errors.add(:current_password, _('does not match.'))
  278. raise IncorrectPassword
  279. end
  280. rescue UserNotActivated => e
  281. self.errors.add(:current_password, e.message)
  282. raise UserNotActivated
  283. end
  284. self.force_change_password!(new, confirmation)
  285. end
  286. # Changes the password of a user without asking for the old password. This
  287. # method is intended to be used by the "I forgot my password", and must be
  288. # used with care.
  289. def force_change_password!(new, confirmation)
  290. self.password = new
  291. self.password_confirmation = confirmation
  292. save! unless new_record?
  293. end
  294. def name
  295. name = (@name || login)
  296. person.nil? ? name : (person.name || name)
  297. end
  298. def name= name
  299. @name = name
  300. end
  301. def enable_email!
  302. self.update_attribute(:enable_email, true)
  303. end
  304. def disable_email!
  305. self.update_attribute(:enable_email, false)
  306. end
  307. def email_activation_pending?
  308. if self.environment.nil?
  309. return false
  310. else
  311. return EmailActivation.exists?(:requestor_id => self.person.id, :target_id => self.environment.id, :status => Task::Status::ACTIVE)
  312. end
  313. end
  314. def moderate_registration_pending?
  315. return ModerateUserRegistration.exists?(:requestor_id => self.person.id, :target_id => self.environment.id, :status => Task::Status::ACTIVE)
  316. end
  317. def data_hash(gravatar_default = nil)
  318. friends_list = {}
  319. enterprises = person.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } }
  320. self.person.friends.online.map do |person|
  321. friends_list[person.identifier] = {
  322. 'avatar' => person.profile_custom_icon(gravatar_default),
  323. 'name' => person.short_name,
  324. 'jid' => person.full_jid,
  325. 'status' => person.user.chat_status,
  326. }
  327. end
  328. {
  329. 'login' => self.login,
  330. 'name' => self.person.name,
  331. 'email' => self.email,
  332. 'avatar' => self.person.profile_custom_icon(gravatar_default),
  333. 'is_admin' => self.person.is_admin?,
  334. 'since_month' => self.person.created_at.month,
  335. 'since_year' => self.person.created_at.year,
  336. 'email_domain' => self.enable_email ? self.email_domain : nil,
  337. 'friends_list' => friends_list,
  338. 'enterprises' => enterprises,
  339. 'amount_of_friends' => friends_list.count,
  340. 'chat_enabled' => person.environment.enabled?('xmpp_chat')
  341. }
  342. end
  343. def self.expires_chat_status_every
  344. 15 # in minutes
  345. end
  346. def not_require_password!
  347. @is_password_required = false
  348. end
  349. def resend_activation_code
  350. return if self.activated?
  351. update_attribute(:activation_code, make_activation_code)
  352. self.deliver_activation_code
  353. end
  354. protected
  355. def normalize_email
  356. self.email = self.email.squish.downcase
  357. end
  358. # before filter
  359. def encrypt_password
  360. return if password.blank?
  361. self.salt ||= Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
  362. self.password_type ||= User.system_encryption_method.to_s
  363. self.crypted_password = encrypt(password)
  364. end
  365. def password_required?
  366. (crypted_password.blank? || !password.blank?) && is_password_required?
  367. end
  368. def is_password_required?
  369. @is_password_required.nil? ? true : @is_password_required
  370. end
  371. def make_activation_code
  372. self.activation_code = Digest::SHA1.hexdigest(Time.now.to_s.split(//).sort_by{rand}.join)
  373. end
  374. def deliver_activation_code
  375. return if person.is_template?
  376. Delayed::Job.enqueue(UserMailer::Job.new(self, :activation_code)) unless self.activation_code.blank?
  377. end
  378. def delay_activation_check
  379. return if person.is_template?
  380. Delayed::Job.enqueue(UserActivationJob.new(self.id), {:priority => 0, :run_at => (NOOSFERO_CONF['hours_until_user_activation_check'] || 72).hours.from_now})
  381. end
  382. end