user_spec.rb 74 KB

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