user_spec.rb 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'models/application_model_examples'
  4. require 'models/concerns/has_groups_examples'
  5. require 'models/concerns/has_history_examples'
  6. require 'models/concerns/has_roles_examples'
  7. require 'models/concerns/has_groups_permissions_examples'
  8. require 'models/concerns/has_xss_sanitized_note_examples'
  9. require 'models/concerns/has_image_sanitized_note_examples'
  10. require 'models/concerns/can_be_imported_examples'
  11. require 'models/concerns/can_csv_import_examples'
  12. require 'models/concerns/can_csv_import_user_examples'
  13. require 'models/concerns/has_object_manager_attributes_examples'
  14. require 'models/user/can_lookup_search_index_attributes_examples'
  15. require 'models/user/performs_geo_lookup_examples'
  16. require 'models/concerns/has_taskbars_examples'
  17. RSpec.describe User, type: :model do
  18. subject(:user) { create(:user) }
  19. let(:customer) { create(:customer) }
  20. let(:agent) { create(:agent) }
  21. let(:admin) { create(:admin) }
  22. it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
  23. it_behaves_like 'HasGroups', group_access_factory: :agent
  24. it_behaves_like 'HasHistory'
  25. it_behaves_like 'HasRoles', group_access_factory: :agent
  26. it_behaves_like 'HasXssSanitizedNote', model_factory: :user
  27. it_behaves_like 'HasImageSanitizedNote', model_factory: :user
  28. it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
  29. it_behaves_like 'CanBeImported'
  30. # it_behaves_like 'CanCsvImport', unique_attributes: 'email'
  31. include_examples 'CanCsvImport - User specific tests'
  32. it_behaves_like 'HasObjectManagerAttributes'
  33. it_behaves_like 'CanLookupSearchIndexAttributes'
  34. it_behaves_like 'HasTaskbars'
  35. it_behaves_like 'UserPerformsGeoLookup'
  36. describe 'Class methods:' do
  37. describe '.identify' do
  38. it 'returns users by given login' do
  39. expect(described_class.identify(user.login)).to eq(user)
  40. end
  41. it 'returns users by given email' do
  42. expect(described_class.identify(user.email)).to eq(user)
  43. end
  44. it 'returns nil for empty username' do
  45. expect(described_class.identify('')).to be_nil
  46. end
  47. end
  48. describe '.reset_notifications_preferences!' do
  49. let(:sample_notifications) { { sample_notifications: true } }
  50. def change_setting_ticket_agent_default_notifications
  51. Setting.set('ticket_agent_default_notifications', sample_notifications)
  52. end
  53. context 'when user is agent' do
  54. before do
  55. # Create the agent, before the default notifications are set, so
  56. agent
  57. change_setting_ticket_agent_default_notifications
  58. end
  59. it 'changes existing matrix' do
  60. expect { described_class.reset_notifications_preferences!(agent) }
  61. .to change { agent.preferences.dig('notification_config', 'matrix') }
  62. .to sample_notifications
  63. end
  64. it 'sets matrix if preferences are empty' do
  65. agent.update_columns preferences: nil
  66. expect { described_class.reset_notifications_preferences!(agent) }
  67. .to change { agent.preferences&.dig('notification_config', 'matrix') }
  68. .to(sample_notifications)
  69. .from(nil)
  70. end
  71. it 'does not touch selected groups do' do
  72. agent.preferences['notification_config']['group_ids'] = ['123']
  73. agent.save!
  74. expect { described_class.reset_notifications_preferences!(agent) }
  75. .not_to change { agent.preferences&.dig('notification_config', 'group_ids') }
  76. end
  77. end
  78. context 'when user is not agent' do
  79. before do
  80. # Create the customer, before the default notifications are set, so
  81. customer
  82. change_setting_ticket_agent_default_notifications
  83. end
  84. it 'does not change existing matrix' do
  85. expect { described_class.reset_notifications_preferences!(customer) }
  86. .not_to change { customer.preferences.dig('notification_config', 'matrix') }
  87. end
  88. it 'sets matrix if preferences are empty' do
  89. customer.update_columns preferences: nil
  90. expect { described_class.reset_notifications_preferences!(customer) }
  91. .not_to change { customer.preferences&.dig('notification_config', 'matrix') }
  92. .from(nil)
  93. end
  94. end
  95. end
  96. end
  97. describe 'Instance methods:' do
  98. describe '#out_of_office?' do
  99. context 'without any out_of_office_* attributes set' do
  100. it 'returns false' do
  101. expect(agent.out_of_office?).to be(false)
  102. end
  103. end
  104. context 'with valid #out_of_office_* attributes' do
  105. before do
  106. agent.update(
  107. out_of_office_start_at: Time.current.yesterday,
  108. out_of_office_end_at: Time.current.tomorrow,
  109. out_of_office_replacement_id: 1
  110. )
  111. end
  112. context 'but #out_of_office: false' do
  113. before { agent.update(out_of_office: false) }
  114. it 'returns false' do
  115. expect(agent.out_of_office?).to be(false)
  116. end
  117. end
  118. context 'and #out_of_office: true' do
  119. before { agent.update(out_of_office: true) }
  120. it 'returns true' do
  121. expect(agent.out_of_office?).to be(true)
  122. end
  123. context 'after the #out_of_office_end_at time has passed' do
  124. before { travel 2.days }
  125. it 'returns false (even though #out_of_office has not changed)' do
  126. expect(agent.out_of_office).to be(true)
  127. expect(agent.out_of_office?).to be(false)
  128. end
  129. end
  130. end
  131. end
  132. context 'date range is inclusive' do
  133. before do
  134. freeze_time
  135. agent.update(
  136. out_of_office: true,
  137. out_of_office_start_at: 1.day.from_now.to_date,
  138. out_of_office_end_at: 1.week.from_now.to_date,
  139. out_of_office_replacement_id: 1
  140. )
  141. end
  142. it 'today in office' do
  143. expect(agent).not_to be_out_of_office
  144. end
  145. it 'tomorrow not in office' do
  146. travel 1.day
  147. expect(agent).to be_out_of_office
  148. end
  149. it 'after 7 days not in office' do
  150. travel 7.days
  151. expect(agent).to be_out_of_office
  152. end
  153. it 'after 8 days in office' do
  154. travel 8.days
  155. expect(agent).not_to be_out_of_office
  156. end
  157. end
  158. # https://github.com/zammad/zammad/issues/3590
  159. context 'when setting the same date' do
  160. before do
  161. freeze_time
  162. target_date = 1.day.from_now.to_date
  163. agent.update(
  164. out_of_office: true,
  165. out_of_office_start_at: target_date,
  166. out_of_office_end_at: target_date,
  167. out_of_office_replacement_id: 1
  168. )
  169. end
  170. it 'agent is out of office tomorrow' do
  171. travel 1.day
  172. expect(agent).to be_out_of_office
  173. end
  174. it 'agent is not out of office the day after tomorrow' do
  175. travel 2.days
  176. expect(agent).not_to be_out_of_office
  177. end
  178. it 'agent is not out of office today' do
  179. expect(agent).not_to be_out_of_office
  180. end
  181. context 'given it respects system time zone' do
  182. before do
  183. travel_to Time.current.end_of_day
  184. end
  185. it 'agent is in office if in UTC' do
  186. expect(agent).not_to be_out_of_office
  187. end
  188. it 'agent is out of office if ahead of UTC' do
  189. travel_to Time.current.end_of_day
  190. Setting.set('timezone_default', 'Europe/Vilnius')
  191. expect(agent).to be_out_of_office
  192. end
  193. end
  194. end
  195. end
  196. describe '#someones_out_of_office_replacement?' do
  197. it 'returns true when is replacing someone' do
  198. create(:agent).update!(
  199. out_of_office: true,
  200. out_of_office_start_at: 1.day.ago,
  201. out_of_office_end_at: 1.day.from_now,
  202. out_of_office_replacement_id: user.id,
  203. )
  204. expect(user).to be_someones_out_of_office_replacement
  205. end
  206. it 'returns false when is not replacing anyone' do
  207. expect(user).not_to be_someones_out_of_office_replacement
  208. end
  209. end
  210. describe '#out_of_office_agent' do
  211. it { is_expected.to respond_to(:out_of_office_agent) }
  212. context 'when user has no designated substitute' do
  213. it 'returns nil' do
  214. expect(user.out_of_office_agent).to be_nil
  215. end
  216. end
  217. context 'when user has designated substitute' do
  218. subject(:user) do
  219. create(:user,
  220. out_of_office: out_of_office,
  221. out_of_office_start_at: Time.zone.yesterday,
  222. out_of_office_end_at: Time.zone.tomorrow,
  223. out_of_office_replacement_id: substitute.id,)
  224. end
  225. let(:substitute) { create(:user) }
  226. context 'but is not out of office' do
  227. let(:out_of_office) { false }
  228. it 'returns nil' do
  229. expect(user.out_of_office_agent).to be_nil
  230. end
  231. end
  232. context 'and is out of office' do
  233. let(:out_of_office) { true }
  234. it 'returns the designated substitute' do
  235. expect(user.out_of_office_agent).to eq(substitute)
  236. end
  237. end
  238. context 'with recursive out of office structure' do
  239. let(:out_of_office) { true }
  240. let(:substitute) do
  241. create(:user,
  242. out_of_office: out_of_office,
  243. out_of_office_start_at: Time.zone.yesterday,
  244. out_of_office_end_at: Time.zone.tomorrow,
  245. out_of_office_replacement_id: user_active.id,)
  246. end
  247. let!(:user_active) { create(:user) }
  248. it 'returns the designated substitute recursive' do
  249. expect(user.out_of_office_agent).to eq(user_active)
  250. end
  251. end
  252. context 'with recursive out of office structure with a endless loop' do
  253. let(:out_of_office) { true }
  254. let(:substitute) do
  255. create(:user,
  256. out_of_office: out_of_office,
  257. out_of_office_start_at: Time.zone.yesterday,
  258. out_of_office_end_at: Time.zone.tomorrow,
  259. out_of_office_replacement_id: user_active.id,)
  260. end
  261. let!(:user_active) do
  262. create(:user,
  263. out_of_office: out_of_office,
  264. out_of_office_start_at: Time.zone.yesterday,
  265. out_of_office_end_at: Time.zone.tomorrow,
  266. out_of_office_replacement_id: agent.id,)
  267. end
  268. before do
  269. user_active.update(out_of_office_replacement_id: substitute.id)
  270. end
  271. it 'returns the designated substitute recursive with a endless loop' do
  272. expect(user.out_of_office_agent).to eq(substitute)
  273. end
  274. end
  275. context 'with stack depth exceeding limit' do
  276. let(:replacement_chain) do
  277. user = create(:agent)
  278. 14
  279. .times
  280. .each_with_object([user]) do |_, memo|
  281. memo << create(:agent, :ooo, ooo_agent: memo.last)
  282. end
  283. .reverse
  284. end
  285. let(:ids_executed) { [] }
  286. before do
  287. allow_any_instance_of(described_class).to receive(:out_of_office_agent).and_wrap_original do |method, *args|
  288. ids_executed << method.receiver.id
  289. method.call(*args)
  290. end
  291. allow(Rails.logger).to receive(:warn)
  292. end
  293. it 'returns the last agent at the limit' do
  294. expect(replacement_chain.first.out_of_office_agent).to eq replacement_chain[10]
  295. end
  296. it 'does not evaluate element beyond the limit' do
  297. user_beyond_limit = replacement_chain[11]
  298. replacement_chain.first.out_of_office_agent
  299. expect(ids_executed).not_to include(user_beyond_limit.id)
  300. end
  301. it 'does evaluate element within the limit' do
  302. user_within_limit = replacement_chain[5]
  303. replacement_chain.first.out_of_office_agent
  304. expect(ids_executed).to include(user_within_limit.id)
  305. end
  306. it 'logs error below the limit' do
  307. replacement_chain.first.out_of_office_agent
  308. expect(Rails.logger).to have_received(:warn).with(%r{#{Regexp.escape('Found more than 10 replacement levels for agent')}})
  309. end
  310. it 'does not logs warn within the limit' do
  311. replacement_chain[10].out_of_office_agent
  312. expect(Rails.logger).not_to have_received(:warn)
  313. end
  314. end
  315. end
  316. end
  317. describe '#out_of_office_agent_of' do
  318. context 'when no other agents are out-of-office' do
  319. it 'returns an empty ActiveRecord::Relation' do
  320. expect(agent.out_of_office_agent_of)
  321. .to be_an(ActiveRecord::Relation)
  322. .and be_empty
  323. end
  324. end
  325. context 'when designated as the substitute' do
  326. let!(:agent_on_holiday) do
  327. create(
  328. :agent,
  329. out_of_office_start_at: Time.current.yesterday,
  330. out_of_office_end_at: Time.current.tomorrow,
  331. out_of_office_replacement_id: agent.id,
  332. out_of_office: out_of_office
  333. )
  334. end
  335. context 'of an in-office agent' do
  336. let(:out_of_office) { false }
  337. it 'returns an empty ActiveRecord::Relation' do
  338. expect(agent.out_of_office_agent_of)
  339. .to be_an(ActiveRecord::Relation)
  340. .and be_empty
  341. end
  342. end
  343. context 'of an out-of-office agent' do
  344. let(:out_of_office) { true }
  345. it 'returns an ActiveRecord::Relation including that agent' do
  346. expect(agent.out_of_office_agent_of)
  347. .to contain_exactly(agent_on_holiday)
  348. end
  349. end
  350. context 'when inherited' do
  351. let(:out_of_office) { true }
  352. let!(:agent_on_holiday_sub) do
  353. create(
  354. :agent,
  355. out_of_office_start_at: Time.current.yesterday,
  356. out_of_office_end_at: Time.current.tomorrow,
  357. out_of_office_replacement_id: agent_on_holiday.id,
  358. out_of_office: out_of_office
  359. )
  360. end
  361. it 'returns an ActiveRecord::Relation including both agents' do
  362. expect(agent.out_of_office_agent_of)
  363. .to contain_exactly(agent_on_holiday, agent_on_holiday_sub)
  364. end
  365. end
  366. context 'when inherited endless loop' do
  367. let(:out_of_office) { true }
  368. let!(:agent_on_holiday_sub) do
  369. create(
  370. :agent,
  371. out_of_office_start_at: Time.current.yesterday,
  372. out_of_office_end_at: Time.current.tomorrow,
  373. out_of_office_replacement_id: agent_on_holiday.id,
  374. out_of_office: out_of_office
  375. )
  376. end
  377. let!(:agent_on_holiday_sub2) do
  378. create(
  379. :agent,
  380. out_of_office_start_at: Time.current.yesterday,
  381. out_of_office_end_at: Time.current.tomorrow,
  382. out_of_office_replacement_id: agent_on_holiday_sub.id,
  383. out_of_office: out_of_office
  384. )
  385. end
  386. before do
  387. agent_on_holiday_sub.update(out_of_office_replacement_id: agent_on_holiday_sub2.id)
  388. end
  389. it 'returns an ActiveRecord::Relation including both agents referencing each other' do
  390. expect(agent_on_holiday_sub.out_of_office_agent_of)
  391. .to contain_exactly(agent_on_holiday_sub, agent_on_holiday_sub2)
  392. end
  393. end
  394. end
  395. end
  396. describe '#by_reset_token' do
  397. subject(:user) { token.user }
  398. let(:token) { create(:token_password_reset) }
  399. context 'with a valid token' do
  400. it 'returns the matching user' do
  401. expect(described_class.by_reset_token(token.token)).to eq(user)
  402. end
  403. end
  404. context 'with an invalid token' do
  405. it 'returns nil' do
  406. expect(described_class.by_reset_token('not-existing')).to be_nil
  407. end
  408. end
  409. end
  410. describe '#password_reset_via_token' do
  411. subject(:user) { token.user }
  412. let!(:token) { create(:token_password_reset) }
  413. it 'changes the password of the token user and destroys the token' do
  414. expect { described_class.password_reset_via_token(token.token, Faker::Internet.password) }
  415. .to change { user.reload.password }
  416. .and change(Token, :count).by(-1)
  417. end
  418. end
  419. describe '#admin_password_auth_new_token' do
  420. context 'with user role agent' do
  421. subject(:user) { create(:agent) }
  422. it 'returns no token' do
  423. expect(described_class.admin_password_auth_new_token(user.login)).to be_nil
  424. end
  425. end
  426. context 'with user role admin' do
  427. subject(:user) { create(:admin) }
  428. it 'returns token' do
  429. expect(described_class.admin_password_auth_new_token(user.login).keys).to include(:user, :token)
  430. end
  431. it 'delete existing tokens when creating multiple times' do
  432. described_class.admin_password_auth_new_token(user.login)
  433. described_class.admin_password_auth_new_token(user.login)
  434. expect(Token.where(action: 'AdminAuth', user_id: user.id).count).to eq(1)
  435. end
  436. end
  437. end
  438. describe '#admin_password_auth_via_token' do
  439. context 'with invalid token' do
  440. it 'returns nil' do
  441. expect(described_class.admin_password_auth_via_token('not-existing')).to be_nil
  442. end
  443. end
  444. context 'with valid token' do
  445. let(:user) { create(:admin) }
  446. it 'returns the matching user' do
  447. result = described_class.admin_password_auth_new_token(user.login)
  448. token = result[:token].token
  449. expect(described_class.admin_password_auth_via_token(token)).to match(user)
  450. end
  451. it 'destroys token' do
  452. result = described_class.admin_password_auth_new_token(user.login)
  453. token = result[:token].token
  454. expect { described_class.admin_password_auth_via_token(token) }.to change(Token, :count).by(-1)
  455. end
  456. end
  457. end
  458. describe '#permissions' do
  459. let(:user) { create(:agent).tap { |u| u.roles = [u.roles.first] } }
  460. let(:role) { user.roles.first }
  461. let(:permissions) { role.permissions }
  462. it 'is a simple association getter' do
  463. expect(user.permissions).to match_array(permissions)
  464. end
  465. context 'for inactive permissions' do
  466. before { permissions.first.update(active: false) }
  467. it 'omits them from the returned hash' do
  468. expect(user.permissions).not_to include(permissions.first)
  469. end
  470. end
  471. context 'for permissions on inactive roles' do
  472. before { role.update(active: false) }
  473. it 'omits them from the returned hash' do
  474. expect(user.permissions).not_to include(*role.permissions)
  475. end
  476. end
  477. end
  478. describe '#permissions?' do
  479. subject(:user) { create(:user, roles: [role]) }
  480. let(:role) { create(:role, permissions: [permission]) }
  481. let(:permission) { create(:permission, name: permission_name) }
  482. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  483. let(:permission_name) { 'foo' }
  484. context 'when given that exact permission' do
  485. it 'returns true' do
  486. expect(user.permissions?('foo')).to be(true)
  487. end
  488. end
  489. context 'when given an active sub-permission' do
  490. before { create(:permission, name: 'foo.bar') }
  491. it 'returns true' do
  492. expect(user.permissions?('foo.bar')).to be(true)
  493. end
  494. end
  495. describe 'chain-of-ancestry quirk' do
  496. context 'when given an inactive sub-permission' do
  497. before { create(:permission, name: 'foo.bar.baz', active: false) }
  498. it 'returns false, even with active ancestors' do
  499. expect(user.permissions?('foo.bar.baz')).to be(false)
  500. end
  501. end
  502. context 'when given a sub-permission that does not exist' do
  503. before { create(:permission, name: 'foo.bar', active: false) }
  504. it 'can return true, even with inactive ancestors' do
  505. expect(user.permissions?('foo.bar.baz')).to be(true)
  506. end
  507. end
  508. end
  509. context 'when given a glob' do
  510. context 'matching that permission' do
  511. it 'returns true' do
  512. expect(user.permissions?('foo.*')).to be(true)
  513. end
  514. end
  515. context 'NOT matching that permission' do
  516. it 'returns false' do
  517. expect(user.permissions?('bar.*')).to be(false)
  518. end
  519. end
  520. end
  521. end
  522. context 'with privileges for a sub-permission (e.g., "foo.bar", not "foo")' do
  523. let(:permission_name) { 'foo.bar' }
  524. context 'when given that exact sub-permission' do
  525. it 'returns true' do
  526. expect(user.permissions?('foo.bar')).to be(true)
  527. end
  528. context 'but the permission is inactive' do
  529. before { permission.update(active: false) }
  530. it 'returns false' do
  531. expect(user.permissions?('foo.bar')).to be(false)
  532. end
  533. end
  534. end
  535. context 'when given a sibling sub-permission' do
  536. let(:sibling_permission) { create(:permission, name: 'foo.baz') }
  537. context 'that exists' do
  538. before { sibling_permission }
  539. it 'returns false' do
  540. expect(user.permissions?('foo.baz')).to be(false)
  541. end
  542. end
  543. context 'that does not exist' do
  544. it 'returns false' do
  545. expect(user.permissions?('foo.baz')).to be(false)
  546. end
  547. end
  548. end
  549. context 'when given the parent permission' do
  550. it 'returns false' do
  551. expect(user.permissions?('foo')).to be(false)
  552. end
  553. end
  554. context 'when given a glob' do
  555. context 'matching that sub-permission' do
  556. it 'returns true' do
  557. expect(user.permissions?('foo.*')).to be(true)
  558. end
  559. context 'but the permission is inactive' do
  560. before { permission.update(active: false) }
  561. it 'returns false' do
  562. expect(user.permissions?('foo.*')).to be(false)
  563. end
  564. end
  565. end
  566. context 'NOT matching that sub-permission' do
  567. it 'returns false' do
  568. expect(user.permissions?('bar.*')).to be(false)
  569. end
  570. end
  571. end
  572. end
  573. end
  574. describe '#permissions_with_child_ids' do
  575. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  576. subject(:user) { create(:user, roles: [role]) }
  577. let(:role) { create(:role, permissions: [permission]) }
  578. let!(:permission) { create(:permission, name: 'foo') }
  579. let!(:child_permission) { create(:permission, name: 'foo.bar') }
  580. let!(:inactive_child_permission) { create(:permission, name: 'foo.baz', active: false) }
  581. it 'includes the IDs of user’s explicit permissions' do
  582. expect(user.permissions_with_child_ids)
  583. .to include(permission.id)
  584. end
  585. it 'includes the IDs of user’s active sub-permissions' do
  586. expect(user.permissions_with_child_ids)
  587. .to include(child_permission.id)
  588. .and not_include(inactive_child_permission.id)
  589. end
  590. end
  591. end
  592. describe '#permissions_with_child_names' do
  593. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  594. subject(:user) { create(:user, roles: [role]) }
  595. let(:role) { create(:role, permissions: [permission]) }
  596. let!(:permission) { create(:permission, name: 'foo') }
  597. let!(:child_permission) { create(:permission, name: 'foo.bar') }
  598. let!(:inactive_child_permission) { create(:permission, name: 'foo.baz', active: false) }
  599. it 'includes the names of user’s explicit permissions' do
  600. expect(user.permissions_with_child_names)
  601. .to include(permission.name)
  602. end
  603. it 'includes the names of user’s active sub-permissions' do
  604. expect(user.permissions_with_child_names)
  605. .to include(child_permission.name)
  606. .and not_include(inactive_child_permission.name)
  607. end
  608. end
  609. end
  610. describe '#locale' do
  611. subject(:user) { create(:user, preferences: preferences) }
  612. context 'with no #preferences[:locale]' do
  613. let(:preferences) { {} }
  614. context 'with default locale' do
  615. before { Setting.set('locale_default', 'foo') }
  616. it 'returns the system-wide default locale' do
  617. expect(user.locale).to eq('foo')
  618. end
  619. end
  620. context 'without default locale' do
  621. before { Setting.set('locale_default', nil) }
  622. it 'returns en-us' do
  623. expect(user.locale).to eq('en-us')
  624. end
  625. end
  626. end
  627. context 'with a #preferences[:locale]' do
  628. let(:preferences) { { locale: 'bar' } }
  629. it 'returns the user’s configured locale' do
  630. expect(user.locale).to eq('bar')
  631. end
  632. end
  633. end
  634. describe '#check_login' do
  635. let(:agent) { create(:agent) }
  636. it 'does use the origin login' do
  637. new_agent = create(:agent)
  638. expect(new_agent.login).not_to end_with('1')
  639. end
  640. it 'does number up agent logins (1)' do
  641. new_agent = create(:agent, login: agent.login)
  642. expect(new_agent.login).to eq("#{agent.login}1")
  643. end
  644. it 'does number up agent logins (5)' do
  645. new_agent = create(:agent, login: agent.login)
  646. 4.times do
  647. new_agent = create(:agent, login: agent.login)
  648. end
  649. expect(new_agent.login).to eq("#{agent.login}5")
  650. end
  651. it 'does backup with uuid in cases of many duplicates' do
  652. new_agent = create(:agent, login: agent.login)
  653. 20.times do
  654. new_agent = create(:agent, login: agent.login)
  655. end
  656. expect(new_agent.login.sub!(agent.login, '')).to be_a_uuid
  657. end
  658. end
  659. describe '#check_name' do
  660. it 'guesses user first/last name with non-ASCII characters' do
  661. user = create(:user, firstname: 'perkūnas ąžuolas', lastname: '')
  662. expect(user).to have_attributes(firstname: 'Perkūnas', lastname: 'Ąžuolas')
  663. end
  664. end
  665. end
  666. describe 'Attributes:' do
  667. describe '#out_of_office' do
  668. context 'with #out_of_office_start_at: nil' do
  669. before { agent.update(out_of_office_start_at: nil, out_of_office_end_at: Time.current) }
  670. it 'cannot be set to true' do
  671. expect { agent.update(out_of_office: true) }
  672. .to raise_error(Exceptions::UnprocessableEntity)
  673. end
  674. end
  675. context 'with #out_of_office_end_at: nil' do
  676. before { agent.update(out_of_office_start_at: Time.current, out_of_office_end_at: nil) }
  677. it 'cannot be set to true' do
  678. expect { agent.update(out_of_office: true) }
  679. .to raise_error(Exceptions::UnprocessableEntity)
  680. end
  681. end
  682. context 'when #out_of_office_start_at is AFTER #out_of_office_end_at' do
  683. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.next_month) }
  684. it 'cannot be set to true' do
  685. expect { agent.update(out_of_office: true) }
  686. .to raise_error(Exceptions::UnprocessableEntity)
  687. end
  688. end
  689. context 'when #out_of_office_start_at is AFTER Time.current' do
  690. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.yesterday) }
  691. it 'cannot be set to true' do
  692. expect { agent.update(out_of_office: true) }
  693. .to raise_error(Exceptions::UnprocessableEntity)
  694. end
  695. end
  696. context 'when #out_of_office_end_at is BEFORE Time.current' do
  697. before { agent.update(out_of_office_start_at: Time.current.last_month, out_of_office_end_at: Time.current.yesterday) }
  698. it 'cannot be set to true' do
  699. expect { agent.update(out_of_office: true) }
  700. .to raise_error(Exceptions::UnprocessableEntity)
  701. end
  702. end
  703. end
  704. describe '#out_of_office_replacement_id' do
  705. it 'cannot be set to invalid user ID' do
  706. expect { agent.update(out_of_office_replacement_id: described_class.pluck(:id).max.next) }
  707. .to raise_error(ActiveRecord::InvalidForeignKey)
  708. end
  709. it 'can be set to a valid user ID' do
  710. expect { agent.update(out_of_office_replacement_id: 1) }
  711. .not_to raise_error
  712. end
  713. end
  714. describe '#login_failed' do
  715. before { user.update(login_failed: 1) }
  716. it 'is reset to 0 when password is updated' do
  717. expect { user.update(password: Faker::Internet.password) }
  718. .to change(user, :login_failed).to(0)
  719. end
  720. end
  721. describe '#password' do
  722. let(:password) { Faker::Internet.password }
  723. context 'when set to plaintext password' do
  724. it 'hashes password before saving to DB' do
  725. user.password = password
  726. expect { user.save }
  727. .to change { PasswordHash.crypted?(user.password) }
  728. end
  729. end
  730. context 'for existing user records' do
  731. before do
  732. user.update(password: password)
  733. allow(user).to receive(:ensured_password).and_call_original
  734. end
  735. context 'when changed to empty string' do
  736. it 'keeps previous password' do
  737. expect { user.update!(password: '') }
  738. .not_to change(user, :password)
  739. end
  740. it 'calls #ensured_password' do
  741. user.update!(password: '')
  742. expect(user).to have_received(:ensured_password)
  743. end
  744. end
  745. context 'when changed to nil' do
  746. it 'keeps previous password' do
  747. expect { user.update!(password: nil) }
  748. .not_to change(user, :password)
  749. end
  750. it 'calls #ensured_password' do
  751. user.update!(password: nil)
  752. expect(user).to have_received(:ensured_password)
  753. end
  754. end
  755. context 'when changed another attribute' do
  756. it 'keeps previous password' do
  757. expect { user.update!(email: "123#{user.email}") }
  758. .not_to change(user, :password)
  759. end
  760. it 'does not call #ensured_password' do
  761. user.update!(email: "123#{user.email}")
  762. expect(user).not_to have_received(:ensured_password)
  763. end
  764. end
  765. end
  766. context 'for new user records' do
  767. context 'when passed as an empty string' do
  768. let(:another_user) { create(:user, password: '') }
  769. it 'sets password to nil' do
  770. expect(another_user.password).to be_nil
  771. end
  772. end
  773. context 'when passed as nil' do
  774. let(:another_user) { create(:user, password: nil) }
  775. it 'sets password to nil' do
  776. expect(another_user.password).to be_nil
  777. end
  778. end
  779. end
  780. context 'when set to SHA2 digest (to facilitate OTRS imports)' do
  781. it 'does not re-hash before saving' do
  782. user.password = "{sha2}#{Digest::SHA2.hexdigest(password)}"
  783. expect { user.save }.not_to change(user, :password)
  784. end
  785. end
  786. context 'when set to Argon2 digest' do
  787. it 'does not re-hash before saving' do
  788. user.password = PasswordHash.crypt(password)
  789. expect { user.save }.not_to change(user, :password)
  790. end
  791. end
  792. context 'when creating two users with the same password' do
  793. before { user.update(password: password) }
  794. let(:another_user) { create(:user, password: password) }
  795. it 'does not generate the same password hash' do
  796. expect(user.password).not_to eq(another_user.password)
  797. end
  798. end
  799. context 'when saving a very long password' do
  800. let(:long_string) { "asd1ASDasd!#{Faker::Lorem.characters(number: 1_000)}" }
  801. it 'marks object as invalid by adding error' do
  802. user.update(password: long_string)
  803. expect(user.errors.first.full_message).to eq('Password is too long')
  804. end
  805. end
  806. end
  807. describe '#phone' do
  808. subject(:user) { create(:user, phone: orig_number) }
  809. context 'when included on create' do
  810. let(:orig_number) { '1234567890' }
  811. it 'adds corresponding CallerId record' do
  812. expect { user }
  813. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
  814. end
  815. end
  816. context 'when added on update' do
  817. let(:orig_number) { nil }
  818. let(:new_number) { '1234567890' }
  819. before { user } # create user
  820. it 'adds corresponding CallerId record' do
  821. expect { user.update(phone: new_number) }
  822. .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  823. end
  824. end
  825. context 'when falsely added on update (change: [nil, ""])' do
  826. let(:orig_number) { nil }
  827. let(:new_number) { '' }
  828. before { user } # create user
  829. it 'does not attempt to update CallerId record' do
  830. allow(Cti::CallerId).to receive(:build).with(any_args)
  831. expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
  832. .to eq(0)
  833. expect { user.update(phone: new_number) }
  834. .not_to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }
  835. expect(Cti::CallerId).not_to have_received(:build)
  836. end
  837. end
  838. context 'when removed on update' do
  839. let(:orig_number) { '1234567890' }
  840. let(:new_number) { nil }
  841. before { user } # create user
  842. it 'removes corresponding CallerId record' do
  843. expect { user.update(phone: nil) }
  844. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  845. end
  846. end
  847. context 'when changed on update' do
  848. let(:orig_number) { '1234567890' }
  849. let(:new_number) { orig_number.next }
  850. before { user } # create user
  851. it 'replaces CallerId record' do
  852. expect { user.update(phone: new_number) }
  853. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  854. .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  855. end
  856. end
  857. end
  858. describe '#preferences' do
  859. describe '"mail_delivery_failed{,_data}" keys' do
  860. before do
  861. user.update(
  862. preferences: {
  863. mail_delivery_failed: true,
  864. mail_delivery_failed_data: Time.current
  865. }
  866. )
  867. end
  868. it 'deletes "mail_delivery_failed"' do
  869. expect { user.update(email: Faker::Internet.email) }
  870. .to change { user.preferences.key?(:mail_delivery_failed) }.to(false)
  871. end
  872. it 'leaves "mail_delivery_failed_data" untouched' do
  873. expect { user.update(email: Faker::Internet.email) }
  874. .to not_change { user.preferences[:mail_delivery_failed_data] }
  875. end
  876. end
  877. end
  878. describe '#image' do
  879. describe 'when value is invalid' do
  880. let(:value) { 'Th1515n0t4v4l1dh45h' }
  881. it 'prevents create' do
  882. expect { create(:user, image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  883. end
  884. it 'prevents update' do
  885. expect { create(:user).update!(image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  886. end
  887. end
  888. end
  889. describe '#image_source' do
  890. describe 'when value is invalid' do
  891. let(:value) { 'Th1515n0t4v4l1dh45h' }
  892. let(:escaped) { Regexp.escape(value) }
  893. it 'valid create' do
  894. expect(create(:user, image_source: 'https://zammad.org/avatar.png').image_source).not_to be_nil
  895. end
  896. it 'removes invalid image source of create' do
  897. expect(create(:user, image_source: value).image_source).to be_nil
  898. end
  899. it 'removes invalid image source of update' do
  900. user = create(:user)
  901. user.update!(image_source: value)
  902. expect(user.image_source).to be_nil
  903. end
  904. end
  905. end
  906. describe 'fetch_avatar_for_email', performs_jobs: true do
  907. it 'enqueues avatar job when creating a user with email' do
  908. expect { create(:user) }.to have_enqueued_job AvatarCreateJob
  909. end
  910. it 'does not enqueue avatar job when creating a user without email' do
  911. expect { create(:user, :without_email) }.not_to have_enqueued_job AvatarCreateJob
  912. end
  913. context 'with an existing user' do
  914. before do
  915. agent
  916. clear_jobs
  917. end
  918. it 'enqueues avatar job when updating a user with email' do
  919. expect { agent.update! email: 'avatar@example.com' }.to have_enqueued_job AvatarCreateJob
  920. end
  921. it 'does not enqueue avatar job when updating a user without email' do
  922. expect { agent.update! login: 'avatar_login', email: nil }.not_to have_enqueued_job AvatarCreateJob
  923. end
  924. it 'does not enqueue avatar job when updating a user having email' do
  925. expect { agent.update! firstname: 'no avatar update' }.not_to have_enqueued_job AvatarCreateJob
  926. end
  927. end
  928. end
  929. end
  930. describe 'Associations:' do
  931. subject(:user) { create(:agent, groups: [group_subject]) }
  932. let!(:group_subject) { create(:group) }
  933. it 'does remove references before destroy' do
  934. refs_known = { 'Group' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  935. 'Token' => { 'user_id' => 1 },
  936. 'Ticket::Article' =>
  937. { 'created_by_id' => 1, 'updated_by_id' => 1, 'origin_by_id' => 1 },
  938. 'Ticket::StateType' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  939. 'Ticket::Article::Sender' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  940. 'Ticket::Article::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  941. 'Ticket::Article::Flag' => { 'created_by_id' => 0 },
  942. 'Ticket::Priority' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  943. 'Ticket::SharedDraftStart' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  944. 'Ticket::SharedDraftZoom' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  945. 'Ticket::TimeAccounting' => { 'created_by_id' => 0 },
  946. 'Ticket::State' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  947. 'Ticket::Flag' => { 'created_by_id' => 0 },
  948. 'PostmasterFilter' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  949. 'PublicLink' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  950. 'OnlineNotification' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  951. 'Ticket' =>
  952. { 'created_by_id' => 0, 'updated_by_id' => 0, 'owner_id' => 1, 'customer_id' => 3 },
  953. 'Template' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  954. 'Avatar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  955. 'Scheduler' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  956. 'Chat' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  957. 'HttpLog' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  958. 'EmailAddress' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  959. 'Taskbar' => { 'user_id' => 1 },
  960. 'Sla' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  961. 'UserDevice' => { 'user_id' => 1 },
  962. 'Chat::Message' => { 'created_by_id' => 1 },
  963. 'Chat::Agent' => { 'created_by_id' => 1, 'updated_by_id' => 1 },
  964. 'Chat::Session' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  965. 'Tag' => { 'created_by_id' => 0 },
  966. 'RecentView' => { 'created_by_id' => 1 },
  967. 'KnowledgeBase::Answer::Translation' =>
  968. { 'created_by_id' => 0, 'updated_by_id' => 0 },
  969. 'LdapSource' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  970. 'KnowledgeBase::Answer' =>
  971. { 'archived_by_id' => 1, 'published_by_id' => 1, 'internal_by_id' => 1 },
  972. 'Report::Profile' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  973. 'Package' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  974. 'Job' => { 'created_by_id' => 0, 'updated_by_id' => 1 },
  975. 'Store' => { 'created_by_id' => 0 },
  976. 'Cti::CallerId' => { 'user_id' => 1 },
  977. 'DataPrivacyTask' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  978. 'Trigger' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  979. 'Translation' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  980. 'ObjectManager::Attribute' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  981. 'User' => { 'created_by_id' => 2, 'out_of_office_replacement_id' => 1, 'updated_by_id' => 2 },
  982. 'User::OverviewSorting' => { 'created_by_id' => 0, 'updated_by_id' => 0, 'user_id' => 1 },
  983. 'Organization' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  984. 'Macro' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  985. 'CoreWorkflow' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  986. 'Mention' => { 'created_by_id' => 1, 'updated_by_id' => 0, 'user_id' => 1 },
  987. 'Channel' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  988. 'Role' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  989. 'History' => { 'created_by_id' => 6 },
  990. 'Webhook' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  991. 'Overview' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  992. 'ActivityStream' => { 'created_by_id' => 0 },
  993. 'StatsStore' => { 'created_by_id' => 0 },
  994. 'TextModule' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  995. 'Calendar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  996. 'UserGroup' => { 'user_id' => 1 },
  997. 'Signature' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  998. 'Authorization' => { 'user_id' => 1 } }
  999. # delete objects
  1000. token = create(:token, user: user)
  1001. online_notification = create(:online_notification, user: user)
  1002. taskbar = create(:taskbar, user: user)
  1003. user_device = create(:user_device, user: user)
  1004. cti_caller_id = create(:cti_caller_id, user: user)
  1005. authorization = create(:twitter_authorization, user: user)
  1006. recent_view = create(:recent_view, created_by: user)
  1007. avatar = create(:avatar, o_id: user.id)
  1008. overview = create(:overview, created_by_id: user.id, user_ids: [user.id])
  1009. mention = build(:mention, mentionable: create(:ticket), user: user).tap { |elem| elem.save!(validate: false) }
  1010. mention_created_by = build(:mention, mentionable: create(:ticket), user: create(:agent), created_by: user).tap { |elem| elem.save!(validate: false) }
  1011. user_created_by = create(:customer, created_by_id: user.id, updated_by_id: user.id, out_of_office_replacement_id: user.id)
  1012. chat_session = create(:'chat/session', user: user)
  1013. chat_message = create(:'chat/message', chat_session: chat_session)
  1014. chat_message2 = create(:'chat/message', chat_session: chat_session, created_by: user)
  1015. draft_start = create(:ticket_shared_draft_start, created_by: user)
  1016. draft_zoom = create(:ticket_shared_draft_zoom, created_by: user)
  1017. public_link = create(:public_link, created_by: user)
  1018. user_overview_sorting = create(:'user/overview_sorting', user: user)
  1019. expect(overview.reload.user_ids).to eq([user.id])
  1020. # create a chat agent for admin user (id=1) before agent user
  1021. # to be sure that the data gets removed and not mapped which
  1022. # would result in a foreign key because of the unique key on the
  1023. # created_by_id and updated_by_id.
  1024. create(:'chat/agent')
  1025. chat_agent_user = create(:'chat/agent', created_by_id: user.id, updated_by_id: user.id)
  1026. # invalid user (by email) which has been updated by the user which
  1027. # will get deleted (#3935)
  1028. invalid_user = build(:user, email: 'abc', created_by_id: user.id, updated_by_id: user.id)
  1029. invalid_user.save!(validate: false)
  1030. # move ownership objects
  1031. group = create(:group, created_by_id: user.id)
  1032. job = create(:job, updated_by_id: user.id)
  1033. ticket = create(:ticket, group: group_subject, owner: user)
  1034. ticket_article = create(:ticket_article, ticket: ticket, created_by_id: user.id, updated_by_id: user.id, origin_by_id: user.id)
  1035. customer_ticket1 = create(:ticket, group: group_subject, customer: user)
  1036. customer_ticket2 = create(:ticket, group: group_subject, customer: user)
  1037. customer_ticket3 = create(:ticket, group: group_subject, customer: user)
  1038. knowledge_base_answer = create(:knowledge_base_answer, archived_by_id: user.id, published_by_id: user.id, internal_by_id: user.id)
  1039. refs_user = Models.references('User', user.id, true)
  1040. expect(refs_user).to eq(refs_known)
  1041. user.destroy
  1042. expect { token.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1043. expect { online_notification.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1044. expect { taskbar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1045. expect { user_device.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1046. expect { cti_caller_id.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1047. expect { authorization.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1048. expect { recent_view.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1049. expect { avatar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1050. expect { customer_ticket1.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1051. expect { customer_ticket2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1052. expect { customer_ticket3.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1053. expect { chat_agent_user.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1054. expect { mention.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1055. expect(mention_created_by.reload.created_by_id).not_to eq(user.id)
  1056. expect(overview.reload.user_ids).to eq([])
  1057. expect { chat_session.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1058. expect { chat_message.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1059. expect { chat_message2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1060. expect { user_overview_sorting.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1061. # move ownership objects
  1062. expect { group.reload }.to change(group, :created_by_id).to(1)
  1063. expect { job.reload }.to change(job, :updated_by_id).to(1)
  1064. expect { ticket.reload }.to change(ticket, :owner_id).to(1)
  1065. expect { ticket_article.reload }
  1066. .to change(ticket_article, :origin_by_id).to(1)
  1067. .and change(ticket_article, :updated_by_id).to(1)
  1068. .and change(ticket_article, :created_by_id).to(1)
  1069. expect { knowledge_base_answer.reload }
  1070. .to change(knowledge_base_answer, :archived_by_id).to(1)
  1071. .and change(knowledge_base_answer, :published_by_id).to(1)
  1072. .and change(knowledge_base_answer, :internal_by_id).to(1)
  1073. expect { user_created_by.reload }
  1074. .to change(user_created_by, :created_by_id).to(1)
  1075. .and change(user_created_by, :updated_by_id).to(1)
  1076. .and change(user_created_by, :out_of_office_replacement_id).to(1)
  1077. expect { draft_start.reload }.to change(draft_start, :created_by_id).to(1)
  1078. expect { draft_zoom.reload }.to change(draft_zoom, :created_by_id).to(1)
  1079. expect { invalid_user.reload }.to change(invalid_user, :created_by_id).to(1)
  1080. expect { public_link.reload }.to change(public_link, :created_by_id).to(1)
  1081. end
  1082. it 'does delete cache after user deletion' do
  1083. online_notification = create(:online_notification, created_by_id: user.id)
  1084. online_notification.attributes_with_association_ids
  1085. user.destroy
  1086. expect(online_notification.reload.attributes_with_association_ids['created_by_id']).to eq(1)
  1087. end
  1088. it 'does return an exception on blocking dependencies' do
  1089. expect { user.send(:destroy_move_dependency_ownership) }.to raise_error(RuntimeError, 'Failed deleting references! Check logic for UserGroup->user_id.')
  1090. end
  1091. describe '#organization' do
  1092. describe 'email domain-based assignment' do
  1093. subject(:user) { build(:user) }
  1094. context 'when not set on creation' do
  1095. before { user.assign_attributes(organization: nil) }
  1096. context 'and #email domain matches an existing Organization#domain' do
  1097. before { user.assign_attributes(email: 'user@example.com') }
  1098. let(:organization) { create(:organization, domain: 'example.com') }
  1099. context 'and Organization#domain_assignment is false (default)' do
  1100. before { organization.update(domain_assignment: false) }
  1101. it 'remains nil' do
  1102. expect { user.save }.not_to change(user, :organization)
  1103. end
  1104. end
  1105. context 'and Organization#domain_assignment is true' do
  1106. before { organization.update(domain_assignment: true) }
  1107. it 'is automatically set to matching Organization' do
  1108. expect { user.save }
  1109. .to change(user, :organization).to(organization)
  1110. end
  1111. end
  1112. end
  1113. context 'and #email domain doesn’t match any Organization#domain' do
  1114. before { user.assign_attributes(email: 'user@example.net') }
  1115. let(:organization) { create(:organization, domain: 'example.com') }
  1116. context 'and Organization#domain_assignment is true' do
  1117. before { organization.update(domain_assignment: true) }
  1118. it 'remains nil' do
  1119. expect { user.save }.not_to change(user, :organization)
  1120. end
  1121. end
  1122. end
  1123. end
  1124. context 'when set on creation' do
  1125. before { user.assign_attributes(organization: specified_organization) }
  1126. let(:specified_organization) { create(:organization, domain: 'example.net') }
  1127. context 'and #email domain matches a DIFFERENT Organization#domain' do
  1128. before { user.assign_attributes(email: 'user@example.com') }
  1129. let!(:matching_organization) { create(:organization, domain: 'example.com') }
  1130. context 'and Organization#domain_assignment is true' do
  1131. before { matching_organization.update(domain_assignment: true) }
  1132. it 'is NOT automatically set to matching Organization' do
  1133. expect { user.save }
  1134. .not_to change(user, :organization).from(specified_organization)
  1135. end
  1136. end
  1137. end
  1138. end
  1139. end
  1140. end
  1141. end
  1142. describe 'Callbacks, Observers, & Async Transactions -' do
  1143. describe 'System-wide agent limit checks:' do
  1144. let(:agent_role) { Role.lookup(name: 'Agent') }
  1145. let(:admin_role) { Role.lookup(name: 'Admin') }
  1146. let(:current_agents) { described_class.with_permissions('ticket.agent') }
  1147. describe '#validate_agent_limit_by_role' do
  1148. context 'for Integer value of system_agent_limit' do
  1149. context 'before exceeding the agent limit' do
  1150. before { Setting.set('system_agent_limit', current_agents.count + 1) }
  1151. it 'grants agent creation' do
  1152. expect { create(:agent) }
  1153. .to change(current_agents, :count).by(1)
  1154. end
  1155. it 'grants role change' do
  1156. future_agent = create(:customer)
  1157. expect { future_agent.roles = [agent_role] }
  1158. .to change(current_agents, :count).by(1)
  1159. end
  1160. describe 'role updates' do
  1161. let(:agent) { create(:agent) }
  1162. it 'grants update by instances' do
  1163. expect { agent.roles = [admin_role, agent_role] }
  1164. .not_to raise_error
  1165. end
  1166. it 'grants update by id (Integer)' do
  1167. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  1168. .not_to raise_error
  1169. end
  1170. it 'grants update by id (String)' do
  1171. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  1172. .not_to raise_error
  1173. end
  1174. end
  1175. end
  1176. context 'when exceeding the agent limit' do
  1177. it 'creation of new agents' do
  1178. Setting.set('system_agent_limit', current_agents.count + 2)
  1179. create_list(:agent, 2)
  1180. expect { create(:agent) }
  1181. .to raise_error(Exceptions::UnprocessableEntity)
  1182. .and not_change(current_agents, :count)
  1183. end
  1184. it 'prevents role change' do
  1185. Setting.set('system_agent_limit', current_agents.count)
  1186. future_agent = create(:customer)
  1187. expect { future_agent.roles = [agent_role] }
  1188. .to raise_error(Exceptions::UnprocessableEntity)
  1189. .and not_change(current_agents, :count)
  1190. end
  1191. end
  1192. end
  1193. context 'for String value of system_agent_limit' do
  1194. context 'before exceeding the agent limit' do
  1195. before { Setting.set('system_agent_limit', (current_agents.count + 1).to_s) }
  1196. it 'grants agent creation' do
  1197. expect { create(:agent) }
  1198. .to change(current_agents, :count).by(1)
  1199. end
  1200. it 'grants role change' do
  1201. future_agent = create(:customer)
  1202. expect { future_agent.roles = [agent_role] }
  1203. .to change(current_agents, :count).by(1)
  1204. end
  1205. describe 'role updates' do
  1206. let(:agent) { create(:agent) }
  1207. it 'grants update by instances' do
  1208. expect { agent.roles = [admin_role, agent_role] }
  1209. .not_to raise_error
  1210. end
  1211. it 'grants update by id (Integer)' do
  1212. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  1213. .not_to raise_error
  1214. end
  1215. it 'grants update by id (String)' do
  1216. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  1217. .not_to raise_error
  1218. end
  1219. end
  1220. end
  1221. context 'when exceeding the agent limit' do
  1222. it 'creation of new agents' do
  1223. Setting.set('system_agent_limit', (current_agents.count + 2).to_s)
  1224. create_list(:agent, 2)
  1225. expect { create(:agent) }
  1226. .to raise_error(Exceptions::UnprocessableEntity)
  1227. .and not_change(current_agents, :count)
  1228. end
  1229. it 'prevents role change' do
  1230. Setting.set('system_agent_limit', current_agents.count.to_s)
  1231. future_agent = create(:customer)
  1232. expect { future_agent.roles = [agent_role] }
  1233. .to raise_error(Exceptions::UnprocessableEntity)
  1234. .and not_change(current_agents, :count)
  1235. end
  1236. end
  1237. end
  1238. end
  1239. describe '#validate_agent_limit_by_attributes' do
  1240. context 'for Integer value of system_agent_limit' do
  1241. before { Setting.set('system_agent_limit', current_agents.count) }
  1242. context 'when exceeding the agent limit' do
  1243. it 'prevents re-activation of agents' do
  1244. inactive_agent = create(:agent, active: false)
  1245. expect { inactive_agent.update!(active: true) }
  1246. .to raise_error(Exceptions::UnprocessableEntity)
  1247. .and not_change(current_agents, :count)
  1248. end
  1249. end
  1250. end
  1251. context 'for String value of system_agent_limit' do
  1252. before { Setting.set('system_agent_limit', current_agents.count.to_s) }
  1253. context 'when exceeding the agent limit' do
  1254. it 'prevents re-activation of agents' do
  1255. inactive_agent = create(:agent, active: false)
  1256. expect { inactive_agent.update!(active: true) }
  1257. .to raise_error(Exceptions::UnprocessableEntity)
  1258. .and not_change(current_agents, :count)
  1259. end
  1260. end
  1261. end
  1262. end
  1263. end
  1264. describe 'Touching associations on update:' do
  1265. subject!(:user) { create(:customer) }
  1266. let!(:organization) { create(:organization) }
  1267. context 'when a customer gets a organization' do
  1268. it 'touches its organization' do
  1269. expect { user.update(organization: organization) }
  1270. .to change { organization.reload.updated_at }
  1271. end
  1272. end
  1273. end
  1274. describe 'Cti::CallerId syncing:' do
  1275. context 'with a #phone attribute' do
  1276. subject(:user) { build(:user, phone: '1234567890') }
  1277. it 'adds CallerId record on creation (via Cti::CallerId.build)' do
  1278. expect(Cti::CallerId).to receive(:build).with(user)
  1279. user.save
  1280. end
  1281. it 'does not update CallerId record on touch/update (via Cti::CallerId.build)' do
  1282. expect(Cti::CallerId).to receive(:build).with(user)
  1283. user.save
  1284. expect(Cti::CallerId).not_to receive(:build).with(user)
  1285. user.touch
  1286. end
  1287. it 'destroys CallerId record on deletion' do
  1288. user.save
  1289. expect { user.destroy }
  1290. .to change(Cti::CallerId, :count).by(-1)
  1291. end
  1292. end
  1293. end
  1294. describe 'Cti::Log syncing:' do
  1295. context 'with existing Log records', performs_jobs: true do
  1296. context 'for incoming calls from an unknown number' do
  1297. let!(:log) { create(:'cti/log', :with_preferences, from: '1234567890', direction: 'in') }
  1298. context 'when creating a new user with that number' do
  1299. subject(:user) { build(:user, phone: log.from) }
  1300. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1301. expect do
  1302. user.save
  1303. perform_enqueued_jobs commit_transaction: true
  1304. end.to change { log.reload.preferences[:from]&.first }
  1305. .to(hash_including('caller_id' => user.phone))
  1306. end
  1307. end
  1308. context 'when updating a user with that number' do
  1309. subject(:user) { create(:user) }
  1310. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1311. expect do
  1312. user.update(phone: log.from)
  1313. perform_enqueued_jobs commit_transaction: true
  1314. end.to change { log.reload.preferences[:from]&.first }
  1315. .to(hash_including('object' => 'User', 'o_id' => user.id))
  1316. end
  1317. end
  1318. context 'when creating a new user with an empty number' do
  1319. subject(:user) { build(:user, phone: '') }
  1320. it 'does not modify any Log records' do
  1321. expect do
  1322. user.save
  1323. perform_enqueued_jobs commit_transaction: true
  1324. end.not_to change { log.reload.attributes }
  1325. end
  1326. end
  1327. context 'when creating a new user with no number' do
  1328. subject(:user) { build(:user, phone: nil) }
  1329. it 'does not modify any Log records' do
  1330. expect do
  1331. user.save
  1332. perform_enqueued_jobs commit_transaction: true
  1333. end.not_to change { log.reload.attributes }
  1334. end
  1335. end
  1336. end
  1337. context 'for incoming calls from the given user' do
  1338. subject(:user) { create(:user, phone: '1234567890') }
  1339. let!(:logs) { create_list(:'cti/log', 5, :with_preferences, from: user.phone, direction: 'in') }
  1340. context 'when updating #phone attribute' do
  1341. context 'to another number' do
  1342. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1343. expect do
  1344. user.update(phone: '0123456789')
  1345. perform_enqueued_jobs commit_transaction: true
  1346. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1347. .to(Array.new(5) { nil })
  1348. end
  1349. end
  1350. context 'to an empty string' do
  1351. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1352. expect do
  1353. user.update(phone: '')
  1354. perform_enqueued_jobs commit_transaction: true
  1355. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1356. .to(Array.new(5) { nil })
  1357. end
  1358. end
  1359. context 'to nil' do
  1360. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1361. expect do
  1362. user.update(phone: nil)
  1363. perform_enqueued_jobs commit_transaction: true
  1364. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1365. .to(Array.new(5) { nil })
  1366. end
  1367. end
  1368. end
  1369. context 'when updating attributes other than #phone' do
  1370. it 'does not modify any Log records' do
  1371. expect do
  1372. user.update(mobile: '2345678901')
  1373. perform_enqueued_jobs commit_transaction: true
  1374. end.not_to change { logs.map(&:reload).map(&:attributes) }
  1375. end
  1376. end
  1377. end
  1378. end
  1379. end
  1380. end
  1381. describe 'Assign user to multiple organizations #1573' do
  1382. context 'when importing users via csv' do
  1383. let(:organization1) { create(:organization) }
  1384. let(:organization2) { create(:organization) }
  1385. let(:organization3) { create(:organization) }
  1386. let(:organization4) { create(:organization) }
  1387. let(:user) { create(:agent, organization: organization1, organizations: [organization2, organization3]) }
  1388. def csv_import(string)
  1389. User.csv_import(
  1390. string: string,
  1391. parse_params: {
  1392. col_sep: ',',
  1393. },
  1394. try: false,
  1395. delete: false,
  1396. )
  1397. end
  1398. before do
  1399. user
  1400. end
  1401. it 'does not change user on re-import' do
  1402. expect { csv_import(described_class.csv_example) }.not_to change { user.reload.updated_at }
  1403. end
  1404. it 'does not change user on different organization order' do
  1405. string = described_class.csv_example
  1406. string.sub!(organization3.name, organization2.name)
  1407. string.sub!(organization2.name, organization3.name)
  1408. expect { csv_import(string) }.not_to change { user.reload.updated_at }
  1409. end
  1410. it 'does change user on different organizations' do
  1411. string = described_class.csv_example
  1412. string.sub!(organization2.name, organization4.name)
  1413. expect { csv_import(string) }.to change { user.reload.updated_at }
  1414. end
  1415. end
  1416. context 'when creating users' do
  1417. it 'does not allow creation without primary organization but secondary organizations' do
  1418. expect { create(:agent, organization: nil, organizations: [create(:organization)]) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Secondary organizations are only allowed when the primary organization is given.')
  1419. end
  1420. it 'does not allow creation with more than 250 organizations' do
  1421. expect { create(:agent, organization: create(:organization), organizations: create_list(:organization, 251)) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: More than 250 secondary organizations are not allowed.')
  1422. end
  1423. end
  1424. end
  1425. describe 'Check default agent notifications preferences' do
  1426. context 'when creating users' do
  1427. it 'does apply default agent notification to agent preferences' do
  1428. user = create(:agent)
  1429. expect(user.reload.preferences[:notification_config][:matrix]).to eq(Setting.get('ticket_agent_default_notifications'))
  1430. end
  1431. it 'does not apply default agent notification to customer preferences' do
  1432. user = create(:customer)
  1433. expect(user.reload.preferences[:notification_config]).to be_blank
  1434. end
  1435. end
  1436. context 'when adding role to existing user' do
  1437. it 'does apply default agent notification to agent preferences (without "ticket.agent" permission before)' do
  1438. future_agent = create(:customer)
  1439. expect { future_agent.roles = [Role.lookup(name: 'Agent')] }
  1440. .to change { future_agent.reload.preferences.dig('notification_config', 'matrix') }
  1441. .to Setting.get('ticket_agent_default_notifications')
  1442. end
  1443. it 'does not apply default agent notification to agent preferences (with "ticket.agent" permission before)' do
  1444. agent = create(:agent)
  1445. expect { agent.roles = [Role.lookup(name: 'Customer')] }
  1446. .not_to change { agent.reload.preferences.dig('notification_config', 'matrix') }
  1447. end
  1448. end
  1449. end
  1450. end