user.rb 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  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. authenticate user again sso
  236. result = User.sso(sso_params)
  237. returns
  238. result = user_model # user model if authentication was successfully
  239. =end
  240. def self.sso(params)
  241. # try to login against configure auth backends
  242. user_auth = Sso.check(params)
  243. return if !user_auth
  244. user_auth
  245. end
  246. =begin
  247. create user from from omni auth hash
  248. result = User.create_from_hash!(hash)
  249. returns
  250. result = user_model # user model if create was successfully
  251. =end
  252. def self.create_from_hash!(hash)
  253. url = ''
  254. hash['info']['urls']&.each_value do |local_url|
  255. next if local_url.blank?
  256. url = local_url
  257. end
  258. begin
  259. data = {
  260. login: hash['info']['nickname'] || hash['uid'],
  261. firstname: hash['info']['name'] || hash['info']['display_name'],
  262. email: hash['info']['email'],
  263. image_source: hash['info']['image'],
  264. web: url,
  265. address: hash['info']['location'],
  266. note: hash['info']['description'],
  267. source: hash['provider'],
  268. role_ids: Role.signup_role_ids,
  269. updated_by_id: 1,
  270. created_by_id: 1,
  271. }
  272. if hash['info']['first_name'].present? && hash['info']['last_name'].present?
  273. data[:firstname] = hash['info']['first_name']
  274. data[:lastname] = hash['info']['last_name']
  275. end
  276. create!(data)
  277. rescue => e
  278. logger.error e
  279. raise Exceptions::UnprocessableEntity, e.message
  280. end
  281. end
  282. =begin
  283. get all permissions of user
  284. user = User.find(123)
  285. user.permissions
  286. returns
  287. {
  288. 'permission.key' => true,
  289. # ...
  290. }
  291. =end
  292. def permissions
  293. list = {}
  294. ::Permission.select('permissions.name, permissions.preferences').joins(:roles).where('roles.id IN (?) AND permissions.active = ?', role_ids, true).pluck(:name, :preferences).each do |permission|
  295. next if permission[1]['selectable'] == false
  296. list[permission[0]] = true
  297. end
  298. list
  299. end
  300. =begin
  301. true or false for permission
  302. user = User.find(123)
  303. user.permissions?('permission.key') # access to certain permission.key
  304. user.permissions?(['permission.key1', 'permission.key2']) # access to permission.key1 or permission.key2
  305. user.permissions?('permission') # access to all sub keys
  306. user.permissions?('permission.*') # access if one sub key access exists
  307. returns
  308. true|false
  309. =end
  310. def permissions?(key)
  311. keys = key
  312. if key.class == String
  313. keys = [key]
  314. end
  315. keys.each do |local_key|
  316. list = []
  317. if local_key.match?(/\.\*$/)
  318. local_key.sub!('.*', '.%')
  319. permissions = ::Permission.with_parents(local_key)
  320. 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)
  321. else
  322. permission = ::Permission.lookup(name: local_key)
  323. break if permission&.active == false
  324. permissions = ::Permission.with_parents(local_key)
  325. 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)
  326. end
  327. return true if list.present?
  328. end
  329. false
  330. end
  331. =begin
  332. returns all accessable permission ids of user
  333. user = User.find(123)
  334. user.permissions_with_child_ids
  335. returns
  336. [permission1_id, permission2_id, permission3_id]
  337. =end
  338. def permissions_with_child_ids
  339. where = ''
  340. where_bind = [true]
  341. permissions.each_key do |permission_name|
  342. where += ' OR ' if where != ''
  343. where += 'permissions.name = ? OR permissions.name LIKE ?'
  344. where_bind.push permission_name
  345. where_bind.push "#{permission_name}.%"
  346. end
  347. return [] if where == ''
  348. ::Permission.where("permissions.active = ? AND (#{where})", *where_bind).pluck(:id)
  349. end
  350. =begin
  351. get all users with permission
  352. users = User.with_permissions('ticket.agent')
  353. get all users with permission "admin.session" or "ticket.agent"
  354. users = User.with_permissions(['admin.session', 'ticket.agent'])
  355. returns
  356. [user1, user2, ...]
  357. =end
  358. def self.with_permissions(keys)
  359. if keys.class != Array
  360. keys = [keys]
  361. end
  362. total_role_ids = []
  363. permission_ids = []
  364. keys.each do |key|
  365. role_ids = []
  366. ::Permission.with_parents(key).each do |local_key|
  367. permission = ::Permission.lookup(name: local_key)
  368. next if !permission
  369. permission_ids.push permission.id
  370. end
  371. next if permission_ids.blank?
  372. 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|
  373. role_ids.push role_id
  374. end
  375. total_role_ids.push role_ids
  376. end
  377. return [] if total_role_ids.blank?
  378. condition = ''
  379. total_role_ids.each do |_role_ids|
  380. if condition != ''
  381. condition += ' OR '
  382. end
  383. condition += 'roles_users.role_id IN (?)'
  384. end
  385. User.joins(:users_roles).where("(#{condition}) AND users.active = ?", *total_role_ids, true).distinct.order(:id)
  386. end
  387. =begin
  388. generate new token for reset password
  389. result = User.password_reset_new_token(username)
  390. returns
  391. result = {
  392. token: token,
  393. user: user,
  394. }
  395. =end
  396. def self.password_reset_new_token(username)
  397. return if username.blank?
  398. # try to find user based on login
  399. user = User.find_by(login: username.downcase.strip, active: true)
  400. # try second lookup with email
  401. user ||= User.find_by(email: username.downcase.strip, active: true)
  402. # check if email address exists
  403. return if !user
  404. return if !user.email
  405. # generate token
  406. token = Token.create(action: 'PasswordReset', user_id: user.id)
  407. {
  408. token: token,
  409. user: user,
  410. }
  411. end
  412. =begin
  413. returns the User instance for a given password token if found
  414. result = User.by_reset_token(token)
  415. returns
  416. result = user_model # user_model if token was verified
  417. =end
  418. def self.by_reset_token(token)
  419. Token.check(action: 'PasswordReset', name: token)
  420. end
  421. =begin
  422. reset password with token and set new password
  423. result = User.password_reset_via_token(token,password)
  424. returns
  425. result = user_model # user_model if token was verified
  426. =end
  427. def self.password_reset_via_token(token, password)
  428. # check token
  429. user = by_reset_token(token)
  430. return if !user
  431. # reset password
  432. user.update!(password: password)
  433. # delete token
  434. Token.find_by(action: 'PasswordReset', name: token).destroy
  435. user
  436. end
  437. =begin
  438. update last login date and reset login_failed (is automatically done by auth and sso backend)
  439. user = User.find(123)
  440. result = user.update_last_login
  441. returns
  442. result = new_user_model
  443. =end
  444. def update_last_login
  445. # reduce DB/ES load by updating last_login every 10 minutes only
  446. if !last_login || last_login < 10.minutes.ago
  447. self.last_login = Time.zone.now
  448. end
  449. # reset login failed
  450. self.login_failed = 0
  451. save
  452. end
  453. =begin
  454. generate new token for signup
  455. result = User.signup_new_token(user) # or email
  456. returns
  457. result = {
  458. token: token,
  459. user: user,
  460. }
  461. =end
  462. def self.signup_new_token(user)
  463. return if !user
  464. return if !user.email
  465. # generate token
  466. token = Token.create(action: 'Signup', user_id: user.id)
  467. {
  468. token: token,
  469. user: user,
  470. }
  471. end
  472. =begin
  473. verify signup with token
  474. result = User.signup_verify_via_token(token, user)
  475. returns
  476. result = user_model # user_model if token was verified
  477. =end
  478. def self.signup_verify_via_token(token, user = nil)
  479. # check token
  480. local_user = Token.check(action: 'Signup', name: token)
  481. return if !local_user
  482. # if requested user is different to current user
  483. return if user && local_user.id != user.id
  484. # set verified
  485. local_user.update!(verified: true)
  486. # delete token
  487. Token.find_by(action: 'Signup', name: token).destroy
  488. local_user
  489. end
  490. =begin
  491. merge two users to one
  492. user = User.find(123)
  493. result = user.merge(user_id_of_duplicate_user)
  494. returns
  495. result = new_user_model
  496. =end
  497. def merge(user_id_of_duplicate_user)
  498. # Raise an exception if the user is not found (?)
  499. #
  500. # (This line used to contain a useless variable assignment,
  501. # and was changed to satisfy the linter.
  502. # We're not certain of its original intention,
  503. # so the User.find call has been kept
  504. # to prevent any unexpected regressions.)
  505. User.find(user_id_of_duplicate_user)
  506. # merge missing attributes
  507. Models.merge('User', id, user_id_of_duplicate_user)
  508. true
  509. end
  510. =begin
  511. list of active users in role
  512. result = User.of_role('Agent', group_ids)
  513. result = User.of_role(['Agent', 'Admin'])
  514. returns
  515. result = [user1, user2]
  516. =end
  517. def self.of_role(role, group_ids = nil)
  518. roles_ids = Role.where(active: true, name: role).map(&:id)
  519. if !group_ids
  520. return User.where(active: true).joins(:users_roles).where('roles_users.role_id IN (?)', roles_ids).order('users.updated_at DESC')
  521. end
  522. User.where(active: true)
  523. .joins(:users_roles)
  524. .joins(:users_groups)
  525. .where('roles_users.role_id IN (?) AND users_groups.group_ids IN (?)', roles_ids, group_ids).order('users.updated_at DESC')
  526. end
  527. =begin
  528. update/sync default preferences of users with dedicated permissions
  529. result = User.update_default_preferences_by_permission('ticket.agent', force)
  530. returns
  531. result = true # false
  532. =end
  533. def self.update_default_preferences_by_permission(permission_name, force = false)
  534. permission = ::Permission.lookup(name: permission_name)
  535. return if !permission
  536. default = Rails.configuration.preferences_default_by_permission
  537. return false if !default
  538. default.deep_stringify_keys!
  539. User.with_permissions(permission.name).each do |user|
  540. next if !default[permission.name]
  541. has_changed = false
  542. default[permission.name].each do |key, value|
  543. next if !force && user.preferences[key]
  544. has_changed = true
  545. user.preferences[key] = value
  546. end
  547. if has_changed
  548. user.save!
  549. end
  550. end
  551. true
  552. end
  553. =begin
  554. update/sync default preferences of users in a dedicated role
  555. result = User.update_default_preferences_by_role('Agent', force)
  556. returns
  557. result = true # false
  558. =end
  559. def self.update_default_preferences_by_role(role_name, force = false)
  560. role = Role.lookup(name: role_name)
  561. return if !role
  562. default = Rails.configuration.preferences_default_by_permission
  563. return false if !default
  564. default.deep_stringify_keys!
  565. role.permissions.each do |permission|
  566. User.update_default_preferences_by_permission(permission.name, force)
  567. end
  568. true
  569. end
  570. def check_notifications(other, should_save = true)
  571. default = Rails.configuration.preferences_default_by_permission
  572. return if !default
  573. default.deep_stringify_keys!
  574. has_changed = false
  575. other.permissions.each do |permission|
  576. next if !default[permission.name]
  577. default[permission.name].each do |key, value|
  578. next if preferences[key]
  579. preferences[key] = value
  580. has_changed = true
  581. end
  582. end
  583. return true if !has_changed
  584. if id && should_save
  585. save!
  586. return true
  587. end
  588. @preferences_default = preferences
  589. true
  590. end
  591. def check_preferences_default
  592. if @preferences_default.blank?
  593. if id
  594. roles.each do |role|
  595. check_notifications(role, false)
  596. end
  597. end
  598. end
  599. return if @preferences_default.blank?
  600. preferences_tmp = @preferences_default.merge(preferences)
  601. self.preferences = preferences_tmp
  602. @preferences_default = nil
  603. true
  604. end
  605. def cache_delete
  606. super
  607. # delete asset caches
  608. key = "User::authorizations::#{id}"
  609. Cache.delete(key)
  610. # delete permission cache
  611. key = "User::permissions?:local_key:::#{id}"
  612. Cache.delete(key)
  613. end
  614. =begin
  615. try to find correct name
  616. [firstname, lastname] = User.name_guess('Some Name', 'some.name@example.com')
  617. =end
  618. def self.name_guess(string, email = nil)
  619. return if string.blank? && email.blank?
  620. string.strip!
  621. firstname = ''
  622. lastname = ''
  623. # "Lastname, Firstname"
  624. if string.match?(',')
  625. name = string.split(', ', 2)
  626. if name.count == 2
  627. if name[0].present?
  628. lastname = name[0].strip
  629. end
  630. if name[1].present?
  631. firstname = name[1].strip
  632. end
  633. return [firstname, lastname] if firstname.present? || lastname.present?
  634. end
  635. end
  636. # "Firstname Lastname"
  637. if string =~ /^(((Dr\.|Prof\.)[[:space:]]|).+?)[[:space:]](.+?)$/i
  638. if $1.present?
  639. firstname = $1.strip
  640. end
  641. if $4.present?
  642. lastname = $4.strip
  643. end
  644. return [firstname, lastname] if firstname.present? || lastname.present?
  645. end
  646. # -no name- "firstname.lastname@example.com"
  647. if string.blank? && email.present?
  648. scan = email.scan(/^(.+?)\.(.+?)\@.+?$/)
  649. if scan[0].present?
  650. if scan[0][0].present?
  651. firstname = scan[0][0].strip
  652. end
  653. if scan[0][1].present?
  654. lastname = scan[0][1].strip
  655. end
  656. return [firstname, lastname] if firstname.present? || lastname.present?
  657. end
  658. end
  659. nil
  660. end
  661. def no_name?
  662. firstname.blank? && lastname.blank?
  663. end
  664. # get locale of user or system if user's own is not set
  665. def locale
  666. preferences.fetch(:locale) { Setting.get('locale_default') }
  667. end
  668. private
  669. def check_name
  670. if firstname.present?
  671. firstname.strip!
  672. end
  673. if lastname.present?
  674. lastname.strip!
  675. end
  676. return true if firstname.present? && lastname.present?
  677. if (firstname.blank? && lastname.present?) || (firstname.present? && lastname.blank?)
  678. used_name = firstname.presence || lastname
  679. (local_firstname, local_lastname) = User.name_guess(used_name, email)
  680. elsif firstname.blank? && lastname.blank? && email.present?
  681. (local_firstname, local_lastname) = User.name_guess('', email)
  682. end
  683. self.firstname = local_firstname if local_firstname.present?
  684. self.lastname = local_lastname if local_lastname.present?
  685. if firstname.present? && firstname.match(/^[A-z]+$/) && (firstname.downcase == firstname || firstname.upcase == firstname)
  686. firstname.capitalize!
  687. end
  688. if lastname.present? && lastname.match(/^[A-z]+$/) && (lastname.downcase == lastname || lastname.upcase == lastname)
  689. lastname.capitalize!
  690. end
  691. true
  692. end
  693. def check_email
  694. return true if Setting.get('import_mode')
  695. return true if email.blank?
  696. self.email = email.downcase.strip
  697. return true if id == 1
  698. raise Exceptions::UnprocessableEntity, 'Invalid email' if email !~ /@/
  699. raise Exceptions::UnprocessableEntity, 'Invalid email' if email.match?(/\s/)
  700. true
  701. end
  702. def check_login
  703. # use email as login if not given
  704. if login.blank?
  705. self.login = email
  706. end
  707. # if email has changed, login is old email, change also login
  708. if changes && changes['email']
  709. if changes['email'][0] == login
  710. self.login = email
  711. end
  712. end
  713. # generate auto login
  714. if login.blank?
  715. self.login = "auto-#{Time.zone.now.to_i}-#{rand(999_999)}"
  716. end
  717. # check if login already exists
  718. self.login = login.downcase.strip
  719. check = true
  720. while check
  721. exists = User.find_by(login: login)
  722. if exists && exists.id != id
  723. self.login = "#{login}#{rand(999)}"
  724. else
  725. check = false
  726. end
  727. end
  728. true
  729. end
  730. def check_mail_delivery_failed
  731. return if email_change.blank?
  732. preferences.delete(:mail_delivery_failed)
  733. end
  734. def ensure_roles
  735. return true if role_ids.present?
  736. self.role_ids = Role.signup_role_ids
  737. end
  738. def ensure_identifier
  739. return true if email.present? || firstname.present? || lastname.present? || phone.present?
  740. return true if login.present? && !login.start_with?('auto-')
  741. raise Exceptions::UnprocessableEntity, 'Minimum one identifier (login, firstname, lastname, phone or email) for user is required.'
  742. end
  743. def ensure_uniq_email
  744. return true if Setting.get('user_email_multiple_use')
  745. return true if Setting.get('import_mode')
  746. return true if email.blank?
  747. return true if !changes
  748. return true if !changes['email']
  749. return true if !User.find_by(email: email.downcase.strip)
  750. raise Exceptions::UnprocessableEntity, 'Email address is already used for other user.'
  751. end
  752. def validate_roles(role)
  753. return true if !role_ids # we need role_ids for checking in role_ids below, in this method
  754. return true if role.preferences[:not].blank?
  755. role.preferences[:not].each do |local_role_name|
  756. local_role = Role.lookup(name: local_role_name)
  757. next if !local_role
  758. next if role_ids.exclude?(local_role.id)
  759. raise "Role #{role.name} conflicts with #{local_role.name}"
  760. end
  761. true
  762. end
  763. def validate_ooo
  764. return true if out_of_office != true
  765. raise Exceptions::UnprocessableEntity, 'out of office start is required' if out_of_office_start_at.blank?
  766. raise Exceptions::UnprocessableEntity, 'out of office end is required' if out_of_office_end_at.blank?
  767. raise Exceptions::UnprocessableEntity, 'out of office end is before start' if out_of_office_start_at > out_of_office_end_at
  768. raise Exceptions::UnprocessableEntity, 'out of office replacement user is required' if out_of_office_replacement_id.blank?
  769. raise Exceptions::UnprocessableEntity, 'out of office no such replacement user' if !User.find_by(id: out_of_office_replacement_id)
  770. true
  771. end
  772. def validate_preferences
  773. return true if !changes
  774. return true if !changes['preferences']
  775. return true if preferences.blank?
  776. return true if !preferences[:notification_sound]
  777. return true if !preferences[:notification_sound][:enabled]
  778. if preferences[:notification_sound][:enabled] == 'true'
  779. preferences[:notification_sound][:enabled] = true
  780. elsif preferences[:notification_sound][:enabled] == 'false'
  781. preferences[:notification_sound][:enabled] = false
  782. end
  783. class_name = preferences[:notification_sound][:enabled].class.to_s
  784. 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'
  785. true
  786. end
  787. =begin
  788. checks if the current user is the last one with admin permissions.
  789. Raises
  790. raise 'Minimum one user need to have admin permissions'
  791. =end
  792. def last_admin_check_by_attribute
  793. return true if !will_save_change_to_attribute?('active')
  794. return true if active != false
  795. return true if !permissions?(['admin', 'admin.user'])
  796. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  797. true
  798. end
  799. def last_admin_check_by_role(role)
  800. return true if Setting.get('import_mode')
  801. return true if !role.with_permission?(['admin', 'admin.user'])
  802. raise Exceptions::UnprocessableEntity, 'Minimum one user needs to have admin permissions.' if last_admin_check_admin_count < 1
  803. true
  804. end
  805. def last_admin_check_admin_count
  806. admin_role_ids = Role.joins(:permissions).where(permissions: { name: ['admin', 'admin.user'], active: true }, roles: { active: true }).pluck(:id)
  807. User.joins(:roles).where(roles: { id: admin_role_ids }, users: { active: true }).distinct().count - 1
  808. end
  809. def validate_agent_limit_by_attributes
  810. return true if Setting.get('system_agent_limit').blank?
  811. return true if !will_save_change_to_attribute?('active')
  812. return true if active != true
  813. return true if !permissions?('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 + 1
  816. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  817. true
  818. end
  819. def validate_agent_limit_by_role(role)
  820. return true if Setting.get('system_agent_limit').blank?
  821. return true if active != true
  822. return true if role.active != true
  823. return true if !role.with_permission?('ticket.agent')
  824. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  825. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct().count
  826. # if new added role is a ticket.agent role
  827. if ticket_agent_role_ids.include?(role.id)
  828. # if user already has a ticket.agent role
  829. hint = false
  830. role_ids.each do |locale_role_id|
  831. next if !ticket_agent_role_ids.include?(locale_role_id)
  832. hint = true
  833. break
  834. end
  835. # user has not already a ticket.agent role
  836. if hint == false
  837. count += 1
  838. end
  839. end
  840. raise Exceptions::UnprocessableEntity, 'Agent limit exceeded, please check your account settings.' if count > Setting.get('system_agent_limit').to_i
  841. true
  842. end
  843. def domain_based_assignment
  844. return true if !email
  845. return true if organization_id
  846. begin
  847. domain = Mail::Address.new(email).domain
  848. return true if !domain
  849. organization = Organization.find_by(domain: domain.downcase, domain_assignment: true)
  850. return true if !organization
  851. self.organization_id = organization.id
  852. rescue
  853. return true
  854. end
  855. true
  856. end
  857. # sets locale of the user
  858. def set_locale
  859. # set the user's locale to the one of the "executing" user
  860. return true if !UserInfo.current_user_id
  861. user = User.find_by(id: UserInfo.current_user_id)
  862. return true if !user
  863. return true if !user.preferences[:locale]
  864. preferences[:locale] = user.preferences[:locale]
  865. true
  866. end
  867. def avatar_for_email_check
  868. return true if Setting.get('import_mode')
  869. return true if email.blank?
  870. return true if email !~ /@/
  871. return true if !saved_change_to_attribute?('email') && updated_at > Time.zone.now - 10.days
  872. # save/update avatar
  873. avatar = Avatar.auto_detection(
  874. object: 'User',
  875. o_id: id,
  876. url: email,
  877. source: 'app',
  878. updated_by_id: updated_by_id,
  879. created_by_id: updated_by_id,
  880. )
  881. # update user link
  882. return true if !avatar
  883. update_column(:image, avatar.store_hash) # rubocop:disable Rails/SkipsModelValidations
  884. cache_delete
  885. true
  886. end
  887. def destroy_longer_required_objects
  888. ::Authorization.where(user_id: id).destroy_all
  889. ::Avatar.remove('User', id)
  890. ::Cti::CallerId.where(user_id: id).destroy_all
  891. ::Taskbar.where(user_id: id).destroy_all
  892. ::Karma::ActivityLog.where(user_id: id).destroy_all
  893. ::Karma::User.where(user_id: id).destroy_all
  894. ::OnlineNotification.where(user_id: id).destroy_all
  895. ::RecentView.where(created_by_id: id).destroy_all
  896. ::UserDevice.remove(id)
  897. ::Token.where(user_id: id).destroy_all
  898. ::StatsStore.remove(
  899. object: 'User',
  900. o_id: id,
  901. )
  902. true
  903. end
  904. def ensure_password
  905. self.password = ensured_password
  906. true
  907. end
  908. def ensured_password
  909. # ensure unset password for blank values of new users
  910. return nil if new_record? && password.blank?
  911. # don't permit empty password update for existing users
  912. return password_was if password.blank?
  913. # don't re-hash passwords
  914. return password if PasswordHash.crypted?(password)
  915. # hash the plaintext password
  916. PasswordHash.crypt(password)
  917. end
  918. # reset login_failed if password is changed
  919. def reset_login_failed
  920. return true if !will_save_change_to_attribute?('password')
  921. self.login_failed = 0
  922. true
  923. end
  924. # When adding/removing a phone number from the User table,
  925. # update caller ID table
  926. # to adopt/orphan matching Cti::Logs accordingly
  927. # (see https://github.com/zammad/zammad/issues/2057)
  928. def update_caller_id
  929. # skip if "phone" does not change, or changes like [nil, ""]
  930. return if persisted? && !previous_changes[:phone]&.any?(&:present?)
  931. return if destroyed? && phone.blank?
  932. Cti::CallerId.build(self)
  933. end
  934. end