user.rb 34 KB

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