user.rb 34 KB

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