user.rb 31 KB

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