user_spec.rb 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  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. describe '#two_factor_configured?' do
  666. let(:user) { create(:user) }
  667. context 'with no two factor configured' do
  668. it 'returns false' do
  669. expect(user.two_factor_configured?).to be(false)
  670. end
  671. end
  672. context 'with two factor configured' do
  673. before do
  674. create(:user_two_factor_preference, :authenticator_app, user: user)
  675. end
  676. it 'returns true' do
  677. # 'user' variable is cached + was created before the preference was set.
  678. expect(user.reload.two_factor_configured?).to be(true)
  679. end
  680. end
  681. end
  682. end
  683. describe 'Attributes:' do
  684. describe '#out_of_office' do
  685. context 'with #out_of_office_start_at: nil' do
  686. before { agent.update(out_of_office_start_at: nil, out_of_office_end_at: Time.current) }
  687. it 'cannot be set to true' do
  688. expect { agent.update(out_of_office: true) }
  689. .to raise_error(Exceptions::UnprocessableEntity)
  690. end
  691. end
  692. context 'with #out_of_office_end_at: nil' do
  693. before { agent.update(out_of_office_start_at: Time.current, out_of_office_end_at: nil) }
  694. it 'cannot be set to true' do
  695. expect { agent.update(out_of_office: true) }
  696. .to raise_error(Exceptions::UnprocessableEntity)
  697. end
  698. end
  699. context 'when #out_of_office_start_at is AFTER #out_of_office_end_at' do
  700. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.next_month) }
  701. it 'cannot be set to true' do
  702. expect { agent.update(out_of_office: true) }
  703. .to raise_error(Exceptions::UnprocessableEntity)
  704. end
  705. end
  706. context 'when #out_of_office_start_at is AFTER Time.current' do
  707. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.yesterday) }
  708. it 'cannot be set to true' do
  709. expect { agent.update(out_of_office: true) }
  710. .to raise_error(Exceptions::UnprocessableEntity)
  711. end
  712. end
  713. context 'when #out_of_office_end_at is BEFORE Time.current' do
  714. before { agent.update(out_of_office_start_at: Time.current.last_month, out_of_office_end_at: Time.current.yesterday) }
  715. it 'cannot be set to true' do
  716. expect { agent.update(out_of_office: true) }
  717. .to raise_error(Exceptions::UnprocessableEntity)
  718. end
  719. end
  720. end
  721. describe '#out_of_office_replacement_id' do
  722. it 'cannot be set to invalid user ID' do
  723. expect { agent.update(out_of_office_replacement_id: described_class.pluck(:id).max.next) }
  724. .to raise_error(ActiveRecord::InvalidForeignKey)
  725. end
  726. it 'can be set to a valid user ID' do
  727. expect { agent.update(out_of_office_replacement_id: 1) }
  728. .not_to raise_error
  729. end
  730. end
  731. describe '#login_failed' do
  732. before { user.update(login_failed: 1) }
  733. it 'is reset to 0 when password is updated' do
  734. expect { user.update(password: Faker::Internet.password) }
  735. .to change(user, :login_failed).to(0)
  736. end
  737. end
  738. describe '#password' do
  739. let(:password) { Faker::Internet.password }
  740. context 'when set to plaintext password' do
  741. it 'hashes password before saving to DB' do
  742. user.password = password
  743. expect { user.save }
  744. .to change { PasswordHash.crypted?(user.password) }
  745. end
  746. end
  747. context 'for existing user records' do
  748. before do
  749. user.update(password: password)
  750. allow(user).to receive(:ensured_password).and_call_original
  751. end
  752. context 'when changed to empty string' do
  753. it 'keeps previous password' do
  754. expect { user.update!(password: '') }
  755. .not_to change(user, :password)
  756. end
  757. it 'calls #ensured_password' do
  758. user.update!(password: '')
  759. expect(user).to have_received(:ensured_password)
  760. end
  761. end
  762. context 'when changed to nil' do
  763. it 'keeps previous password' do
  764. expect { user.update!(password: nil) }
  765. .not_to change(user, :password)
  766. end
  767. it 'calls #ensured_password' do
  768. user.update!(password: nil)
  769. expect(user).to have_received(:ensured_password)
  770. end
  771. end
  772. context 'when changed another attribute' do
  773. it 'keeps previous password' do
  774. expect { user.update!(email: "123#{user.email}") }
  775. .not_to change(user, :password)
  776. end
  777. it 'does not call #ensured_password' do
  778. user.update!(email: "123#{user.email}")
  779. expect(user).not_to have_received(:ensured_password)
  780. end
  781. end
  782. end
  783. context 'for new user records' do
  784. context 'when passed as an empty string' do
  785. let(:another_user) { create(:user, password: '') }
  786. it 'sets password to nil' do
  787. expect(another_user.password).to be_nil
  788. end
  789. end
  790. context 'when passed as nil' do
  791. let(:another_user) { create(:user, password: nil) }
  792. it 'sets password to nil' do
  793. expect(another_user.password).to be_nil
  794. end
  795. end
  796. end
  797. context 'when set to SHA2 digest (to facilitate OTRS imports)' do
  798. it 'does not re-hash before saving' do
  799. user.password = "{sha2}#{Digest::SHA2.hexdigest(password)}"
  800. expect { user.save }.not_to change(user, :password)
  801. end
  802. end
  803. context 'when set to Argon2 digest' do
  804. it 'does not re-hash before saving' do
  805. user.password = PasswordHash.crypt(password)
  806. expect { user.save }.not_to change(user, :password)
  807. end
  808. end
  809. context 'when creating two users with the same password' do
  810. before { user.update(password: password) }
  811. let(:another_user) { create(:user, password: password) }
  812. it 'does not generate the same password hash' do
  813. expect(user.password).not_to eq(another_user.password)
  814. end
  815. end
  816. context 'when saving a very long password' do
  817. let(:long_string) { "asd1ASDasd!#{Faker::Lorem.characters(number: 1_000)}" }
  818. it 'marks object as invalid by adding error' do
  819. user.update(password: long_string)
  820. expect(user.errors.first.full_message).to eq('Password is too long')
  821. end
  822. end
  823. end
  824. describe '#phone' do
  825. subject(:user) { create(:user, phone: orig_number) }
  826. context 'when included on create' do
  827. let(:orig_number) { '1234567890' }
  828. it 'adds corresponding CallerId record' do
  829. expect { user }
  830. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
  831. end
  832. end
  833. context 'when added on update' do
  834. let(:orig_number) { nil }
  835. let(:new_number) { '1234567890' }
  836. before { user } # create user
  837. it 'adds corresponding CallerId record' do
  838. expect { user.update(phone: new_number) }
  839. .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  840. end
  841. end
  842. context 'when falsely added on update (change: [nil, ""])' do
  843. let(:orig_number) { nil }
  844. let(:new_number) { '' }
  845. before { user } # create user
  846. it 'does not attempt to update CallerId record' do
  847. allow(Cti::CallerId).to receive(:build).with(any_args)
  848. expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
  849. .to eq(0)
  850. expect { user.update(phone: new_number) }
  851. .not_to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }
  852. expect(Cti::CallerId).not_to have_received(:build)
  853. end
  854. end
  855. context 'when removed on update' do
  856. let(:orig_number) { '1234567890' }
  857. let(:new_number) { nil }
  858. before { user } # create user
  859. it 'removes corresponding CallerId record' do
  860. expect { user.update(phone: nil) }
  861. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  862. end
  863. end
  864. context 'when changed on update' do
  865. let(:orig_number) { '1234567890' }
  866. let(:new_number) { orig_number.next }
  867. before { user } # create user
  868. it 'replaces CallerId record' do
  869. expect { user.update(phone: new_number) }
  870. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  871. .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  872. end
  873. end
  874. end
  875. describe '#preferences' do
  876. describe '"mail_delivery_failed{,_data}" keys' do
  877. before do
  878. user.update(
  879. preferences: {
  880. mail_delivery_failed: true,
  881. mail_delivery_failed_data: Time.current
  882. }
  883. )
  884. end
  885. it 'deletes "mail_delivery_failed"' do
  886. expect { user.update(email: Faker::Internet.email) }
  887. .to change { user.preferences.key?(:mail_delivery_failed) }.to(false)
  888. end
  889. it 'leaves "mail_delivery_failed_data" untouched' do
  890. expect { user.update(email: Faker::Internet.email) }
  891. .to not_change { user.preferences[:mail_delivery_failed_data] }
  892. end
  893. end
  894. end
  895. describe '#image' do
  896. describe 'when value is invalid' do
  897. let(:value) { 'Th1515n0t4v4l1dh45h' }
  898. it 'prevents create' do
  899. expect { create(:user, image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  900. end
  901. it 'prevents update' do
  902. expect { create(:user).update!(image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  903. end
  904. end
  905. end
  906. describe '#image_source' do
  907. describe 'when value is invalid' do
  908. let(:value) { 'Th1515n0t4v4l1dh45h' }
  909. let(:escaped) { Regexp.escape(value) }
  910. it 'valid create' do
  911. expect(create(:user, image_source: 'https://zammad.org/avatar.png').image_source).not_to be_nil
  912. end
  913. it 'removes invalid image source of create' do
  914. expect(create(:user, image_source: value).image_source).to be_nil
  915. end
  916. it 'removes invalid image source of update' do
  917. user = create(:user)
  918. user.update!(image_source: value)
  919. expect(user.image_source).to be_nil
  920. end
  921. end
  922. end
  923. describe 'fetch_avatar_for_email', performs_jobs: true do
  924. it 'enqueues avatar job when creating a user with email' do
  925. expect { create(:user) }.to have_enqueued_job AvatarCreateJob
  926. end
  927. it 'does not enqueue avatar job when creating a user without email' do
  928. expect { create(:user, :without_email) }.not_to have_enqueued_job AvatarCreateJob
  929. end
  930. context 'with an existing user' do
  931. before do
  932. agent
  933. clear_jobs
  934. end
  935. it 'enqueues avatar job when updating a user with email' do
  936. expect { agent.update! email: 'avatar@example.com' }.to have_enqueued_job AvatarCreateJob
  937. end
  938. it 'does not enqueue avatar job when updating a user without email' do
  939. expect { agent.update! login: 'avatar_login', email: nil }.not_to have_enqueued_job AvatarCreateJob
  940. end
  941. it 'does not enqueue avatar job when updating a user having email' do
  942. expect { agent.update! firstname: 'no avatar update' }.not_to have_enqueued_job AvatarCreateJob
  943. end
  944. end
  945. end
  946. end
  947. describe 'Associations:' do
  948. subject(:user) { create(:agent, groups: [group_subject]) }
  949. let!(:group_subject) { create(:group) }
  950. it 'does remove references before destroy' do
  951. refs_known = { 'Group' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  952. 'Token' => { 'user_id' => 1 },
  953. 'Ticket::Article' =>
  954. { 'created_by_id' => 1, 'updated_by_id' => 1, 'origin_by_id' => 1 },
  955. 'Ticket::StateType' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  956. 'Ticket::Article::Sender' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  957. 'Ticket::Article::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  958. 'Ticket::Article::Flag' => { 'created_by_id' => 0 },
  959. 'Ticket::Priority' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  960. 'Ticket::SharedDraftStart' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  961. 'Ticket::SharedDraftZoom' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  962. 'Ticket::TimeAccounting' => { 'created_by_id' => 0 },
  963. 'Ticket::TimeAccounting::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  964. 'Ticket::State' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  965. 'Ticket::Flag' => { 'created_by_id' => 0 },
  966. 'PostmasterFilter' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  967. 'PublicLink' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  968. 'User::TwoFactorPreference' => { 'created_by_id' => 1, 'updated_by_id' => 1, 'user_id' => 1 },
  969. 'OnlineNotification' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  970. 'Ticket' =>
  971. { 'created_by_id' => 0, 'updated_by_id' => 0, 'owner_id' => 1, 'customer_id' => 3 },
  972. 'Template' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  973. 'Avatar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  974. 'Scheduler' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  975. 'Chat' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  976. 'HttpLog' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  977. 'EmailAddress' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  978. 'Taskbar' => { 'user_id' => 1 },
  979. 'Sla' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  980. 'UserDevice' => { 'user_id' => 1 },
  981. 'Chat::Message' => { 'created_by_id' => 1 },
  982. 'Chat::Agent' => { 'created_by_id' => 1, 'updated_by_id' => 1 },
  983. 'Chat::Session' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  984. 'Tag' => { 'created_by_id' => 0 },
  985. 'RecentView' => { 'created_by_id' => 1 },
  986. 'KnowledgeBase::Answer::Translation' =>
  987. { 'created_by_id' => 0, 'updated_by_id' => 0 },
  988. 'LdapSource' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  989. 'KnowledgeBase::Answer' =>
  990. { 'archived_by_id' => 1, 'published_by_id' => 1, 'internal_by_id' => 1 },
  991. 'Report::Profile' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  992. 'Package' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  993. 'Job' => { 'created_by_id' => 0, 'updated_by_id' => 1 },
  994. 'Store' => { 'created_by_id' => 0 },
  995. 'Cti::CallerId' => { 'user_id' => 1 },
  996. 'DataPrivacyTask' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  997. 'Trigger' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  998. 'Translation' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  999. 'ObjectManager::Attribute' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1000. 'User' => { 'created_by_id' => 2, 'out_of_office_replacement_id' => 1, 'updated_by_id' => 2 },
  1001. 'User::OverviewSorting' => { 'created_by_id' => 0, 'updated_by_id' => 0, 'user_id' => 1 },
  1002. 'Organization' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1003. 'Macro' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1004. 'CoreWorkflow' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1005. 'Mention' => { 'created_by_id' => 1, 'updated_by_id' => 0, 'user_id' => 1 },
  1006. 'Channel' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1007. 'Role' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1008. 'History' => { 'created_by_id' => 6 },
  1009. 'Webhook' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1010. 'Overview' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  1011. 'PGPKey' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1012. 'ActivityStream' => { 'created_by_id' => 0 },
  1013. 'StatsStore' => { 'created_by_id' => 0 },
  1014. 'TextModule' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1015. 'Calendar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1016. 'UserGroup' => { 'user_id' => 1 },
  1017. 'Signature' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  1018. 'Authorization' => { 'user_id' => 1 } }
  1019. # delete objects
  1020. token = create(:token, user: user)
  1021. online_notification = create(:online_notification, user: user)
  1022. taskbar = create(:taskbar, user: user)
  1023. user_device = create(:user_device, user: user)
  1024. cti_caller_id = create(:cti_caller_id, user: user)
  1025. authorization = create(:twitter_authorization, user: user)
  1026. recent_view = create(:recent_view, created_by: user)
  1027. avatar = create(:avatar, o_id: user.id)
  1028. overview = create(:overview, created_by_id: user.id, user_ids: [user.id])
  1029. mention = build(:mention, mentionable: create(:ticket), user: user).tap { |elem| elem.save!(validate: false) }
  1030. mention_created_by = build(:mention, mentionable: create(:ticket), user: create(:agent), created_by: user).tap { |elem| elem.save!(validate: false) }
  1031. user_created_by = create(:customer, created_by_id: user.id, updated_by_id: user.id, out_of_office_replacement_id: user.id)
  1032. chat_session = create(:'chat/session', user: user)
  1033. chat_message = create(:'chat/message', chat_session: chat_session)
  1034. chat_message2 = create(:'chat/message', chat_session: chat_session, created_by: user)
  1035. draft_start = create(:ticket_shared_draft_start, created_by: user)
  1036. draft_zoom = create(:ticket_shared_draft_zoom, created_by: user)
  1037. public_link = create(:public_link, created_by: user)
  1038. user_two_factor_preference = create(:user_two_factor_preference, :authenticator_app, user: user)
  1039. user_overview_sorting = create(:'user/overview_sorting', user: user)
  1040. expect(overview.reload.user_ids).to eq([user.id])
  1041. # create a chat agent for admin user (id=1) before agent user
  1042. # to be sure that the data gets removed and not mapped which
  1043. # would result in a foreign key because of the unique key on the
  1044. # created_by_id and updated_by_id.
  1045. create(:'chat/agent')
  1046. chat_agent_user = create(:'chat/agent', created_by_id: user.id, updated_by_id: user.id)
  1047. # invalid user (by email) which has been updated by the user which
  1048. # will get deleted (#3935)
  1049. invalid_user = build(:user, email: 'abc', created_by_id: user.id, updated_by_id: user.id)
  1050. invalid_user.save!(validate: false)
  1051. # move ownership objects
  1052. group = create(:group, created_by_id: user.id)
  1053. job = create(:job, updated_by_id: user.id)
  1054. ticket = create(:ticket, group: group_subject, owner: user)
  1055. ticket_article = create(:ticket_article, ticket: ticket, created_by_id: user.id, updated_by_id: user.id, origin_by_id: user.id)
  1056. customer_ticket1 = create(:ticket, group: group_subject, customer: user)
  1057. customer_ticket2 = create(:ticket, group: group_subject, customer: user)
  1058. customer_ticket3 = create(:ticket, group: group_subject, customer: user)
  1059. knowledge_base_answer = create(:knowledge_base_answer, archived_by_id: user.id, published_by_id: user.id, internal_by_id: user.id)
  1060. refs_user = Models.references('User', user.id, true)
  1061. expect(refs_user).to eq(refs_known)
  1062. user.destroy
  1063. expect { token.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1064. expect { online_notification.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1065. expect { taskbar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1066. expect { user_device.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1067. expect { cti_caller_id.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1068. expect { authorization.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1069. expect { recent_view.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1070. expect { avatar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1071. expect { customer_ticket1.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1072. expect { customer_ticket2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1073. expect { customer_ticket3.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1074. expect { chat_agent_user.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1075. expect { mention.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1076. expect(mention_created_by.reload.created_by_id).not_to eq(user.id)
  1077. expect(overview.reload.user_ids).to eq([])
  1078. expect { chat_session.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1079. expect { chat_message.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1080. expect { chat_message2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1081. expect { user_two_factor_preference.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1082. expect { user_overview_sorting.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1083. # move ownership objects
  1084. expect { group.reload }.to change(group, :created_by_id).to(1)
  1085. expect { job.reload }.to change(job, :updated_by_id).to(1)
  1086. expect { ticket.reload }.to change(ticket, :owner_id).to(1)
  1087. expect { ticket_article.reload }
  1088. .to change(ticket_article, :origin_by_id).to(1)
  1089. .and change(ticket_article, :updated_by_id).to(1)
  1090. .and change(ticket_article, :created_by_id).to(1)
  1091. expect { knowledge_base_answer.reload }
  1092. .to change(knowledge_base_answer, :archived_by_id).to(1)
  1093. .and change(knowledge_base_answer, :published_by_id).to(1)
  1094. .and change(knowledge_base_answer, :internal_by_id).to(1)
  1095. expect { user_created_by.reload }
  1096. .to change(user_created_by, :created_by_id).to(1)
  1097. .and change(user_created_by, :updated_by_id).to(1)
  1098. .and change(user_created_by, :out_of_office_replacement_id).to(1)
  1099. expect { draft_start.reload }.to change(draft_start, :created_by_id).to(1)
  1100. expect { draft_zoom.reload }.to change(draft_zoom, :created_by_id).to(1)
  1101. expect { invalid_user.reload }.to change(invalid_user, :created_by_id).to(1)
  1102. expect { public_link.reload }.to change(public_link, :created_by_id).to(1)
  1103. end
  1104. it 'does delete cache after user deletion' do
  1105. online_notification = create(:online_notification, created_by_id: user.id)
  1106. online_notification.attributes_with_association_ids
  1107. user.destroy
  1108. expect(online_notification.reload.attributes_with_association_ids['created_by_id']).to eq(1)
  1109. end
  1110. it 'does return an exception on blocking dependencies' do
  1111. expect { user.send(:destroy_move_dependency_ownership) }.to raise_error(RuntimeError, 'Failed deleting references! Check logic for UserGroup->user_id.')
  1112. end
  1113. describe '#organization' do
  1114. describe 'email domain-based assignment' do
  1115. subject(:user) { build(:user) }
  1116. context 'when not set on creation' do
  1117. before { user.assign_attributes(organization: nil) }
  1118. context 'and #email domain matches an existing Organization#domain' do
  1119. before { user.assign_attributes(email: 'user@example.com') }
  1120. let(:organization) { create(:organization, domain: 'example.com') }
  1121. context 'and Organization#domain_assignment is false (default)' do
  1122. before { organization.update(domain_assignment: false) }
  1123. it 'remains nil' do
  1124. expect { user.save }.not_to change(user, :organization)
  1125. end
  1126. end
  1127. context 'and Organization#domain_assignment is true' do
  1128. before { organization.update(domain_assignment: true) }
  1129. it 'is automatically set to matching Organization' do
  1130. expect { user.save }
  1131. .to change(user, :organization).to(organization)
  1132. end
  1133. end
  1134. end
  1135. context 'and #email domain doesn’t match any Organization#domain' do
  1136. before { user.assign_attributes(email: 'user@example.net') }
  1137. let(:organization) { create(:organization, domain: 'example.com') }
  1138. context 'and Organization#domain_assignment is true' do
  1139. before { organization.update(domain_assignment: true) }
  1140. it 'remains nil' do
  1141. expect { user.save }.not_to change(user, :organization)
  1142. end
  1143. end
  1144. end
  1145. end
  1146. context 'when set on creation' do
  1147. before { user.assign_attributes(organization: specified_organization) }
  1148. let(:specified_organization) { create(:organization, domain: 'example.net') }
  1149. context 'and #email domain matches a DIFFERENT Organization#domain' do
  1150. before { user.assign_attributes(email: 'user@example.com') }
  1151. let!(:matching_organization) { create(:organization, domain: 'example.com') }
  1152. context 'and Organization#domain_assignment is true' do
  1153. before { matching_organization.update(domain_assignment: true) }
  1154. it 'is NOT automatically set to matching Organization' do
  1155. expect { user.save }
  1156. .not_to change(user, :organization).from(specified_organization)
  1157. end
  1158. end
  1159. end
  1160. end
  1161. end
  1162. end
  1163. end
  1164. describe 'Callbacks, Observers, & Async Transactions -' do
  1165. describe 'System-wide agent limit checks:' do
  1166. let(:agent_role) { Role.lookup(name: 'Agent') }
  1167. let(:admin_role) { Role.lookup(name: 'Admin') }
  1168. let(:current_agents) { described_class.with_permissions('ticket.agent') }
  1169. describe '#validate_agent_limit_by_role' do
  1170. context 'for Integer value of system_agent_limit' do
  1171. context 'before exceeding the agent limit' do
  1172. before { Setting.set('system_agent_limit', current_agents.count + 1) }
  1173. it 'grants agent creation' do
  1174. expect { create(:agent) }
  1175. .to change(current_agents, :count).by(1)
  1176. end
  1177. it 'grants role change' do
  1178. future_agent = create(:customer)
  1179. expect { future_agent.roles = [agent_role] }
  1180. .to change(current_agents, :count).by(1)
  1181. end
  1182. describe 'role updates' do
  1183. let(:agent) { create(:agent) }
  1184. it 'grants update by instances' do
  1185. expect { agent.roles = [admin_role, agent_role] }
  1186. .not_to raise_error
  1187. end
  1188. it 'grants update by id (Integer)' do
  1189. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  1190. .not_to raise_error
  1191. end
  1192. it 'grants update by id (String)' do
  1193. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  1194. .not_to raise_error
  1195. end
  1196. end
  1197. end
  1198. context 'when exceeding the agent limit' do
  1199. it 'creation of new agents' do
  1200. Setting.set('system_agent_limit', current_agents.count + 2)
  1201. create_list(:agent, 2)
  1202. expect { create(:agent) }
  1203. .to raise_error(Exceptions::UnprocessableEntity)
  1204. .and not_change(current_agents, :count)
  1205. end
  1206. it 'prevents role change' do
  1207. Setting.set('system_agent_limit', current_agents.count)
  1208. future_agent = create(:customer)
  1209. expect { future_agent.roles = [agent_role] }
  1210. .to raise_error(Exceptions::UnprocessableEntity)
  1211. .and not_change(current_agents, :count)
  1212. end
  1213. end
  1214. end
  1215. context 'for String value of system_agent_limit' do
  1216. context 'before exceeding the agent limit' do
  1217. before { Setting.set('system_agent_limit', (current_agents.count + 1).to_s) }
  1218. it 'grants agent creation' do
  1219. expect { create(:agent) }
  1220. .to change(current_agents, :count).by(1)
  1221. end
  1222. it 'grants role change' do
  1223. future_agent = create(:customer)
  1224. expect { future_agent.roles = [agent_role] }
  1225. .to change(current_agents, :count).by(1)
  1226. end
  1227. describe 'role updates' do
  1228. let(:agent) { create(:agent) }
  1229. it 'grants update by instances' do
  1230. expect { agent.roles = [admin_role, agent_role] }
  1231. .not_to raise_error
  1232. end
  1233. it 'grants update by id (Integer)' do
  1234. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  1235. .not_to raise_error
  1236. end
  1237. it 'grants update by id (String)' do
  1238. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  1239. .not_to raise_error
  1240. end
  1241. end
  1242. end
  1243. context 'when exceeding the agent limit' do
  1244. it 'creation of new agents' do
  1245. Setting.set('system_agent_limit', (current_agents.count + 2).to_s)
  1246. create_list(:agent, 2)
  1247. expect { create(:agent) }
  1248. .to raise_error(Exceptions::UnprocessableEntity)
  1249. .and not_change(current_agents, :count)
  1250. end
  1251. it 'prevents role change' do
  1252. Setting.set('system_agent_limit', current_agents.count.to_s)
  1253. future_agent = create(:customer)
  1254. expect { future_agent.roles = [agent_role] }
  1255. .to raise_error(Exceptions::UnprocessableEntity)
  1256. .and not_change(current_agents, :count)
  1257. end
  1258. end
  1259. end
  1260. end
  1261. describe '#validate_agent_limit_by_attributes' do
  1262. context 'for Integer value of system_agent_limit' do
  1263. before { Setting.set('system_agent_limit', current_agents.count) }
  1264. context 'when exceeding the agent limit' do
  1265. it 'prevents re-activation of agents' do
  1266. inactive_agent = create(:agent, active: false)
  1267. expect { inactive_agent.update!(active: true) }
  1268. .to raise_error(Exceptions::UnprocessableEntity)
  1269. .and not_change(current_agents, :count)
  1270. end
  1271. end
  1272. end
  1273. context 'for String value of system_agent_limit' do
  1274. before { Setting.set('system_agent_limit', current_agents.count.to_s) }
  1275. context 'when exceeding the agent limit' do
  1276. it 'prevents re-activation of agents' do
  1277. inactive_agent = create(:agent, active: false)
  1278. expect { inactive_agent.update!(active: true) }
  1279. .to raise_error(Exceptions::UnprocessableEntity)
  1280. .and not_change(current_agents, :count)
  1281. end
  1282. end
  1283. end
  1284. end
  1285. end
  1286. describe 'Touching associations on update:' do
  1287. subject!(:user) { create(:customer) }
  1288. let!(:organization) { create(:organization) }
  1289. context 'when a customer gets a organization' do
  1290. it 'touches its organization' do
  1291. expect { user.update(organization: organization) }
  1292. .to change { organization.reload.updated_at }
  1293. end
  1294. end
  1295. end
  1296. describe 'Cti::CallerId syncing:' do
  1297. context 'with a #phone attribute' do
  1298. subject(:user) { build(:user, phone: '1234567890') }
  1299. it 'adds CallerId record on creation (via Cti::CallerId.build)' do
  1300. expect(Cti::CallerId).to receive(:build).with(user)
  1301. user.save
  1302. end
  1303. it 'does not update CallerId record on touch/update (via Cti::CallerId.build)' do
  1304. expect(Cti::CallerId).to receive(:build).with(user)
  1305. user.save
  1306. expect(Cti::CallerId).not_to receive(:build).with(user)
  1307. user.touch
  1308. end
  1309. it 'destroys CallerId record on deletion' do
  1310. user.save
  1311. expect { user.destroy }
  1312. .to change(Cti::CallerId, :count).by(-1)
  1313. end
  1314. end
  1315. end
  1316. describe 'Cti::Log syncing:' do
  1317. context 'with existing Log records', performs_jobs: true do
  1318. context 'for incoming calls from an unknown number' do
  1319. let!(:log) { create(:'cti/log', :with_preferences, from: '1234567890', direction: 'in') }
  1320. context 'when creating a new user with that number' do
  1321. subject(:user) { build(:user, phone: log.from) }
  1322. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1323. expect do
  1324. user.save
  1325. perform_enqueued_jobs commit_transaction: true
  1326. end.to change { log.reload.preferences[:from]&.first }
  1327. .to(hash_including('caller_id' => user.phone))
  1328. end
  1329. end
  1330. context 'when updating a user with that number' do
  1331. subject(:user) { create(:user) }
  1332. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1333. expect do
  1334. user.update(phone: log.from)
  1335. perform_enqueued_jobs commit_transaction: true
  1336. end.to change { log.reload.preferences[:from]&.first }
  1337. .to(hash_including('object' => 'User', 'o_id' => user.id))
  1338. end
  1339. end
  1340. context 'when creating a new user with an empty number' do
  1341. subject(:user) { build(:user, phone: '') }
  1342. it 'does not modify any Log records' do
  1343. expect do
  1344. user.save
  1345. perform_enqueued_jobs commit_transaction: true
  1346. end.not_to change { log.reload.attributes }
  1347. end
  1348. end
  1349. context 'when creating a new user with no number' do
  1350. subject(:user) { build(:user, phone: nil) }
  1351. it 'does not modify any Log records' do
  1352. expect do
  1353. user.save
  1354. perform_enqueued_jobs commit_transaction: true
  1355. end.not_to change { log.reload.attributes }
  1356. end
  1357. end
  1358. end
  1359. context 'for incoming calls from the given user' do
  1360. subject(:user) { create(:user, phone: '1234567890') }
  1361. let!(:logs) { create_list(:'cti/log', 5, :with_preferences, from: user.phone, direction: 'in') }
  1362. context 'when updating #phone attribute' do
  1363. context 'to another number' do
  1364. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1365. expect do
  1366. user.update(phone: '0123456789')
  1367. perform_enqueued_jobs commit_transaction: true
  1368. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1369. .to(Array.new(5) { nil })
  1370. end
  1371. end
  1372. context 'to an empty string' do
  1373. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1374. expect do
  1375. user.update(phone: '')
  1376. perform_enqueued_jobs commit_transaction: true
  1377. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1378. .to(Array.new(5) { nil })
  1379. end
  1380. end
  1381. context 'to nil' do
  1382. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1383. expect do
  1384. user.update(phone: nil)
  1385. perform_enqueued_jobs commit_transaction: true
  1386. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1387. .to(Array.new(5) { nil })
  1388. end
  1389. end
  1390. end
  1391. context 'when updating attributes other than #phone' do
  1392. it 'does not modify any Log records' do
  1393. expect do
  1394. user.update(mobile: '2345678901')
  1395. perform_enqueued_jobs commit_transaction: true
  1396. end.not_to change { logs.map { |x| x.reload.attributes } }
  1397. end
  1398. end
  1399. end
  1400. end
  1401. end
  1402. end
  1403. describe 'Assign user to multiple organizations #1573' do
  1404. context 'when importing users via csv' do
  1405. let(:organization1) { create(:organization) }
  1406. let(:organization2) { create(:organization) }
  1407. let(:organization3) { create(:organization) }
  1408. let(:organization4) { create(:organization) }
  1409. let(:user) { create(:agent, organization: organization1, organizations: [organization2, organization3]) }
  1410. def csv_import(string)
  1411. User.csv_import(
  1412. string: string,
  1413. parse_params: {
  1414. col_sep: ',',
  1415. },
  1416. try: false,
  1417. delete: false,
  1418. )
  1419. end
  1420. before do
  1421. user
  1422. end
  1423. it 'does not change user on re-import' do
  1424. expect { csv_import(described_class.csv_example) }.not_to change { user.reload.updated_at }
  1425. end
  1426. it 'does not change user on different organization order' do
  1427. string = described_class.csv_example
  1428. string.sub!(organization3.name, organization2.name)
  1429. string.sub!(organization2.name, organization3.name)
  1430. expect { csv_import(string) }.not_to change { user.reload.updated_at }
  1431. end
  1432. it 'does change user on different organizations' do
  1433. string = described_class.csv_example
  1434. string.sub!(organization2.name, organization4.name)
  1435. expect { csv_import(string) }.to change { user.reload.updated_at }
  1436. end
  1437. end
  1438. context 'when creating users' do
  1439. it 'does not allow creation without primary organization but secondary organizations' do
  1440. expect { create(:agent, organization: nil, organizations: create_list(:organization, 1)) }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Secondary organizations are only allowed when the primary organization is given.')
  1441. end
  1442. it 'does not allow creation with more than 250 organizations' do
  1443. 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.')
  1444. end
  1445. end
  1446. end
  1447. describe 'Check default agent notifications preferences' do
  1448. context 'when creating users' do
  1449. it 'does apply default agent notification to agent preferences' do
  1450. user = create(:agent)
  1451. expect(user.reload.preferences[:notification_config][:matrix]).to eq(Setting.get('ticket_agent_default_notifications'))
  1452. end
  1453. it 'does not apply default agent notification to customer preferences' do
  1454. user = create(:customer)
  1455. expect(user.reload.preferences[:notification_config]).to be_blank
  1456. end
  1457. end
  1458. context 'when adding role to existing user' do
  1459. it 'does apply default agent notification to agent preferences (without "ticket.agent" permission before)' do
  1460. future_agent = create(:customer)
  1461. expect { future_agent.roles = [Role.lookup(name: 'Agent')] }
  1462. .to change { future_agent.reload.preferences.dig('notification_config', 'matrix') }
  1463. .to Setting.get('ticket_agent_default_notifications')
  1464. end
  1465. it 'does not apply default agent notification to agent preferences (with "ticket.agent" permission before)' do
  1466. agent = create(:agent)
  1467. expect { agent.roles = [Role.lookup(name: 'Customer')] }
  1468. .not_to change { agent.reload.preferences.dig('notification_config', 'matrix') }
  1469. end
  1470. end
  1471. end
  1472. end