user.rb 14 KB

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