user.rb 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. require 'digest/md5'
  3. # @model User
  4. #
  5. # @property id(required) [Integer] The identifier for the User.
  6. # @property login(required) [String] The login of the User used for authentication.
  7. # @property firstname [String] The firstname of the User.
  8. # @property lastname [String] The lastname of the User.
  9. # @property email [String] The email of the User.
  10. # @property image [String] The Image used as the User avatar (TODO: Image model?).
  11. # @property web [String] The website/URL of the User.
  12. # @property password [String] The password of the User.
  13. # @property phone [String] The phone number of the User.
  14. # @property fax [String] The fax number of the User.
  15. # @property mobile [String] The mobile number of the User.
  16. # @property department [String] The department the User is working at.
  17. # @property street [String] The street the User lives in.
  18. # @property zip [Integer] The zip postal code of the User city.
  19. # @property city [String] The city the User lives in.
  20. # @property country [String] The country the User lives in.
  21. # @property verified [Boolean] The flag that shows the verified state of the User.
  22. # @property active [Boolean] The flag that shows the active state of the User.
  23. # @property note [String] The note or comment stored to the User.
  24. class User < ApplicationModel
  25. load 'user/permission.rb'
  26. include User::Permission
  27. load 'user/assets.rb'
  28. include User::Assets
  29. extend User::Search
  30. load 'user/search_index.rb'
  31. include User::SearchIndex
  32. before_validation :check_name, :check_email, :check_login, :check_password
  33. before_create :check_preferences_default, :validate_roles
  34. before_update :check_preferences_default, :validate_roles
  35. after_create :avatar_for_email_check
  36. after_update :avatar_for_email_check
  37. after_destroy :avatar_destroy
  38. notify_clients_support
  39. has_and_belongs_to_many :groups, after_add: :cache_update, after_remove: :cache_update, class_name: 'Group'
  40. has_and_belongs_to_many :roles, after_add: [:cache_update, :check_notifications], after_remove: :cache_update, class_name: 'Role'
  41. has_and_belongs_to_many :organizations, after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
  42. #has_many :permissions, class_name: 'Permission', through: :roles, class_name: 'Role'
  43. has_many :tokens, after_add: :cache_update, after_remove: :cache_update
  44. has_many :authorizations, after_add: :cache_update, after_remove: :cache_update
  45. belongs_to :organization, class_name: 'Organization'
  46. store :preferences
  47. activity_stream_support(
  48. permission: 'admin.user',
  49. ignore_attributes: {
  50. last_login: true,
  51. login_failed: true,
  52. image: true,
  53. image_source: true,
  54. preferences: true,
  55. }
  56. )
  57. history_support(
  58. ignore_attributes: {
  59. password: true,
  60. image: true,
  61. image_source: true,
  62. preferences: true,
  63. }
  64. )
  65. search_index_support(
  66. ignore_attributes: {
  67. password: true,
  68. image: true,
  69. image_source: true,
  70. source: true,
  71. login_failed: true,
  72. preferences: true,
  73. },
  74. ignore_ids: [1],
  75. )
  76. =begin
  77. fullname of user
  78. user = User.find(123)
  79. result = user.fullname
  80. returns
  81. result = "Bob Smith"
  82. =end
  83. def fullname
  84. name = ''
  85. if firstname && !firstname.empty?
  86. name = name + firstname
  87. end
  88. if lastname && !lastname.empty?
  89. if name != ''
  90. name += ' '
  91. end
  92. name += lastname
  93. end
  94. if name == '' && email
  95. name = email
  96. end
  97. name
  98. end
  99. =begin
  100. longname of user
  101. user = User.find(123)
  102. result = user.longname
  103. returns
  104. result = "Bob Smith"
  105. or with org
  106. result = "Bob Smith (Org ABC)"
  107. =end
  108. def longname
  109. name = fullname
  110. if organization_id
  111. organization = Organization.lookup(id: organization_id)
  112. if organization
  113. name += " (#{organization.name})"
  114. end
  115. end
  116. name
  117. end
  118. =begin
  119. check if user is in role
  120. user = User.find(123)
  121. result = user.role?('Customer')
  122. result = user.role?(['Agent', 'Admin'])
  123. returns
  124. result = true|false
  125. =end
  126. def role?(role_name)
  127. result = false
  128. roles.each { |role|
  129. if role_name.class == Array
  130. next if !role_name.include?(role.name)
  131. elsif role.name != role_name
  132. next
  133. end
  134. result = true
  135. break
  136. }
  137. result
  138. end
  139. =begin
  140. get users activity stream
  141. user = User.find(123)
  142. result = user.activity_stream(20)
  143. returns
  144. result = [
  145. {
  146. id: 2,
  147. o_id: 2,
  148. created_by_id: 3,
  149. created_at: '2013-09-28 00:57:21',
  150. object: "User",
  151. type: "created",
  152. },
  153. {
  154. id: 2,
  155. o_id: 2,
  156. created_by_id: 3,
  157. created_at: '2013-09-28 00:59:21',
  158. object: "User",
  159. type: "updated",
  160. },
  161. ]
  162. =end
  163. def activity_stream(limit, fulldata = false)
  164. activity_stream = ActivityStream.list(self, limit)
  165. return activity_stream if !fulldata
  166. # get related objects
  167. assets = ApplicationModel.assets_of_object_list(activity_stream)
  168. {
  169. activity_stream: activity_stream,
  170. assets: assets,
  171. }
  172. end
  173. =begin
  174. authenticate user
  175. result = User.authenticate(username, password)
  176. returns
  177. result = user_model # user model if authentication was successfully
  178. =end
  179. def self.authenticate(username, password)
  180. # do not authenticate with nothing
  181. return if !username || username == ''
  182. return if !password || password == ''
  183. # try to find user based on login
  184. user = User.find_by(login: username.downcase, active: true)
  185. # try second lookup with email
  186. if !user
  187. user = User.find_by(email: username.downcase, active: true)
  188. end
  189. # check failed logins
  190. max_login_failed = Setting.get('password_max_login_failed').to_i || 10
  191. if user && user.login_failed > max_login_failed
  192. logger.info "Max login faild reached for user #{user.login}."
  193. return false
  194. end
  195. user_auth = Auth.check(username, password, user)
  196. # set login failed +1
  197. if !user_auth && user
  198. sleep 1
  199. user.login_failed = user.login_failed + 1
  200. user.save
  201. end
  202. # auth ok
  203. user_auth
  204. end
  205. =begin
  206. authenticate user agains sso
  207. result = User.sso(sso_params)
  208. returns
  209. result = user_model # user model if authentication was successfully
  210. =end
  211. def self.sso(params)
  212. # try to login against configure auth backends
  213. user_auth = Sso.check(params)
  214. return if !user_auth
  215. user_auth
  216. end
  217. =begin
  218. create user from from omni auth hash
  219. result = User.create_from_hash!(hash)
  220. returns
  221. result = user_model # user model if create was successfully
  222. =end
  223. def self.create_from_hash!(hash)
  224. role_ids = Role.signup_role_ids
  225. url = ''
  226. if hash['info']['urls']
  227. hash['info']['urls'].each { |_name, local_url|
  228. next if !local_url
  229. next if local_url.empty?
  230. url = local_url
  231. }
  232. end
  233. create(
  234. login: hash['info']['nickname'] || hash['uid'],
  235. firstname: hash['info']['name'],
  236. email: hash['info']['email'],
  237. image_source: hash['info']['image'],
  238. web: url,
  239. address: hash['info']['location'],
  240. note: hash['info']['description'],
  241. source: hash['provider'],
  242. role_ids: role_ids,
  243. updated_by_id: 1,
  244. created_by_id: 1,
  245. )
  246. end
  247. =begin
  248. get all permissions of user
  249. user = User.find(123)
  250. user.permissions
  251. returns
  252. {
  253. 'permission.key' => true,
  254. # ...
  255. }
  256. =end
  257. def permissions
  258. list = {}
  259. Object.const_get('Permission').select('permissions.name, permissions.preferences').joins(:roles).where('roles.id IN (?)', role_ids).pluck(:name, :preferences).each { |permission|
  260. next if permission[1]['selectable'] == false
  261. list[permission[0]] = true
  262. }
  263. list
  264. end
  265. =begin
  266. true or false for permission
  267. user = User.find(123)
  268. user.permissions?('permission.key') # access to certain permission.key
  269. user.permissions?(['permission.key1', 'permission.key2']) # access to permission.key1 or permission.key2
  270. user.permissions?('permission') # access to all sub keys
  271. user.permissions?('permission.*') # access if one sub key access exists
  272. returns
  273. true|false
  274. =end
  275. def permissions?(key)
  276. keys = key
  277. names = []
  278. if key.class == String
  279. keys = [key]
  280. end
  281. keys.each { |local_key|
  282. cache_key = "User::permissions?:local_key:::#{id}"
  283. if Rails.env.production?
  284. cache = Cache.get(cache_key)
  285. return cache if cache
  286. end
  287. list = []
  288. if local_key =~ /\.\*$/
  289. local_key.sub!('.*', '.%')
  290. permissions = Object.const_get('Permission').with_parents(local_key)
  291. list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND (permissions.name IN (?) OR permissions.name LIKE ?)', role_ids, true, permissions, local_key).pluck(:preferences)
  292. else
  293. permissions = Object.const_get('Permission').with_parents(local_key)
  294. list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND permissions.name IN (?)', role_ids, true, permissions).pluck(:preferences)
  295. end
  296. list.each { |preferences|
  297. next if preferences[:selectable] == false
  298. Cache.write(key, true, expires_in: 20.seconds)
  299. return true
  300. }
  301. }
  302. Cache.write(key, false, expires_in: 20.seconds)
  303. false
  304. end
  305. =begin
  306. get all users with permission
  307. users = User.with_permissions('admin.session')
  308. get all users with permission "admin.session" or "ticket.agent"
  309. users = User.with_permissions(['admin.session', 'ticket.agent'])
  310. returns
  311. [user1, user2, ...]
  312. =end
  313. def self.with_permissions(keys)
  314. if keys.class != Array
  315. keys = [keys]
  316. end
  317. total_role_ids = []
  318. permission_ids = []
  319. keys.each { |key|
  320. role_ids = []
  321. Object.const_get('Permission').with_parents(key).each { |local_key|
  322. permission = Object.const_get('Permission').lookup(name: local_key)
  323. next if !permission
  324. permission_ids.push permission.id
  325. }
  326. next if permission_ids.empty?
  327. Role.joins(:roles_permissions).where('permissions_roles.permission_id IN (?) AND roles.active = ?', permission_ids, true).uniq().pluck(:id).each { |role_id|
  328. role_ids.push role_id
  329. }
  330. total_role_ids.push role_ids
  331. }
  332. return [] if total_role_ids.empty?
  333. condition = ''
  334. total_role_ids.each { |_role_ids|
  335. if condition != ''
  336. condition += ' OR '
  337. end
  338. condition += 'roles_users.role_id IN (?)'
  339. }
  340. User.joins(:users_roles).where("(#{condition}) AND users.active = ?", *total_role_ids, true).distinct.order(:id)
  341. end
  342. =begin
  343. generate new token for reset password
  344. result = User.password_reset_new_token(username)
  345. returns
  346. result = {
  347. token: token,
  348. user: user,
  349. }
  350. =end
  351. def self.password_reset_new_token(username)
  352. return if !username || username == ''
  353. # try to find user based on login
  354. user = User.find_by(login: username.downcase, active: true)
  355. # try second lookup with email
  356. if !user
  357. user = User.find_by(email: username.downcase, active: true)
  358. end
  359. # check if email address exists
  360. return if !user
  361. return if !user.email
  362. # generate token
  363. token = Token.create(action: 'PasswordReset', user_id: user.id)
  364. {
  365. token: token,
  366. user: user,
  367. }
  368. end
  369. =begin
  370. check reset password token
  371. result = User.password_reset_check(token)
  372. returns
  373. result = user_model # user_model if token was verified
  374. =end
  375. def self.password_reset_check(token)
  376. user = Token.check(action: 'PasswordReset', name: token)
  377. # reset login failed if token is valid
  378. if user
  379. user.login_failed = 0
  380. user.save
  381. end
  382. user
  383. end
  384. =begin
  385. reset password with token and set new password
  386. result = User.password_reset_via_token(token,password)
  387. returns
  388. result = user_model # user_model if token was verified
  389. =end
  390. def self.password_reset_via_token(token, password)
  391. # check token
  392. user = Token.check(action: 'PasswordReset', name: token)
  393. return if !user
  394. # reset password
  395. user.update_attributes(password: password)
  396. # delete token
  397. Token.find_by(action: 'PasswordReset', name: token).destroy
  398. user
  399. end
  400. =begin
  401. update last login date and reset login_failed (is automatically done by auth and sso backend)
  402. user = User.find(123)
  403. result = user.update_last_login
  404. returns
  405. result = new_user_model
  406. =end
  407. def update_last_login
  408. self.last_login = Time.zone.now
  409. # reset login failed
  410. self.login_failed = 0
  411. save
  412. end
  413. =begin
  414. generate new token for signup
  415. result = User.signup_new_token(user) # or email
  416. returns
  417. result = {
  418. token: token,
  419. user: user,
  420. }
  421. =end
  422. def self.signup_new_token(user)
  423. return if !user
  424. return if !user.email
  425. # generate token
  426. token = Token.create(action: 'Signup', user_id: user.id)
  427. {
  428. token: token,
  429. user: user,
  430. }
  431. end
  432. =begin
  433. verify signup with token
  434. result = User.signup_verify_via_token(token, user)
  435. returns
  436. result = user_model # user_model if token was verified
  437. =end
  438. def self.signup_verify_via_token(token, user = nil)
  439. # check token
  440. local_user = Token.check(action: 'Signup', name: token)
  441. return if !local_user
  442. # if requested user is different to current user
  443. return if user && local_user.id != user.id
  444. # set verified
  445. local_user.update_attributes(verified: true)
  446. # delete token
  447. Token.find_by(action: 'Signup', name: token).destroy
  448. local_user
  449. end
  450. =begin
  451. merge two users to one
  452. user = User.find(123)
  453. result = user.merge(user_id_of_duplicate_user)
  454. returns
  455. result = new_user_model
  456. =end
  457. def merge(user_id_of_duplicate_user)
  458. # find email addresses and move them to primary user
  459. duplicate_user = User.find(user_id_of_duplicate_user)
  460. # merge missing attibutes
  461. Models.merge('User', id, user_id_of_duplicate_user)
  462. true
  463. end
  464. =begin
  465. list of active users in role
  466. result = User.of_role('Agent', group_ids)
  467. result = User.of_role(['Agent', 'Admin'])
  468. returns
  469. result = [user1, user2]
  470. =end
  471. def self.of_role(role, group_ids = nil)
  472. roles_ids = Role.where(active: true, name: role).map(&:id)
  473. if !group_ids
  474. return User.where(active: true).joins(:users_roles).where('roles_users.role_id IN (?)', roles_ids).order('users.updated_at DESC')
  475. end
  476. User.where(active: true)
  477. .joins(:users_roles)
  478. .joins(:users_groups)
  479. .where('roles_users.role_id IN (?) AND users_groups.group_ids IN (?)', roles_ids, group_ids).order('users.updated_at DESC')
  480. end
  481. =begin
  482. update/sync default preferences of users in a dedecated permissions
  483. result = User.update_default_preferences_by_permission('ticket.agent', force)
  484. returns
  485. result = true # false
  486. =end
  487. def self.update_default_preferences_by_permission(permission_name, force = false)
  488. permission = Object.const_get('Permission').lookup(name: permission_name)
  489. return if !permission
  490. default = Rails.configuration.preferences_default_by_permission
  491. return false if !default
  492. default.deep_stringify_keys!
  493. User.with_permissions(permission.name).each { |user|
  494. next if !default[permission.name]
  495. has_changed = false
  496. default[permission.name].each { |key, value|
  497. next if !force && user.preferences[key]
  498. has_changed = true
  499. user.preferences[key] = value
  500. }
  501. if has_changed
  502. user.save!
  503. end
  504. }
  505. true
  506. end
  507. =begin
  508. update/sync default preferences of users in a dedecated role
  509. result = User.update_default_preferences_by_role('Agent', force)
  510. returns
  511. result = true # false
  512. =end
  513. def self.update_default_preferences_by_role(role_name, force = false)
  514. role = Role.lookup(name: role_name)
  515. return if !role
  516. default = Rails.configuration.preferences_default_by_permission
  517. return false if !default
  518. default.deep_stringify_keys!
  519. role.permissions.each { |permission|
  520. User.update_default_preferences_by_permission(permission.name, force)
  521. }
  522. true
  523. end
  524. def check_notifications(o)
  525. default = Rails.configuration.preferences_default_by_permission
  526. return if !default
  527. default.deep_stringify_keys!
  528. has_changed = false
  529. o.permissions.each { |permission|
  530. next if !default[permission.name]
  531. default[permission.name].each { |key, value|
  532. next if preferences[key]
  533. preferences[key] = value
  534. has_changed = true
  535. }
  536. }
  537. return true if !has_changed
  538. if id
  539. save!
  540. return true
  541. end
  542. @preferences_default = preferences
  543. true
  544. end
  545. def check_preferences_default
  546. return if !@preferences_default
  547. return if @preferences_default.empty?
  548. preferences_tmp = @preferences_default.merge(preferences)
  549. self.preferences = preferences_tmp
  550. @preferences_default = nil
  551. true
  552. end
  553. private
  554. def cache_delete
  555. super
  556. # delete asset caches
  557. key = "User::authorizations::#{id}"
  558. Cache.delete(key)
  559. # delete permission cache
  560. key = "User::permissions?:local_key:::#{id}"
  561. Cache.delete(key)
  562. end
  563. def check_name
  564. return if !firstname.empty? && !lastname.empty?
  565. if !firstname.empty? && lastname.empty?
  566. # "Lastname, Firstname"
  567. scan = firstname.scan(/, /)
  568. if scan[0]
  569. name = firstname.split(', ', 2)
  570. if !name[0].nil?
  571. self.lastname = name[0]
  572. end
  573. if !name[1].nil?
  574. self.firstname = name[1]
  575. end
  576. return
  577. end
  578. # "Firstname Lastname"
  579. name = firstname.split(' ', 2)
  580. if !name[0].nil?
  581. self.firstname = name[0]
  582. end
  583. if !name[1].nil?
  584. self.lastname = name[1]
  585. end
  586. return
  587. # -no name- "firstname.lastname@example.com"
  588. elsif firstname.empty? && lastname.empty? && !email.empty?
  589. scan = email.scan(/^(.+?)\.(.+?)\@.+?$/)
  590. if scan[0]
  591. if !scan[0][0].nil?
  592. self.firstname = scan[0][0].capitalize
  593. end
  594. if !scan[0][1].nil?
  595. self.lastname = scan[0][1].capitalize
  596. end
  597. end
  598. end
  599. end
  600. def check_email
  601. return if email.empty?
  602. self.email = email.downcase.strip
  603. return if id == 1
  604. raise Exceptions::UnprocessableEntity, 'Invalid email' if email !~ /@/
  605. raise Exceptions::UnprocessableEntity, 'Invalid email' if email =~ /\s/
  606. end
  607. def check_login
  608. # use email as login if not given
  609. if login.empty? && !email.empty?
  610. self.login = email
  611. end
  612. # if email has changed, login is old email, change also login
  613. if changes && changes['email']
  614. if changes['email'][0] == login
  615. self.login = email
  616. end
  617. end
  618. # if no email, complain about missing login
  619. if id != 1 && login.empty? && email.empty?
  620. raise Exceptions::UnprocessableEntity, 'Attribute \'login\' required!'
  621. end
  622. # check if login already exists
  623. self.login = login.downcase.strip
  624. check = true
  625. while check
  626. exists = User.find_by(login: login)
  627. if exists && exists.id != id
  628. self.login = login + rand(999).to_s
  629. else
  630. check = false
  631. end
  632. end
  633. end
  634. def validate_roles
  635. return if !role_ids
  636. role_ids.each { |role_id|
  637. role = Role.lookup(id: role_id)
  638. raise "Unable to find role for id #{role_id}" if !role
  639. next if !role.preferences[:not]
  640. role.preferences[:not].each { |local_role_name|
  641. local_role = Role.lookup(name: local_role_name)
  642. next if !local_role
  643. raise "Role #{role.name} conflicts with #{local_role.name}" if role_ids.include?(local_role.id)
  644. }
  645. }
  646. end
  647. def avatar_for_email_check
  648. return if !email
  649. return if email.empty?
  650. return if email !~ /@/
  651. return if !changes['email'] && updated_at > Time.zone.now - 10.days
  652. # save/update avatar
  653. avatar = Avatar.auto_detection(
  654. object: 'User',
  655. o_id: id,
  656. url: email,
  657. source: 'app',
  658. updated_by_id: updated_by_id,
  659. created_by_id: updated_by_id,
  660. )
  661. # update user link
  662. return if !avatar
  663. update_column(:image, avatar.store_hash)
  664. cache_delete
  665. end
  666. def avatar_destroy
  667. Avatar.remove('User', id)
  668. end
  669. def check_password
  670. # set old password again if not given
  671. if password == '' || !password
  672. # get current record
  673. if id
  674. #current = User.find(self.id)
  675. #self.password = current.password
  676. self.password = password_was
  677. end
  678. end
  679. # crypt password if not already crypted
  680. return if !password
  681. return if password =~ /^\{sha2\}/
  682. crypted = Digest::SHA2.hexdigest(password)
  683. self.password = "{sha2}#{crypted}"
  684. end
  685. end