user.rb 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class User < ApplicationModel
  3. include HasActivityStreamLog
  4. include ChecksClientNotification
  5. include HasHistory
  6. include HasSearchIndexBackend
  7. include CanCsvImport
  8. include HasGroups
  9. include HasRoles
  10. include User::ChecksAccess
  11. include User::Assets
  12. include User::Search
  13. include User::SearchIndex
  14. has_and_belongs_to_many :roles, after_add: %i[cache_update check_notifications], after_remove: :cache_update, before_add: %i[validate_agent_limit_by_role validate_roles], before_remove: :last_admin_check_by_role, class_name: 'Role'
  15. has_and_belongs_to_many :organizations, after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
  16. has_many :tokens, after_add: :cache_update, after_remove: :cache_update
  17. has_many :authorizations, after_add: :cache_update, after_remove: :cache_update
  18. belongs_to :organization, inverse_of: :members
  19. before_validation :check_name, :check_email, :check_login, :ensure_uniq_email, :ensure_password, :ensure_roles, :ensure_identifier
  20. before_validation :check_mail_delivery_failed, on: :update
  21. before_create :check_preferences_default, :validate_preferences, :validate_ooo, :domain_based_assignment, :set_locale
  22. before_update :check_preferences_default, :validate_preferences, :validate_ooo, :reset_login_failed, :validate_agent_limit_by_attributes, :last_admin_check_by_attribute
  23. after_create :avatar_for_email_check
  24. after_update :avatar_for_email_check
  25. after_commit :update_caller_id
  26. before_destroy :destroy_longer_required_objects
  27. skip_callback :create, :after, :avatar_for_email_check, if: -> { BulkImportInfo.enabled? }
  28. skip_callback :update, :after, :avatar_for_email_check, if: -> { BulkImportInfo.enabled? }
  29. store :preferences
  30. activity_stream_permission 'admin.user'
  31. activity_stream_attributes_ignored :last_login,
  32. :login_failed,
  33. :image,
  34. :image_source,
  35. :preferences
  36. history_attributes_ignored :password,
  37. :last_login,
  38. :image,
  39. :image_source,
  40. :preferences
  41. search_index_attributes_ignored :password,
  42. :image,
  43. :image_source,
  44. :source,
  45. :login_failed
  46. csv_object_ids_ignored 1
  47. csv_attributes_ignored :password,
  48. :login_failed,
  49. :source,
  50. :image_source,
  51. :image,
  52. :authorizations,
  53. :organizations,
  54. :groups,
  55. :user_groups
  56. def ignore_search_indexing?(_action)
  57. # ignore internal user
  58. return true if id == 1
  59. false
  60. end
  61. =begin
  62. fullname of user
  63. user = User.find(123)
  64. result = user.fullname
  65. returns
  66. result = "Bob Smith"
  67. =end
  68. def fullname
  69. name = ''
  70. if firstname.present?
  71. name = firstname
  72. end
  73. if lastname.present?
  74. if name != ''
  75. name += ' '
  76. end
  77. name += lastname
  78. end
  79. if name.blank? && email.present?
  80. name = email
  81. end
  82. name
  83. end
  84. =begin
  85. longname of user
  86. user = User.find(123)
  87. result = user.longname
  88. returns
  89. result = "Bob Smith"
  90. or with org
  91. result = "Bob Smith (Org ABC)"
  92. =end
  93. def longname
  94. name = fullname
  95. if organization_id
  96. organization = Organization.lookup(id: organization_id)
  97. if organization
  98. name += " (#{organization.name})"
  99. end
  100. end
  101. name
  102. end
  103. =begin
  104. check if user is in role
  105. user = User.find(123)
  106. result = user.role?('Customer')
  107. result = user.role?(['Agent', 'Admin'])
  108. returns
  109. result = true|false
  110. =end
  111. def role?(role_name)
  112. roles.where(name: role_name).any?
  113. end
  114. =begin
  115. check if user is in role
  116. user = User.find(123)
  117. result = user.out_of_office?
  118. returns
  119. result = true|false
  120. =end
  121. def out_of_office?
  122. return false if out_of_office != true
  123. return false if out_of_office_start_at.blank?
  124. return false if out_of_office_end_at.blank?
  125. Time.zone.today.between?(out_of_office_start_at, out_of_office_end_at)
  126. end
  127. =begin
  128. check if user is in role
  129. user = User.find(123)
  130. result = user.out_of_office_agent
  131. returns
  132. result = user_model
  133. =end
  134. def out_of_office_agent
  135. return if !out_of_office?
  136. return if out_of_office_replacement_id.blank?
  137. User.find_by(id: out_of_office_replacement_id)
  138. end
  139. =begin
  140. gets users where user is replacement
  141. user = User.find(123)
  142. result = user.out_of_office_agent_of
  143. returns
  144. result = [user_model1, user_model2]
  145. =end
  146. def out_of_office_agent_of
  147. 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)
  148. end
  149. =begin
  150. get users activity stream
  151. user = User.find(123)
  152. result = user.activity_stream(20)
  153. returns
  154. result = [
  155. {
  156. id: 2,
  157. o_id: 2,
  158. created_by_id: 3,
  159. created_at: '2013-09-28 00:57:21',
  160. object: "User",
  161. type: "created",
  162. },
  163. {
  164. id: 2,
  165. o_id: 2,
  166. created_by_id: 3,
  167. created_at: '2013-09-28 00:59:21',
  168. object: "User",
  169. type: "updated",
  170. },
  171. ]
  172. =end
  173. def activity_stream(limit, fulldata = false)
  174. stream = ActivityStream.list(self, limit)
  175. return stream if !fulldata
  176. # get related objects
  177. assets = {}
  178. stream.each do |item|
  179. assets = item.assets(assets)
  180. end
  181. {
  182. stream: stream,
  183. assets: assets,
  184. }
  185. end
  186. =begin
  187. authenticate user
  188. result = User.authenticate(username, password)
  189. returns
  190. result = user_model # user model if authentication was successfully
  191. =end
  192. def self.authenticate(username, password)
  193. # do not authenticate with nothing
  194. return if username.blank? || password.blank?
  195. user = User.identify(username)
  196. return if !user
  197. return if !Auth.can_login?(user)
  198. return user if Auth.valid?(user, password)
  199. sleep 1
  200. user.login_failed += 1
  201. user.save!
  202. nil
  203. end
  204. =begin
  205. checks if a user has reached the maximum of failed login tries
  206. user = User.find(123)
  207. result = user.max_login_failed?
  208. returns
  209. result = true | false
  210. =end
  211. def max_login_failed?
  212. max_login_failed = Setting.get('password_max_login_failed').to_i || 10
  213. login_failed > max_login_failed
  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. # try to find user based on login
  226. user = User.find_by(login: identifier.downcase)
  227. return user if user
  228. # try second lookup with email
  229. User.find_by(email: identifier.downcase)
  230. end
  231. =begin
  232. authenticate user agains sso
  233. result = User.sso(sso_params)
  234. returns
  235. result = user_model # user model if authentication was successfully
  236. =end
  237. def self.sso(params)
  238. # try to login against configure auth backends
  239. user_auth = Sso.check(params)
  240. return if !user_auth
  241. user_auth
  242. end
  243. =begin
  244. create user from from omni auth hash
  245. result = User.create_from_hash!(hash)
  246. returns
  247. result = user_model # user model if create was successfully
  248. =end
  249. def self.create_from_hash!(hash)
  250. url = ''
  251. hash['info']['urls']&.each_value do |local_url|
  252. next if local_url.blank?
  253. url = local_url
  254. end
  255. begin
  256. data = {
  257. login: hash['info']['nickname'] || hash['uid'],
  258. firstname: hash['info']['name'] || hash['info']['display_name'],
  259. email: hash['info']['email'],
  260. image_source: hash['info']['image'],
  261. web: url,
  262. address: hash['info']['location'],
  263. note: hash['info']['description'],
  264. source: hash['provider'],
  265. role_ids: Role.signup_role_ids,
  266. updated_by_id: 1,
  267. created_by_id: 1,
  268. }
  269. if hash['info']['first_name'].present? && hash['info']['last_name'].present?
  270. data[:firstname] = hash['info']['first_name']
  271. data[:lastname] = hash['info']['last_name']
  272. end
  273. create!(data)
  274. rescue => e
  275. logger.error e
  276. raise Exceptions::UnprocessableEntity, e.message
  277. end
  278. end
  279. =begin
  280. get all permissions of user
  281. user = User.find(123)
  282. user.permissions
  283. returns
  284. {
  285. 'permission.key' => true,
  286. # ...
  287. }
  288. =end
  289. def permissions
  290. list = {}
  291. ::Permission.select('permissions.name, permissions.preferences').joins(:roles).where('roles.id IN (?) AND permissions.active = ?', role_ids, true).pluck(:name, :preferences).each do |permission|
  292. next if permission[1]['selectable'] == false
  293. list[permission[0]] = true
  294. end
  295. list
  296. end
  297. =begin
  298. true or false for permission
  299. user = User.find(123)
  300. user.permissions?('permission.key') # access to certain permission.key
  301. user.permissions?(['permission.key1', 'permission.key2']) # access to permission.key1 or permission.key2
  302. user.permissions?('permission') # access to all sub keys
  303. user.permissions?('permission.*') # access if one sub key access exists
  304. returns
  305. true|false
  306. =end
  307. def permissions?(key)
  308. keys = key
  309. names = []
  310. if key.class == String
  311. keys = [key]
  312. end
  313. keys.each do |local_key|
  314. list = []
  315. if local_key.match?(/\.\*$/)
  316. local_key.sub!('.*', '.%')
  317. permissions = ::Permission.with_parents(local_key)
  318. list = ::Permission.select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND (permissions.name IN (?) OR permissions.name LIKE ?) AND permissions.active = ?', role_ids, true, permissions, local_key, true).pluck(:preferences)
  319. else
  320. permission = ::Permission.lookup(name: local_key)
  321. break if permission&.active == false
  322. permissions = ::Permission.with_parents(local_key)
  323. list = ::Permission.select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND permissions.name IN (?) AND permissions.active = ?', role_ids, true, permissions, true).pluck(:preferences)
  324. end
  325. return true if list.present?
  326. end
  327. false
  328. end
  329. =begin
  330. returns all accessable permission ids of user
  331. user = User.find(123)
  332. user.permissions_with_child_ids
  333. returns
  334. [permission1_id, permission2_id, permission3_id]
  335. =end
  336. def permissions_with_child_ids
  337. where = ''
  338. where_bind = [true]
  339. permissions.each_key do |permission_name|
  340. where += ' OR ' if where != ''
  341. where += 'permissions.name = ? OR permissions.name LIKE ?'
  342. where_bind.push permission_name
  343. where_bind.push "#{permission_name}.%"
  344. end
  345. return [] if where == ''
  346. ::Permission.where("permissions.active = ? AND (#{where})", *where_bind).pluck(:id)
  347. end
  348. =begin
  349. get all users with permission
  350. users = User.with_permissions('ticket.agent')
  351. get all users with permission "admin.session" or "ticket.agent"
  352. users = User.with_permissions(['admin.session', 'ticket.agent'])
  353. returns
  354. [user1, user2, ...]
  355. =end
  356. def self.with_permissions(keys)
  357. if keys.class != Array
  358. keys = [keys]
  359. end
  360. total_role_ids = []
  361. permission_ids = []
  362. keys.each do |key|
  363. role_ids = []
  364. ::Permission.with_parents(key).each do |local_key|
  365. permission = ::Permission.lookup(name: local_key)
  366. next if !permission
  367. permission_ids.push permission.id
  368. end
  369. next if permission_ids.blank?
  370. Role.joins(:roles_permissions).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|
  371. role_ids.push role_id
  372. end
  373. total_role_ids.push role_ids
  374. end
  375. return [] if total_role_ids.blank?
  376. condition = ''
  377. total_role_ids.each do |_role_ids|
  378. if condition != ''
  379. condition += ' OR '
  380. end
  381. condition += 'roles_users.role_id IN (?)'
  382. end
  383. User.joins(:users_roles).where("(#{condition}) AND users.active = ?", *total_role_ids, true).distinct.order(:id)
  384. end
  385. =begin
  386. generate new token for reset password
  387. result = User.password_reset_new_token(username)
  388. returns
  389. result = {
  390. token: token,
  391. user: user,
  392. }
  393. =end
  394. def self.password_reset_new_token(username)
  395. return if username.blank?
  396. # try to find user based on login
  397. user = User.find_by(login: username.downcase.strip, active: true)
  398. # try second lookup with email
  399. user ||= User.find_by(email: username.downcase.strip, active: true)
  400. # check if email address exists
  401. return if !user
  402. return if !user.email
  403. # generate token
  404. token = Token.create(action: 'PasswordReset', user_id: user.id)
  405. {
  406. token: token,
  407. user: user,
  408. }
  409. end
  410. =begin
  411. returns the User instance for a given password token if found
  412. result = User.by_reset_token(token)
  413. returns
  414. result = user_model # user_model if token was verified
  415. =end
  416. def self.by_reset_token(token)
  417. Token.check(action: 'PasswordReset', name: token)
  418. end
  419. =begin
  420. reset password with token and set new password
  421. result = User.password_reset_via_token(token,password)
  422. returns
  423. result = user_model # user_model if token was verified
  424. =end
  425. def self.password_reset_via_token(token, password)
  426. # check token
  427. user = by_reset_token(token)
  428. return if !user
  429. # reset password
  430. user.update!(password: password)
  431. # delete token
  432. Token.find_by(action: 'PasswordReset', name: token).destroy
  433. user
  434. end
  435. =begin
  436. update last login date and reset login_failed (is automatically done by auth and sso backend)
  437. user = User.find(123)
  438. result = user.update_last_login
  439. returns
  440. result = new_user_model
  441. =end
  442. def update_last_login
  443. self.last_login = Time.zone.now
  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. # find email addresses and move them to primary user
  494. duplicate_user = User.find(user_id_of_duplicate_user)
  495. # merge missing attibutes
  496. Models.merge('User', id, user_id_of_duplicate_user)
  497. true
  498. end
  499. =begin
  500. list of active users in role
  501. result = User.of_role('Agent', group_ids)
  502. result = User.of_role(['Agent', 'Admin'])
  503. returns
  504. result = [user1, user2]
  505. =end
  506. def self.of_role(role, group_ids = nil)
  507. roles_ids = Role.where(active: true, name: role).map(&:id)
  508. if !group_ids
  509. return User.where(active: true).joins(:users_roles).where('roles_users.role_id IN (?)', roles_ids).order('users.updated_at DESC')
  510. end
  511. User.where(active: true)
  512. .joins(:users_roles)
  513. .joins(:users_groups)
  514. .where('roles_users.role_id IN (?) AND users_groups.group_ids IN (?)', roles_ids, group_ids).order('users.updated_at DESC')
  515. end
  516. =begin
  517. update/sync default preferences of users in a dedecated permissions
  518. result = User.update_default_preferences_by_permission('ticket.agent', force)
  519. returns
  520. result = true # false
  521. =end
  522. def self.update_default_preferences_by_permission(permission_name, force = false)
  523. permission = ::Permission.lookup(name: permission_name)
  524. return if !permission
  525. default = Rails.configuration.preferences_default_by_permission
  526. return false if !default
  527. default.deep_stringify_keys!
  528. User.with_permissions(permission.name).each do |user|
  529. next if !default[permission.name]
  530. has_changed = false
  531. default[permission.name].each do |key, value|
  532. next if !force && user.preferences[key]
  533. has_changed = true
  534. user.preferences[key] = value
  535. end
  536. if has_changed
  537. user.save!
  538. end
  539. end
  540. true
  541. end
  542. =begin
  543. update/sync default preferences of users in a dedecated role
  544. result = User.update_default_preferences_by_role('Agent', force)
  545. returns
  546. result = true # false
  547. =end
  548. def self.update_default_preferences_by_role(role_name, force = false)
  549. role = Role.lookup(name: role_name)
  550. return if !role
  551. default = Rails.configuration.preferences_default_by_permission
  552. return false if !default
  553. default.deep_stringify_keys!
  554. role.permissions.each do |permission|
  555. User.update_default_preferences_by_permission(permission.name, force)
  556. end
  557. true
  558. end
  559. def check_notifications(other, should_save = true)
  560. default = Rails.configuration.preferences_default_by_permission
  561. return if !default
  562. default.deep_stringify_keys!
  563. has_changed = false
  564. other.permissions.each do |permission|
  565. next if !default[permission.name]
  566. default[permission.name].each do |key, value|
  567. next if preferences[key]
  568. preferences[key] = value
  569. has_changed = true
  570. end
  571. end
  572. return true if !has_changed
  573. if id && should_save
  574. save!
  575. return true
  576. end
  577. @preferences_default = preferences
  578. true
  579. end
  580. def check_preferences_default
  581. if @preferences_default.blank?
  582. if id
  583. roles.each do |role|
  584. check_notifications(role, false)
  585. end
  586. end
  587. end
  588. return if @preferences_default.blank?
  589. preferences_tmp = @preferences_default.merge(preferences)
  590. self.preferences = preferences_tmp
  591. @preferences_default = nil
  592. true
  593. end
  594. def cache_delete
  595. super
  596. # delete asset caches
  597. key = "User::authorizations::#{id}"
  598. Cache.delete(key)
  599. # delete permission cache
  600. key = "User::permissions?:local_key:::#{id}"
  601. Cache.delete(key)
  602. end
  603. =begin
  604. try to find correct name
  605. [firstname, lastname] = User.name_guess('Some Name', 'some.name@example.com')
  606. =end
  607. def self.name_guess(string, email = nil)
  608. return if string.blank? && email.blank?
  609. string.strip!
  610. firstname = ''
  611. lastname = ''
  612. # "Lastname, Firstname"
  613. if string.match?(',')
  614. name = string.split(', ', 2)
  615. if name.count == 2
  616. if name[0].present?
  617. lastname = name[0].strip
  618. end
  619. if name[1].present?
  620. firstname = name[1].strip
  621. end
  622. return [firstname, lastname] if firstname.present? || lastname.present?
  623. end
  624. end
  625. # "Firstname Lastname"
  626. if string =~ /^(((Dr\.|Prof\.)[[:space:]]|).+?)[[:space:]](.+?)$/i
  627. if $1.present?
  628. firstname = $1.strip
  629. end
  630. if $4.present?
  631. lastname = $4.strip
  632. end
  633. return [firstname, lastname] if firstname.present? || lastname.present?
  634. end
  635. # -no name- "firstname.lastname@example.com"
  636. if string.blank? && email.present?
  637. scan = email.scan(/^(.+?)\.(.+?)\@.+?$/)
  638. if scan[0].present?
  639. if scan[0][0].present?
  640. firstname = scan[0][0].strip
  641. end
  642. if scan[0][1].present?
  643. lastname = scan[0][1].strip
  644. end
  645. return [firstname, lastname] if firstname.present? || lastname.present?
  646. end
  647. end
  648. nil
  649. end
  650. def no_name?
  651. firstname.blank? && lastname.blank?
  652. end
  653. private
  654. def check_name
  655. if firstname.present?
  656. firstname.strip!
  657. end
  658. if lastname.present?
  659. lastname.strip!
  660. end
  661. return true if firstname.present? && lastname.present?
  662. if (firstname.blank? && lastname.present?) || (firstname.present? && lastname.blank?)
  663. used_name = firstname.presence || lastname
  664. (local_firstname, local_lastname) = User.name_guess(used_name, email)
  665. elsif firstname.blank? && lastname.blank? && email.present?
  666. (local_firstname, local_lastname) = User.name_guess('', email)
  667. end
  668. self.firstname = local_firstname if local_firstname.present?
  669. self.lastname = local_lastname if local_lastname.present?
  670. if firstname.present? && firstname.match(/^[A-z]+$/) && (firstname.downcase == firstname || firstname.upcase == firstname)
  671. firstname.capitalize!
  672. end
  673. if lastname.present? && lastname.match(/^[A-z]+$/) && (lastname.downcase == lastname || lastname.upcase == lastname)
  674. lastname.capitalize!
  675. end
  676. true
  677. end
  678. def check_email
  679. return true if Setting.get('import_mode')
  680. return true if email.blank?
  681. self.email = email.downcase.strip
  682. return true if id == 1
  683. raise Exceptions::UnprocessableEntity, 'Invalid email' if email !~ /@/
  684. raise Exceptions::UnprocessableEntity, 'Invalid email' if email.match?(/\s/)
  685. true
  686. end
  687. def check_login
  688. # use email as login if not given
  689. if login.blank?
  690. self.login = email
  691. end
  692. # if email has changed, login is old email, change also login
  693. if changes && changes['email']
  694. if changes['email'][0] == login
  695. self.login = email
  696. end
  697. end
  698. # generate auto login
  699. if login.blank?
  700. self.login = "auto-#{Time.zone.now.to_i}-#{rand(999_999)}"
  701. end
  702. # check if login already exists
  703. self.login = login.downcase.strip
  704. check = true
  705. while check
  706. exists = User.find_by(login: login)
  707. if exists && exists.id != id
  708. self.login = "#{login}#{rand(999)}"
  709. else
  710. check = false
  711. end
  712. end
  713. true
  714. end
  715. def check_mail_delivery_failed
  716. return if email_change.blank?
  717. preferences.delete(:mail_delivery_failed)
  718. end
  719. def ensure_roles
  720. return true if role_ids.present?
  721. self.role_ids = Role.signup_role_ids
  722. end
  723. def ensure_identifier
  724. return true if email.present? || firstname.present? || lastname.present? || phone.present?
  725. return true if login.present? && !login.start_with?('auto-')
  726. raise Exceptions::UnprocessableEntity, 'Minimum one identifier (login, firstname, lastname, phone or email) for user is required.'
  727. end
  728. def ensure_uniq_email
  729. return true if Setting.get('user_email_multiple_use')
  730. return true if Setting.get('import_mode')
  731. return true if email.blank?
  732. return true if !changes
  733. return true if !changes['email']
  734. return true if !User.find_by(email: email.downcase.strip)
  735. raise Exceptions::UnprocessableEntity, 'Email address is already used for other user.'
  736. end
  737. def validate_roles(role)
  738. return true if !role_ids # we need role_ids for checking in role_ids below, in this method
  739. return true if role.preferences[:not].blank?
  740. role.preferences[:not].each do |local_role_name|
  741. local_role = Role.lookup(name: local_role_name)
  742. next if !local_role
  743. next if role_ids.exclude?(local_role.id)
  744. raise "Role #{role.name} conflicts with #{local_role.name}"
  745. end
  746. true
  747. end
  748. def validate_ooo
  749. return true if out_of_office != true
  750. raise Exceptions::UnprocessableEntity, 'out of office start is required' if out_of_office_start_at.blank?
  751. raise Exceptions::UnprocessableEntity, 'out of office end is required' if out_of_office_end_at.blank?
  752. raise Exceptions::UnprocessableEntity, 'out of office end is before start' if out_of_office_start_at > out_of_office_end_at
  753. raise Exceptions::UnprocessableEntity, 'out of office replacement user is required' if out_of_office_replacement_id.blank?
  754. raise Exceptions::UnprocessableEntity, 'out of office no such replacement user' if !User.find_by(id: out_of_office_replacement_id)
  755. true
  756. end
  757. def validate_preferences
  758. return true if !changes
  759. return true if !changes['preferences']
  760. return true if preferences.blank?
  761. return true if !preferences[:notification_sound]
  762. return true if !preferences[:notification_sound][:enabled]
  763. if preferences[:notification_sound][:enabled] == 'true'
  764. preferences[:notification_sound][:enabled] = true
  765. elsif preferences[:notification_sound][:enabled] == 'false'
  766. preferences[:notification_sound][:enabled] = false
  767. end
  768. class_name = preferences[:notification_sound][:enabled].class.to_s
  769. 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'
  770. true
  771. end
  772. =begin
  773. checks if the current user is the last one with admin permissions.
  774. Raises
  775. raise 'Minimum one user need to have admin permissions'
  776. =end
  777. def last_admin_check_by_attribute
  778. return true if !will_save_change_to_attribute?('active')
  779. return true if active != false
  780. return true if !permissions?(['admin', 'admin.user'])
  781. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  782. true
  783. end
  784. def last_admin_check_by_role(role)
  785. return true if Setting.get('import_mode')
  786. return true if !role.with_permission?(['admin', 'admin.user'])
  787. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  788. true
  789. end
  790. def last_admin_check_admin_count
  791. admin_role_ids = Role.joins(:permissions).where(permissions: { name: ['admin', 'admin.user'], active: true }, roles: { active: true }).pluck(:id)
  792. User.joins(:roles).where(roles: { id: admin_role_ids }, users: { active: true }).distinct().count - 1
  793. end
  794. def validate_agent_limit_by_attributes
  795. return true if Setting.get('system_agent_limit').blank?
  796. return true if !will_save_change_to_attribute?('active')
  797. return true if active != true
  798. return true if !permissions?('ticket.agent')
  799. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  800. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct().count + 1
  801. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  802. true
  803. end
  804. def validate_agent_limit_by_role(role)
  805. return true if Setting.get('system_agent_limit').blank?
  806. return true if active != true
  807. return true if role.active != true
  808. return true if !role.with_permission?('ticket.agent')
  809. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  810. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct().count
  811. # if new added role is a ticket.agent role
  812. if ticket_agent_role_ids.include?(role.id)
  813. # if user already has a ticket.agent role
  814. hint = false
  815. role_ids.each do |locale_role_id|
  816. next if !ticket_agent_role_ids.include?(locale_role_id)
  817. hint = true
  818. break
  819. end
  820. # user has not already a ticket.agent role
  821. if hint == false
  822. count += 1
  823. end
  824. end
  825. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  826. true
  827. end
  828. def domain_based_assignment
  829. return true if !email
  830. return true if organization_id
  831. begin
  832. domain = Mail::Address.new(email).domain
  833. return true if !domain
  834. organization = Organization.find_by(domain: domain.downcase, domain_assignment: true)
  835. return true if !organization
  836. self.organization_id = organization.id
  837. rescue
  838. return true
  839. end
  840. true
  841. end
  842. # sets locale of the user
  843. def set_locale
  844. # set the user's locale to the one of the "executing" user
  845. return true if !UserInfo.current_user_id
  846. user = User.find_by(id: UserInfo.current_user_id)
  847. return true if !user
  848. return true if !user.preferences[:locale]
  849. preferences[:locale] = user.preferences[:locale]
  850. true
  851. end
  852. def avatar_for_email_check
  853. return true if Setting.get('import_mode')
  854. return true if email.blank?
  855. return true if email !~ /@/
  856. return true if !saved_change_to_attribute?('email') && updated_at > Time.zone.now - 10.days
  857. # save/update avatar
  858. avatar = Avatar.auto_detection(
  859. object: 'User',
  860. o_id: id,
  861. url: email,
  862. source: 'app',
  863. updated_by_id: updated_by_id,
  864. created_by_id: updated_by_id,
  865. )
  866. # update user link
  867. return true if !avatar
  868. update_column(:image, avatar.store_hash) # rubocop:disable Rails/SkipsModelValidations
  869. cache_delete
  870. true
  871. end
  872. def destroy_longer_required_objects
  873. ::Authorization.where(user_id: id).destroy_all
  874. ::Avatar.remove('User', id)
  875. ::Cti::CallerId.where(user_id: id).destroy_all
  876. ::Taskbar.where(user_id: id).destroy_all
  877. ::Karma::ActivityLog.where(user_id: id).destroy_all
  878. ::Karma::User.where(user_id: id).destroy_all
  879. ::OnlineNotification.where(user_id: id).destroy_all
  880. ::RecentView.where(created_by_id: id).destroy_all
  881. ::UserDevice.remove(id)
  882. ::Token.where(user_id: id).destroy_all
  883. ::StatsStore.remove(
  884. object: 'User',
  885. o_id: id,
  886. )
  887. true
  888. end
  889. def ensure_password
  890. return true if password_empty?
  891. return true if PasswordHash.crypted?(password)
  892. self.password = PasswordHash.crypt(password)
  893. true
  894. end
  895. def password_empty?
  896. # set old password again if not given
  897. return if password.present?
  898. # skip if it's not desired to set a password (yet)
  899. return true if !password
  900. # get current record
  901. return if !id
  902. self.password = password_was
  903. true
  904. end
  905. # reset login_failed if password is changed
  906. def reset_login_failed
  907. return true if !will_save_change_to_attribute?('password')
  908. self.login_failed = 0
  909. true
  910. end
  911. # When adding/removing a phone number from the User table,
  912. # update caller ID table
  913. # to adopt/orphan matching Cti::Logs accordingly
  914. # (see https://github.com/zammad/zammad/issues/2057)
  915. def update_caller_id
  916. # skip if "phone" does not change, or changes like [nil, ""]
  917. return if persisted? && !previous_changes[:phone]&.any?(&:present?)
  918. return if destroyed? && phone.blank?
  919. Cti::CallerId.build(self)
  920. end
  921. end