user.rb 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. require 'digest/md5'
  3. class User < ApplicationModel
  4. load 'user/assets.rb'
  5. include User::Assets
  6. extend User::Search
  7. include User::SearchIndex
  8. before_create :check_name, :check_email, :check_login, :check_image, :check_password
  9. before_update :check_password, :check_image, :check_email, :check_login_update
  10. after_create :check_image_load, :notify_clients_after_create
  11. after_update :check_image_load, :notify_clients_after_update
  12. after_destroy :notify_clients_after_destroy
  13. has_and_belongs_to_many :groups, :after_add => :cache_update, :after_remove => :cache_update
  14. has_and_belongs_to_many :roles, :after_add => :cache_update, :after_remove => :cache_update
  15. has_and_belongs_to_many :organizations, :after_add => :cache_update, :after_remove => :cache_update
  16. has_many :tokens, :after_add => :cache_update, :after_remove => :cache_update
  17. has_many :authorizations, :after_add => :cache_update, :after_remove => :cache_update
  18. belongs_to :organization, :class_name => 'Organization'
  19. store :preferences
  20. activity_stream_support(
  21. :role => 'Admin',
  22. :ignore_attributes => {
  23. :last_login => true,
  24. :image => true,
  25. :image_source => true,
  26. }
  27. )
  28. history_support(
  29. :ignore_attributes => {
  30. :password => true,
  31. :image => true,
  32. :image_source => true,
  33. }
  34. )
  35. search_index_support(
  36. :ignore_attributes => {
  37. :password => true,
  38. :image => true,
  39. :image_source => true,
  40. :source => true,
  41. :login_failed => true,
  42. :preferences => true,
  43. :locale => true,
  44. }
  45. )
  46. =begin
  47. fullname of user
  48. user = User.find(123)
  49. result = user.fulename
  50. returns
  51. result = "Bob Smith"
  52. =end
  53. def fullname
  54. fullname = ''
  55. if self.firstname
  56. fullname = fullname + self.firstname
  57. end
  58. if self.lastname
  59. if fullname != ''
  60. fullname = fullname + ' '
  61. end
  62. fullname = fullname + self.lastname
  63. end
  64. fullname
  65. end
  66. =begin
  67. check if user is in role
  68. user = User.find(123)
  69. result = user.is_role('Customer')
  70. returns
  71. result = true|false
  72. =end
  73. def is_role( role_name )
  74. self.roles.each { |role|
  75. return role if role.name == role_name
  76. }
  77. false
  78. end
  79. =begin
  80. get users activity stream
  81. user = User.find(123)
  82. result = user.activity_stream( 20 )
  83. returns
  84. result = [
  85. {
  86. :id =>2,
  87. :o_id =>2,
  88. :created_by_id => 3,
  89. :created_at => '2013-09-28 00:57:21',
  90. :object => "User",
  91. :type => "created",
  92. },
  93. {
  94. :id =>2,
  95. :o_id =>2,
  96. :created_by_id => 3,
  97. :created_at => '2013-09-28 00:59:21',
  98. :object => "User",
  99. :type => "updated",
  100. },
  101. ]
  102. =end
  103. def activity_stream( limit, fulldata = false )
  104. activity_stream = ActivityStream.list( self, limit )
  105. return activity_stream if !fulldata
  106. # get related objects
  107. assets = ApplicationModel.assets_of_object_list(activity_stream)
  108. return {
  109. :activity_stream => activity_stream,
  110. :assets => assets,
  111. }
  112. end
  113. =begin
  114. authenticate user
  115. result = User.authenticate(username, password)
  116. returns
  117. result = user_model # user model if authentication was successfully
  118. =end
  119. def self.authenticate( username, password )
  120. # do not authenticate with nothing
  121. return if !username || username == ''
  122. return if !password || password == ''
  123. # try to find user based on login
  124. user = User.where( :login => username.downcase, :active => true ).first
  125. # try second lookup with email
  126. if !user
  127. user = User.where( :email => username.downcase, :active => true ).first
  128. end
  129. # check failed logins
  130. max_login_failed = Setting.get('password_max_login_failed') || 10
  131. if user && user.login_failed > max_login_failed
  132. return false
  133. end
  134. user_auth = Auth.check( username, password, user )
  135. # set login failed +1
  136. if !user_auth && user
  137. sleep 1
  138. user.login_failed = user.login_failed + 1
  139. user.save
  140. end
  141. # auth ok
  142. return user_auth
  143. end
  144. =begin
  145. authenticate user agains sso
  146. result = User.sso(sso_params)
  147. returns
  148. result = user_model # user model if authentication was successfully
  149. =end
  150. def self.sso(params)
  151. # try to login against configure auth backends
  152. user_auth = Sso.check( params )
  153. return if !user_auth
  154. return user_auth
  155. end
  156. =begin
  157. create user from from omni auth hash
  158. result = User.create_from_hash!(hash)
  159. returns
  160. result = user_model # user model if create was successfully
  161. =end
  162. def self.create_from_hash!(hash)
  163. url = ''
  164. if hash['info']['urls'] then
  165. url = hash['info']['urls']['Website'] || hash['info']['urls']['Twitter'] || ''
  166. end
  167. roles = Role.where( :name => 'Customer' )
  168. self.create(
  169. :login => hash['info']['nickname'] || hash['uid'],
  170. :firstname => hash['info']['name'],
  171. :email => hash['info']['email'],
  172. :image => hash['info']['image'],
  173. # :url => url.to_s,
  174. :note => hash['info']['description'],
  175. :source => hash['provider'],
  176. :roles => roles,
  177. :updated_by_id => 1,
  178. :created_by_id => 1,
  179. )
  180. end
  181. =begin
  182. send reset password email with token to user
  183. result = User.password_reset_send(username)
  184. returns
  185. result = true|false
  186. =end
  187. def self.password_reset_send(username)
  188. return if !username || username == ''
  189. # try to find user based on login
  190. user = User.where( :login => username.downcase, :active => true ).first
  191. # try second lookup with email
  192. if !user
  193. user = User.where( :email => username.downcase, :active => true ).first
  194. end
  195. # check if email address exists
  196. return if !user
  197. return if !user.email
  198. # generate token
  199. token = Token.create( :action => 'PasswordReset', :user_id => user.id )
  200. # send mail
  201. data = {}
  202. data[:subject] = 'Reset your #{config.product_name} password'
  203. data[:body] = 'Forgot your password?
  204. We received a request to reset the password for your #{config.product_name} account (#{user.login}).
  205. If you want to reset your password, click on the link below (or copy and paste the URL into your browser):
  206. #{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}
  207. This link takes you to a page where you can change your password.
  208. If you don\'t want to reset your password, please ignore this message. Your password will not be reset.
  209. Your #{config.product_name} Team
  210. '
  211. # prepare subject & body
  212. [:subject, :body].each { |key|
  213. data[key.to_sym] = NotificationFactory.build(
  214. :locale => user.locale,
  215. :string => data[key.to_sym],
  216. :objects => {
  217. :token => token,
  218. :user => user,
  219. }
  220. )
  221. }
  222. # send notification
  223. NotificationFactory.send(
  224. :recipient => user,
  225. :subject => data[:subject],
  226. :body => data[:body]
  227. )
  228. return true
  229. end
  230. =begin
  231. check reset password token
  232. result = User.password_reset_check(token)
  233. returns
  234. result = user_model # user_model if token was verified
  235. =end
  236. def self.password_reset_check(token)
  237. user = Token.check( :action => 'PasswordReset', :name => token )
  238. # reset login failed if token is valid
  239. if user
  240. user.login_failed = 0
  241. user.save
  242. end
  243. return user
  244. end
  245. =begin
  246. reset reset password with token and set new password
  247. result = User.password_reset_via_token(token,password)
  248. returns
  249. result = user_model # user_model if token was verified
  250. =end
  251. def self.password_reset_via_token(token,password)
  252. # check token
  253. user = Token.check( :action => 'PasswordReset', :name => token )
  254. return if !user
  255. # reset password
  256. user.update_attributes( :password => password )
  257. # delete token
  258. Token.where( :action => 'PasswordReset', :name => token ).first.destroy
  259. return user
  260. end
  261. =begin
  262. update last login date and reset login_failed (is automatically done by auth and sso backend)
  263. user = User.find(123)
  264. result = user.update_last_login
  265. returns
  266. result = new_user_model
  267. =end
  268. def update_last_login
  269. self.last_login = Time.now
  270. # reset login failed
  271. self.login_failed = 0
  272. # set updated by user
  273. self.updated_by_id = self.id
  274. self.save
  275. end
  276. =begin
  277. get image of user
  278. user = User.find(123)
  279. result = user.get_image
  280. returns
  281. result = {
  282. :filename => 'some filename',
  283. :content_type => 'image/png',
  284. :content => bin_string,
  285. }
  286. =end
  287. def get_image
  288. # find file
  289. list = Store.list( :object => 'User::Image', :o_id => self.id )
  290. logger.debug list.inspect
  291. if list && list[0]
  292. file = Store.find( list[0] )
  293. result = {
  294. :content => file.content,
  295. :filename => file.filename,
  296. :content_type => file.preferences['Content-Type'] || file.preferences['Mime-Type'],
  297. }
  298. return result
  299. end
  300. # serve defalt image
  301. image = 'R0lGODdhMAAwAOMAAMzMzJaWlr6+vqqqqqOjo8XFxbe3t7GxsZycnAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAMAAwAAAEcxDISau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru98TwuAA+KQAQqJK8EAgBAgMEqmkzUgBIeSwWGZtR5XhSqAULACCoGCJGwlm1MGQrq9RqgB8fm4ZTUgDBIEcRR9fz6HiImKi4yNjo+QkZKTlJWWkBEAOw=='
  302. result = {
  303. :content => Base64.decode64(image),
  304. :filename => 'image.gif',
  305. :content_type => 'image/gif',
  306. }
  307. return result
  308. end
  309. private
  310. def check_name
  311. if ( self.firstname && !self.firstname.empty? ) && ( !self.lastname || self.lastname.empty? )
  312. # Lastname, Firstname
  313. scan = self.firstname.scan(/, /)
  314. if scan[0]
  315. name = self.firstname.split(', ', 2)
  316. self.lastname = name[0]
  317. self.firstname = name[1]
  318. return
  319. end
  320. # Firstname Lastname
  321. name = self.firstname.split(' ', 2)
  322. self.firstname = name[0]
  323. self.lastname = name[1]
  324. return
  325. # -no name- firstname.lastname@example.com
  326. elsif ( !self.firstname || self.firstname.empty? ) && ( !self.lastname || self.lastname.empty? ) && ( self.email && !self.email.empty? )
  327. scan = self.email.scan(/^(.+?)\.(.+?)\@.+?$/)
  328. if scan[0]
  329. self.firstname = scan[0][0].capitalize
  330. self.lastname = scan[0][1].capitalize
  331. end
  332. end
  333. end
  334. def check_email
  335. if self.email
  336. self.email = self.email.downcase
  337. end
  338. end
  339. def check_login
  340. if !self.login && self.email
  341. self.login = self.email
  342. end
  343. if self.login
  344. self.login = self.login.downcase
  345. check = true
  346. while check
  347. exists = User.where( :login => self.login ).first
  348. if exists
  349. self.login = self.login + rand(99).to_s
  350. else
  351. check = false
  352. end
  353. end
  354. end
  355. end
  356. # FIXME: Remove me later
  357. def check_login_update
  358. if self.login
  359. self.login = self.login.downcase
  360. end
  361. end
  362. def check_image
  363. if !self.image_source || self.image_source == '' || self.image_source =~ /gravatar.com/i
  364. if self.email
  365. hash = Digest::MD5.hexdigest(self.email)
  366. self.image_source = "http://www.gravatar.com/avatar/#{hash}?s=48&d=404"
  367. logger.debug "#{self.email}: #{self.image_source}"
  368. end
  369. end
  370. end
  371. def check_image_load
  372. return if !self.image_source
  373. return if self.image_source !~ /http/i
  374. # download image
  375. response = UserAgent.request( self.image_source )
  376. if !response.success?
  377. self.update_column( :image, 'none' )
  378. self.cache_delete
  379. #puts "WARNING: Can't fetch '#{self.image_source}' (maybe no avatar available), http code: #{response.code.to_s}"
  380. #raise "Can't fetch '#{self.image_source}', http code: #{response.code.to_s}"
  381. return
  382. end
  383. #puts "NOTICE: Fetch '#{self.image_source}', http code: #{response.code.to_s}"
  384. # store image local
  385. hash = Digest::MD5.hexdigest( response.body )
  386. # check if image has changed
  387. return if self.image == hash
  388. #puts "NOTICE: update image in store"
  389. # save new image
  390. self.update_column( :image, hash )
  391. Store.remove( :object => 'User::Image', :o_id => self.id )
  392. Store.add(
  393. :object => 'User::Image',
  394. :o_id => self.id,
  395. :data => response.body,
  396. :filename => 'image',
  397. :preferences => {
  398. 'Content-Type' => response.content_type
  399. },
  400. :created_by_id => self.updated_by_id,
  401. )
  402. self.cache_delete
  403. end
  404. def check_password
  405. # set old password again if not given
  406. if self.password == '' || !self.password
  407. # get current record
  408. if self.id
  409. current = User.find(self.id)
  410. self.password = current.password
  411. end
  412. # create crypted password if not already crypted
  413. else
  414. if self.password !~ /^\{sha2\}/
  415. crypted = Digest::SHA2.hexdigest( self.password )
  416. self.password = "{sha2}#{crypted}"
  417. end
  418. end
  419. end
  420. end