user_spec.rb 64 KB

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