user.rb 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
  2. class User < ApplicationModel
  3. include CanBeAuthorized
  4. include CanBeImported
  5. include HasActivityStreamLog
  6. include ChecksClientNotification
  7. include HasHistory
  8. include HasSearchIndexBackend
  9. include CanCsvImport
  10. include ChecksHtmlSanitized
  11. include HasGroups
  12. include HasRoles
  13. include HasObjectManagerAttributes
  14. include ::HasTicketCreateScreenImpact
  15. include HasTaskbars
  16. include User::HasTicketCreateScreenImpact
  17. include User::Assets
  18. include User::Avatar
  19. include User::Search
  20. include User::SearchIndex
  21. include User::TouchesOrganization
  22. include User::PerformsGeoLookup
  23. include User::UpdatesTicketOrganization
  24. include HasTransactionDispatcher
  25. has_and_belongs_to_many :organizations, after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
  26. has_and_belongs_to_many :overviews, dependent: :nullify
  27. has_many :tokens, after_add: :cache_update, after_remove: :cache_update, dependent: :destroy
  28. has_many :authorizations, after_add: :cache_update, after_remove: :cache_update, dependent: :destroy
  29. has_many :online_notifications, dependent: :destroy
  30. has_many :taskbars, dependent: :destroy
  31. has_many :user_devices, dependent: :destroy
  32. has_one :chat_agent_created_by, class_name: 'Chat::Agent', foreign_key: :created_by_id, dependent: :destroy, inverse_of: :created_by
  33. has_one :chat_agent_updated_by, class_name: 'Chat::Agent', foreign_key: :updated_by_id, dependent: :destroy, inverse_of: :updated_by
  34. has_many :chat_sessions, class_name: 'Chat::Session', dependent: :destroy
  35. has_many :karma_user, class_name: 'Karma::User', dependent: :destroy
  36. has_many :mentions, dependent: :destroy
  37. has_many :karma_activity_logs, class_name: 'Karma::ActivityLog', dependent: :destroy
  38. has_many :cti_caller_ids, class_name: 'Cti::CallerId', dependent: :destroy
  39. has_many :customer_tickets, class_name: 'Ticket', foreign_key: :customer_id, dependent: :destroy, inverse_of: :customer
  40. has_many :owner_tickets, class_name: 'Ticket', foreign_key: :owner_id, inverse_of: :owner
  41. has_many :created_recent_views, class_name: 'RecentView', foreign_key: :created_by_id, dependent: :destroy, inverse_of: :created_by
  42. has_many :permissions, -> { where(roles: { active: true }, active: true) }, through: :roles
  43. has_many :data_privacy_tasks, as: :deletable
  44. belongs_to :organization, inverse_of: :members, optional: true
  45. before_validation :check_name, :check_email, :check_login, :ensure_uniq_email, :ensure_password, :ensure_roles, :ensure_identifier
  46. before_validation :check_mail_delivery_failed, on: :update
  47. before_create :check_preferences_default, :validate_preferences, :validate_ooo, :domain_based_assignment, :set_locale
  48. before_update :check_preferences_default, :validate_preferences, :validate_ooo, :reset_login_failed_after_password_change, :validate_agent_limit_by_attributes, :last_admin_check_by_attribute
  49. before_destroy :destroy_longer_required_objects, :destroy_move_dependency_ownership
  50. after_commit :update_caller_id
  51. store :preferences
  52. association_attributes_ignored :online_notifications, :templates, :taskbars, :user_devices, :chat_sessions, :karma_activity_logs, :cti_caller_ids, :text_modules, :customer_tickets, :owner_tickets, :created_recent_views, :chat_agents, :data_privacy_tasks, :overviews, :mentions
  53. activity_stream_permission 'admin.user'
  54. activity_stream_attributes_ignored :last_login,
  55. :login_failed,
  56. :image,
  57. :image_source,
  58. :preferences
  59. association_attributes_ignored :permissions
  60. history_attributes_ignored :password,
  61. :last_login,
  62. :image,
  63. :image_source,
  64. :preferences
  65. search_index_attributes_ignored :password,
  66. :image,
  67. :image_source,
  68. :source,
  69. :login_failed
  70. csv_object_ids_ignored 1
  71. csv_attributes_ignored :password,
  72. :login_failed,
  73. :source,
  74. :image_source,
  75. :image,
  76. :authorizations,
  77. :organizations,
  78. :groups,
  79. :user_groups
  80. sanitized_html :note
  81. def ignore_search_indexing?(_action)
  82. # ignore internal user
  83. return true if id == 1
  84. false
  85. end
  86. =begin
  87. fullname of user
  88. user = User.find(123)
  89. result = user.fullname
  90. returns
  91. result = "Bob Smith"
  92. =end
  93. def fullname
  94. name = ''
  95. if firstname.present?
  96. name = firstname
  97. end
  98. if lastname.present?
  99. if name != ''
  100. name += ' '
  101. end
  102. name += lastname
  103. end
  104. if name.blank? && email.present?
  105. name = email
  106. end
  107. name
  108. end
  109. =begin
  110. longname of user
  111. user = User.find(123)
  112. result = user.longname
  113. returns
  114. result = "Bob Smith"
  115. or with org
  116. result = "Bob Smith (Org ABC)"
  117. =end
  118. def longname
  119. name = fullname
  120. if organization_id
  121. organization = Organization.lookup(id: organization_id)
  122. if organization
  123. name += " (#{organization.name})"
  124. end
  125. end
  126. name
  127. end
  128. =begin
  129. check if user is in role
  130. user = User.find(123)
  131. result = user.role?('Customer')
  132. result = user.role?(['Agent', 'Admin'])
  133. returns
  134. result = true|false
  135. =end
  136. def role?(role_name)
  137. roles.where(name: role_name).any?
  138. end
  139. =begin
  140. check if user is in role
  141. user = User.find(123)
  142. result = user.out_of_office?
  143. returns
  144. result = true|false
  145. =end
  146. def out_of_office?
  147. return false if out_of_office != true
  148. return false if out_of_office_start_at.blank?
  149. return false if out_of_office_end_at.blank?
  150. Time.use_zone(Setting.get('timezone_default').presence) do
  151. start = out_of_office_start_at.beginning_of_day
  152. finish = out_of_office_end_at.end_of_day
  153. Time.zone.now.between? start, finish
  154. end
  155. end
  156. =begin
  157. check if user is in role
  158. user = User.find(123)
  159. result = user.out_of_office_agent
  160. returns
  161. result = user_model
  162. =end
  163. def out_of_office_agent
  164. return if !out_of_office?
  165. return if out_of_office_replacement_id.blank?
  166. User.find_by(id: out_of_office_replacement_id)
  167. end
  168. =begin
  169. gets users where user is replacement
  170. user = User.find(123)
  171. result = user.out_of_office_agent_of
  172. returns
  173. result = [user_model1, user_model2]
  174. =end
  175. def out_of_office_agent_of
  176. User.where(active: true, out_of_office: true, out_of_office_replacement_id: id).where('out_of_office_start_at <= ? AND out_of_office_end_at >= ?', Time.zone.today, Time.zone.today)
  177. end
  178. =begin
  179. get users activity stream
  180. user = User.find(123)
  181. result = user.activity_stream(20)
  182. returns
  183. result = [
  184. {
  185. id: 2,
  186. o_id: 2,
  187. created_by_id: 3,
  188. created_at: '2013-09-28 00:57:21',
  189. object: "User",
  190. type: "created",
  191. },
  192. {
  193. id: 2,
  194. o_id: 2,
  195. created_by_id: 3,
  196. created_at: '2013-09-28 00:59:21',
  197. object: "User",
  198. type: "updated",
  199. },
  200. ]
  201. =end
  202. def activity_stream(limit, fulldata = false)
  203. stream = ActivityStream.list(self, limit)
  204. return stream if !fulldata
  205. # get related objects
  206. assets = {}
  207. stream.each do |item|
  208. assets = item.assets(assets)
  209. end
  210. {
  211. stream: stream,
  212. assets: assets,
  213. }
  214. end
  215. =begin
  216. tries to find the matching instance by the given identifier. Currently email and login is supported.
  217. user = User.indentify('User123')
  218. # or
  219. user = User.indentify('user-123@example.com')
  220. returns
  221. # User instance
  222. user.login # 'user123'
  223. =end
  224. def self.identify(identifier)
  225. return if identifier.blank?
  226. # try to find user based on login
  227. user = User.find_by(login: identifier.downcase)
  228. return user if user
  229. # try second lookup with email
  230. User.find_by(email: identifier.downcase)
  231. end
  232. =begin
  233. create user from from omni auth hash
  234. result = User.create_from_hash!(hash)
  235. returns
  236. result = user_model # user model if create was successfully
  237. =end
  238. def self.create_from_hash!(hash)
  239. url = ''
  240. hash['info']['urls']&.each_value do |local_url|
  241. next if local_url.blank?
  242. url = local_url
  243. end
  244. begin
  245. data = {
  246. login: hash['info']['nickname'] || hash['uid'],
  247. firstname: hash['info']['name'] || hash['info']['display_name'],
  248. email: hash['info']['email'],
  249. image_source: hash['info']['image'],
  250. web: url,
  251. address: hash['info']['location'],
  252. note: hash['info']['description'],
  253. source: hash['provider'],
  254. role_ids: Role.signup_role_ids,
  255. updated_by_id: 1,
  256. created_by_id: 1,
  257. }
  258. if hash['info']['first_name'].present? && hash['info']['last_name'].present?
  259. data[:firstname] = hash['info']['first_name']
  260. data[:lastname] = hash['info']['last_name']
  261. end
  262. create!(data)
  263. rescue => e
  264. logger.error e
  265. raise Exceptions::UnprocessableEntity, e.message
  266. end
  267. end
  268. =begin
  269. returns all accessable permission ids of user
  270. user = User.find(123)
  271. user.permissions_with_child_ids
  272. returns
  273. [permission1_id, permission2_id, permission3_id]
  274. =end
  275. def permissions_with_child_ids
  276. where = ''
  277. where_bind = [true]
  278. permissions.pluck(:name).each do |permission_name|
  279. where += ' OR ' if where != ''
  280. where += 'permissions.name = ? OR permissions.name LIKE ?'
  281. where_bind.push permission_name
  282. where_bind.push "#{permission_name}.%"
  283. end
  284. return [] if where == ''
  285. ::Permission.where("permissions.active = ? AND (#{where})", *where_bind).pluck(:id)
  286. end
  287. =begin
  288. get all users with permission
  289. users = User.with_permissions('ticket.agent')
  290. get all users with permission "admin.session" or "ticket.agent"
  291. users = User.with_permissions(['admin.session', 'ticket.agent'])
  292. returns
  293. [user1, user2, ...]
  294. =end
  295. def self.with_permissions(keys)
  296. if keys.class != Array
  297. keys = [keys]
  298. end
  299. total_role_ids = []
  300. permission_ids = []
  301. keys.each do |key|
  302. role_ids = []
  303. ::Permission.with_parents(key).each do |local_key|
  304. permission = ::Permission.lookup(name: local_key)
  305. next if !permission
  306. permission_ids.push permission.id
  307. end
  308. next if permission_ids.blank?
  309. Role.joins(:permissions_roles).joins(:permissions).where('permissions_roles.permission_id IN (?) AND roles.active = ? AND permissions.active = ?', permission_ids, true, true).distinct.pluck(:id).each do |role_id|
  310. role_ids.push role_id
  311. end
  312. total_role_ids.push role_ids
  313. end
  314. return [] if total_role_ids.blank?
  315. condition = ''
  316. total_role_ids.each do |_role_ids|
  317. if condition != ''
  318. condition += ' OR '
  319. end
  320. condition += 'roles_users.role_id IN (?)'
  321. end
  322. User.joins(:roles_users).where("(#{condition}) AND users.active = ?", *total_role_ids, true).distinct.order(:id)
  323. end
  324. =begin
  325. generate new token for reset password
  326. result = User.password_reset_new_token(username)
  327. returns
  328. result = {
  329. token: token,
  330. user: user,
  331. }
  332. =end
  333. def self.password_reset_new_token(username)
  334. return if username.blank?
  335. # try to find user based on login
  336. user = User.find_by(login: username.downcase.strip, active: true)
  337. # try second lookup with email
  338. user ||= User.find_by(email: username.downcase.strip, active: true)
  339. # check if email address exists
  340. return if !user
  341. return if !user.email
  342. # generate token
  343. token = Token.create(action: 'PasswordReset', user_id: user.id)
  344. {
  345. token: token,
  346. user: user,
  347. }
  348. end
  349. =begin
  350. returns the User instance for a given password token if found
  351. result = User.by_reset_token(token)
  352. returns
  353. result = user_model # user_model if token was verified
  354. =end
  355. def self.by_reset_token(token)
  356. Token.check(action: 'PasswordReset', name: token)
  357. end
  358. =begin
  359. reset password with token and set new password
  360. result = User.password_reset_via_token(token,password)
  361. returns
  362. result = user_model # user_model if token was verified
  363. =end
  364. def self.password_reset_via_token(token, password)
  365. # check token
  366. user = by_reset_token(token)
  367. return if !user
  368. # reset password
  369. user.update!(password: password, verified: true)
  370. # delete token
  371. Token.find_by(action: 'PasswordReset', name: token).destroy
  372. user
  373. end
  374. =begin
  375. update last login date and reset login_failed (is automatically done by auth and sso backend)
  376. user = User.find(123)
  377. result = user.update_last_login
  378. returns
  379. result = new_user_model
  380. =end
  381. def update_last_login
  382. # reduce DB/ES load by updating last_login every 10 minutes only
  383. if !last_login || last_login < 10.minutes.ago
  384. self.last_login = Time.zone.now
  385. end
  386. # reset login failed
  387. self.login_failed = 0
  388. save
  389. end
  390. =begin
  391. generate new token for signup
  392. result = User.signup_new_token(user) # or email
  393. returns
  394. result = {
  395. token: token,
  396. user: user,
  397. }
  398. =end
  399. def self.signup_new_token(user)
  400. return if !user
  401. return if !user.email
  402. # generate token
  403. token = Token.create(action: 'Signup', user_id: user.id)
  404. {
  405. token: token,
  406. user: user,
  407. }
  408. end
  409. =begin
  410. verify signup with token
  411. result = User.signup_verify_via_token(token, user)
  412. returns
  413. result = user_model # user_model if token was verified
  414. =end
  415. def self.signup_verify_via_token(token, user = nil)
  416. # check token
  417. local_user = Token.check(action: 'Signup', name: token)
  418. return if !local_user
  419. # if requested user is different to current user
  420. return if user && local_user.id != user.id
  421. # set verified
  422. local_user.update!(verified: true)
  423. # delete token
  424. Token.find_by(action: 'Signup', name: token).destroy
  425. local_user
  426. end
  427. =begin
  428. merge two users to one
  429. user = User.find(123)
  430. result = user.merge(user_id_of_duplicate_user)
  431. returns
  432. result = new_user_model
  433. =end
  434. def merge(user_id_of_duplicate_user)
  435. # Raise an exception if the user is not found (?)
  436. #
  437. # (This line used to contain a useless variable assignment,
  438. # and was changed to satisfy the linter.
  439. # We're not certain of its original intention,
  440. # so the User.find call has been kept
  441. # to prevent any unexpected regressions.)
  442. User.find(user_id_of_duplicate_user)
  443. # merge missing attributes
  444. Models.merge('User', id, user_id_of_duplicate_user)
  445. true
  446. end
  447. =begin
  448. list of active users in role
  449. result = User.of_role('Agent', group_ids)
  450. result = User.of_role(['Agent', 'Admin'])
  451. returns
  452. result = [user1, user2]
  453. =end
  454. def self.of_role(role, group_ids = nil)
  455. roles_ids = Role.where(active: true, name: role).map(&:id)
  456. if !group_ids
  457. return User.where(active: true).joins(:roles_users).where('roles_users.role_id' => roles_ids).order('users.updated_at DESC')
  458. end
  459. User.where(active: true)
  460. .joins(:roles_users)
  461. .joins(:users_groups)
  462. .where('roles_users.role_id IN (?) AND users_groups.group_ids IN (?)', roles_ids, group_ids).order('users.updated_at DESC')
  463. end
  464. =begin
  465. update/sync default preferences of users with dedicated permissions
  466. result = User.update_default_preferences_by_permission('ticket.agent', force)
  467. returns
  468. result = true # false
  469. =end
  470. def self.update_default_preferences_by_permission(permission_name, force = false)
  471. permission = ::Permission.lookup(name: permission_name)
  472. return if !permission
  473. default = Rails.configuration.preferences_default_by_permission
  474. return false if !default
  475. default.deep_stringify_keys!
  476. User.with_permissions(permission.name).each do |user|
  477. next if !default[permission.name]
  478. has_changed = false
  479. default[permission.name].each do |key, value|
  480. next if !force && user.preferences[key]
  481. has_changed = true
  482. user.preferences[key] = value
  483. end
  484. if has_changed
  485. user.save!
  486. end
  487. end
  488. true
  489. end
  490. =begin
  491. update/sync default preferences of users in a dedicated role
  492. result = User.update_default_preferences_by_role('Agent', force)
  493. returns
  494. result = true # false
  495. =end
  496. def self.update_default_preferences_by_role(role_name, force = false)
  497. role = Role.lookup(name: role_name)
  498. return if !role
  499. default = Rails.configuration.preferences_default_by_permission
  500. return false if !default
  501. default.deep_stringify_keys!
  502. role.permissions.each do |permission|
  503. User.update_default_preferences_by_permission(permission.name, force)
  504. end
  505. true
  506. end
  507. def check_notifications(other, should_save = true)
  508. default = Rails.configuration.preferences_default_by_permission
  509. return if !default
  510. default.deep_stringify_keys!
  511. has_changed = false
  512. other.permissions.each do |permission|
  513. next if !default[permission.name]
  514. default[permission.name].each do |key, value|
  515. next if preferences[key]
  516. preferences[key] = value
  517. has_changed = true
  518. end
  519. end
  520. return true if !has_changed
  521. if id && should_save
  522. save!
  523. return true
  524. end
  525. @preferences_default = preferences
  526. true
  527. end
  528. def check_preferences_default
  529. if @preferences_default.blank? && id
  530. roles.each do |role|
  531. check_notifications(role, false)
  532. end
  533. end
  534. return if @preferences_default.blank?
  535. preferences_tmp = @preferences_default.merge(preferences)
  536. self.preferences = preferences_tmp
  537. @preferences_default = nil
  538. true
  539. end
  540. def cache_delete
  541. super
  542. # delete asset caches
  543. key = "User::authorizations::#{id}"
  544. Cache.delete(key)
  545. # delete permission cache
  546. key = "User::permissions?:local_key:::#{id}"
  547. Cache.delete(key)
  548. end
  549. =begin
  550. try to find correct name
  551. [firstname, lastname] = User.name_guess('Some Name', 'some.name@example.com')
  552. =end
  553. def self.name_guess(string, email = nil)
  554. return if string.blank? && email.blank?
  555. string.strip!
  556. firstname = ''
  557. lastname = ''
  558. # "Lastname, Firstname"
  559. if string.match?(',')
  560. name = string.split(', ', 2)
  561. if name.count == 2
  562. if name[0].present?
  563. lastname = name[0].strip
  564. end
  565. if name[1].present?
  566. firstname = name[1].strip
  567. end
  568. return [firstname, lastname] if firstname.present? || lastname.present?
  569. end
  570. end
  571. # "Firstname Lastname"
  572. if string =~ %r{^(((Dr\.|Prof\.)[[:space:]]|).+?)[[:space:]](.+?)$}i
  573. if $1.present?
  574. firstname = $1.strip
  575. end
  576. if $4.present?
  577. lastname = $4.strip
  578. end
  579. return [firstname, lastname] if firstname.present? || lastname.present?
  580. end
  581. # -no name- "firstname.lastname@example.com"
  582. if string.blank? && email.present?
  583. scan = email.scan(%r{^(.+?)\.(.+?)@.+?$})
  584. if scan[0].present?
  585. if scan[0][0].present?
  586. firstname = scan[0][0].strip
  587. end
  588. if scan[0][1].present?
  589. lastname = scan[0][1].strip
  590. end
  591. return [firstname, lastname] if firstname.present? || lastname.present?
  592. end
  593. end
  594. nil
  595. end
  596. def no_name?
  597. firstname.blank? && lastname.blank?
  598. end
  599. # get locale identifier of user or system if user's own is not set
  600. def locale
  601. preferences.fetch(:locale) { Locale.default }
  602. end
  603. private
  604. def check_name
  605. if firstname.present?
  606. firstname.strip!
  607. end
  608. if lastname.present?
  609. lastname.strip!
  610. end
  611. return true if firstname.present? && lastname.present?
  612. if (firstname.blank? && lastname.present?) || (firstname.present? && lastname.blank?)
  613. used_name = firstname.presence || lastname
  614. (local_firstname, local_lastname) = User.name_guess(used_name, email)
  615. elsif firstname.blank? && lastname.blank? && email.present?
  616. (local_firstname, local_lastname) = User.name_guess('', email)
  617. end
  618. self.firstname = local_firstname if local_firstname.present?
  619. self.lastname = local_lastname if local_lastname.present?
  620. if firstname.present? && firstname.match(%r{^[A-z]+$}) && (firstname.downcase == firstname || firstname.upcase == firstname)
  621. firstname.capitalize!
  622. end
  623. if lastname.present? && lastname.match(%r{^[A-z]+$}) && (lastname.downcase == lastname || lastname.upcase == lastname)
  624. lastname.capitalize!
  625. end
  626. true
  627. end
  628. def check_email
  629. return true if Setting.get('import_mode')
  630. return true if email.blank?
  631. self.email = email.downcase.strip
  632. return true if id == 1
  633. email_address_validation = EmailAddressValidation.new(email)
  634. if !email_address_validation.valid_format?
  635. raise Exceptions::UnprocessableEntity, "Invalid email '#{email}'"
  636. end
  637. true
  638. end
  639. def check_login
  640. # use email as login if not given
  641. if login.blank?
  642. self.login = email
  643. end
  644. # if email has changed, login is old email, change also login
  645. if changes && changes['email'] && changes['email'][0] == login
  646. self.login = email
  647. end
  648. # generate auto login
  649. if login.blank?
  650. self.login = "auto-#{Time.zone.now.to_i}-#{rand(999_999)}"
  651. end
  652. # check if login already exists
  653. self.login = login.downcase.strip
  654. check = true
  655. while check
  656. exists = User.find_by(login: login)
  657. if exists && exists.id != id
  658. self.login = "#{login}#{rand(999)}"
  659. else
  660. check = false
  661. end
  662. end
  663. true
  664. end
  665. def check_mail_delivery_failed
  666. return if email_change.blank?
  667. preferences.delete(:mail_delivery_failed)
  668. end
  669. def ensure_roles
  670. return true if role_ids.present?
  671. self.role_ids = Role.signup_role_ids
  672. end
  673. def ensure_identifier
  674. return true if email.present? || firstname.present? || lastname.present? || phone.present?
  675. return true if login.present? && !login.start_with?('auto-')
  676. raise Exceptions::UnprocessableEntity, 'Minimum one identifier (login, firstname, lastname, phone or email) for user is required.'
  677. end
  678. def ensure_uniq_email
  679. return true if Setting.get('user_email_multiple_use')
  680. return true if Setting.get('import_mode')
  681. return true if email.blank?
  682. return true if !changes
  683. return true if !changes['email']
  684. return true if !User.exists?(email: email.downcase.strip)
  685. raise Exceptions::UnprocessableEntity, "Email address '#{email.downcase.strip}' is already used for other user."
  686. end
  687. def validate_roles(role)
  688. return true if !role_ids # we need role_ids for checking in role_ids below, in this method
  689. return true if role.preferences[:not].blank?
  690. role.preferences[:not].each do |local_role_name|
  691. local_role = Role.lookup(name: local_role_name)
  692. next if !local_role
  693. next if role_ids.exclude?(local_role.id)
  694. raise "Role #{role.name} conflicts with #{local_role.name}"
  695. end
  696. true
  697. end
  698. def validate_ooo
  699. return true if out_of_office != true
  700. raise Exceptions::UnprocessableEntity, 'out of office start is required' if out_of_office_start_at.blank?
  701. raise Exceptions::UnprocessableEntity, 'out of office end is required' if out_of_office_end_at.blank?
  702. raise Exceptions::UnprocessableEntity, 'out of office end is before start' if out_of_office_start_at > out_of_office_end_at
  703. raise Exceptions::UnprocessableEntity, 'out of office replacement user is required' if out_of_office_replacement_id.blank?
  704. raise Exceptions::UnprocessableEntity, 'out of office no such replacement user' if !User.exists?(id: out_of_office_replacement_id)
  705. true
  706. end
  707. def validate_preferences
  708. return true if !changes
  709. return true if !changes['preferences']
  710. return true if preferences.blank?
  711. return true if !preferences[:notification_sound]
  712. return true if !preferences[:notification_sound][:enabled]
  713. case preferences[:notification_sound][:enabled]
  714. when 'true'
  715. preferences[:notification_sound][:enabled] = true
  716. when 'false'
  717. preferences[:notification_sound][:enabled] = false
  718. end
  719. class_name = preferences[:notification_sound][:enabled].class.to_s
  720. raise Exceptions::UnprocessableEntity, "preferences.notification_sound.enabled need to be an boolean, but it was a #{class_name}" if class_name != 'TrueClass' && class_name != 'FalseClass'
  721. true
  722. end
  723. =begin
  724. checks if the current user is the last one with admin permissions.
  725. Raises
  726. raise 'Minimum one user need to have admin permissions'
  727. =end
  728. def last_admin_check_by_attribute
  729. return true if !will_save_change_to_attribute?('active')
  730. return true if active != false
  731. return true if !permissions?(['admin', 'admin.user'])
  732. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  733. true
  734. end
  735. def last_admin_check_by_role(role)
  736. return true if Setting.get('import_mode')
  737. return true if !role.with_permission?(['admin', 'admin.user'])
  738. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  739. true
  740. end
  741. def last_admin_check_admin_count
  742. admin_role_ids = Role.joins(:permissions).where(permissions: { name: ['admin', 'admin.user'], active: true }, roles: { active: true }).pluck(:id)
  743. User.joins(:roles).where(roles: { id: admin_role_ids }, users: { active: true }).distinct.count - 1
  744. end
  745. def validate_agent_limit_by_attributes
  746. return true if Setting.get('system_agent_limit').blank?
  747. return true if !will_save_change_to_attribute?('active')
  748. return true if active != true
  749. return true if !permissions?('ticket.agent')
  750. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  751. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct.count + 1
  752. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  753. true
  754. end
  755. def validate_agent_limit_by_role(role)
  756. return true if Setting.get('system_agent_limit').blank?
  757. return true if active != true
  758. return true if role.active != true
  759. return true if !role.with_permission?('ticket.agent')
  760. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  761. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct.count
  762. # if new added role is a ticket.agent role
  763. if ticket_agent_role_ids.include?(role.id)
  764. # if user already has a ticket.agent role
  765. hint = false
  766. role_ids.each do |locale_role_id|
  767. next if ticket_agent_role_ids.exclude?(locale_role_id)
  768. hint = true
  769. break
  770. end
  771. # user has not already a ticket.agent role
  772. if hint == false
  773. count += 1
  774. end
  775. end
  776. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  777. true
  778. end
  779. def domain_based_assignment
  780. return true if !email
  781. return true if organization_id
  782. begin
  783. domain = Mail::Address.new(email).domain
  784. return true if !domain
  785. organization = Organization.find_by(domain: domain.downcase, domain_assignment: true)
  786. return true if !organization
  787. self.organization_id = organization.id
  788. rescue
  789. return true
  790. end
  791. true
  792. end
  793. # sets locale of the user
  794. def set_locale
  795. # set the user's locale to the one of the "executing" user
  796. return true if !UserInfo.current_user_id
  797. user = User.find_by(id: UserInfo.current_user_id)
  798. return true if !user
  799. return true if !user.preferences[:locale]
  800. preferences[:locale] = user.preferences[:locale]
  801. true
  802. end
  803. def destroy_longer_required_objects
  804. ::Avatar.remove(self.class.to_s, id)
  805. ::UserDevice.remove(id)
  806. ::StatsStore.where(stats_storable: self).destroy_all
  807. end
  808. def destroy_move_dependency_ownership
  809. result = Models.references(self.class.to_s, id)
  810. user_columns = %w[created_by_id updated_by_id out_of_office_replacement_id origin_by_id owner_id archived_by_id published_by_id internal_by_id]
  811. result.each do |class_name, references|
  812. next if class_name.blank?
  813. next if references.blank?
  814. ref_class = class_name.constantize
  815. ref_update_columns = []
  816. references.each do |column, reference_found|
  817. next if !reference_found
  818. if user_columns.include?(column)
  819. ref_update_columns << column
  820. elsif ref_class.exists?(column => id)
  821. raise "Failed deleting references! Check logic for #{class_name}->#{column}."
  822. end
  823. end
  824. next if ref_update_columns.blank?
  825. where_sql = ref_update_columns.map { |column| "#{column} = #{id}" }.join(' OR ')
  826. ref_class.where(where_sql).find_in_batches(batch_size: 1000) do |batch_list|
  827. batch_list.each do |record|
  828. ref_update_columns.each do |column|
  829. next if record[column] != id
  830. record[column] = 1
  831. end
  832. record.save!
  833. rescue => e
  834. Rails.logger.error e
  835. end
  836. end
  837. end
  838. true
  839. end
  840. def ensure_password
  841. self.password = ensured_password
  842. true
  843. end
  844. def ensured_password
  845. # ensure unset password for blank values of new users
  846. return nil if new_record? && password.blank?
  847. # don't permit empty password update for existing users
  848. return password_was if password.blank?
  849. # don't re-hash passwords
  850. return password if PasswordHash.crypted?(password)
  851. # hash the plaintext password
  852. PasswordHash.crypt(password)
  853. end
  854. # reset login_failed if password is changed
  855. def reset_login_failed_after_password_change
  856. return true if !will_save_change_to_attribute?('password')
  857. self.login_failed = 0
  858. true
  859. end
  860. # When adding/removing a phone number from the User table,
  861. # update caller ID table
  862. # to adopt/orphan matching Cti::Logs accordingly
  863. # (see https://github.com/zammad/zammad/issues/2057)
  864. def update_caller_id
  865. # skip if "phone" does not change, or changes like [nil, ""]
  866. return if persisted? && !previous_changes[:phone]&.any?(&:present?)
  867. return if destroyed? && phone.blank?
  868. Cti::CallerId.build(self)
  869. end
  870. end