user_spec.rb 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  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. require 'models/concerns/has_two_factor_examples'
  18. RSpec.describe User, type: :model do
  19. subject(:user) { create(:user) }
  20. let(:customer) { create(:customer) }
  21. let(:agent) { create(:agent) }
  22. let(:admin) { create(:admin) }
  23. it_behaves_like 'ApplicationModel',
  24. can_assets: { associations: :organization },
  25. can_param: { sample_data_attribute: :email }
  26. it_behaves_like 'HasGroups', group_access_factory: :agent
  27. it_behaves_like 'HasHistory'
  28. it_behaves_like 'HasRoles', group_access_factory: :agent
  29. it_behaves_like 'HasXssSanitizedNote', model_factory: :user
  30. it_behaves_like 'HasImageSanitizedNote', model_factory: :user
  31. it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
  32. it_behaves_like 'CanBeImported'
  33. # it_behaves_like 'CanCsvImport', unique_attributes: 'email'
  34. include_examples 'CanCsvImport - User specific tests'
  35. it_behaves_like 'HasObjectManagerAttributes'
  36. it_behaves_like 'CanLookupSearchIndexAttributes'
  37. it_behaves_like 'HasTaskbars'
  38. it_behaves_like 'UserPerformsGeoLookup'
  39. it_behaves_like 'Association clears cache', association: :roles
  40. it_behaves_like 'Association clears cache', association: :organizations
  41. it_behaves_like 'User::HasTwoFactor'
  42. describe 'Class methods:' do
  43. describe '.identify' do
  44. it 'returns users by given login' do
  45. expect(described_class.identify(user.login)).to eq(user)
  46. end
  47. it 'returns users by given email' do
  48. expect(described_class.identify(user.email)).to eq(user)
  49. end
  50. it 'returns nil for empty username' do
  51. expect(described_class.identify('')).to be_nil
  52. end
  53. end
  54. describe '.reset_notifications_preferences!' do
  55. let(:sample_notifications) { { sample_notifications: true } }
  56. def change_setting_ticket_agent_default_notifications
  57. Setting.set('ticket_agent_default_notifications', sample_notifications)
  58. end
  59. context 'when user is agent' do
  60. before do
  61. # Create the agent, before the default notifications are set, so
  62. agent
  63. change_setting_ticket_agent_default_notifications
  64. end
  65. it 'changes existing matrix' do
  66. expect { described_class.reset_notifications_preferences!(agent) }
  67. .to change { agent.preferences.dig('notification_config', 'matrix') }
  68. .to sample_notifications
  69. end
  70. it 'sets matrix if preferences are empty' do
  71. agent.update_columns preferences: nil
  72. expect { described_class.reset_notifications_preferences!(agent) }
  73. .to change { agent.preferences&.dig('notification_config', 'matrix') }
  74. .to(sample_notifications)
  75. .from(nil)
  76. end
  77. it 'does not touch selected groups do' do
  78. agent.preferences['notification_config']['group_ids'] = ['123']
  79. agent.save!
  80. expect { described_class.reset_notifications_preferences!(agent) }
  81. .not_to change { agent.preferences&.dig('notification_config', 'group_ids') }
  82. end
  83. end
  84. context 'when user is not agent' do
  85. before do
  86. # Create the customer, before the default notifications are set, so
  87. customer
  88. change_setting_ticket_agent_default_notifications
  89. end
  90. it 'does not change existing matrix' do
  91. expect { described_class.reset_notifications_preferences!(customer) }
  92. .not_to change { customer.preferences.dig('notification_config', 'matrix') }
  93. end
  94. it 'sets matrix if preferences are empty' do
  95. customer.update_columns preferences: nil
  96. expect { described_class.reset_notifications_preferences!(customer) }
  97. .not_to change { customer.preferences&.dig('notification_config', 'matrix') }
  98. .from(nil)
  99. end
  100. end
  101. end
  102. describe '.by_mobile' do
  103. let!(:user) { create(:customer, mobile: saved_mobile) }
  104. let(:saved_mobile) { '+4912341234' }
  105. context 'with a number saved with prefixed +' do
  106. context 'searching for the same mobile number' do
  107. it 'finds the user (by direct lookup)' do
  108. expect(described_class.by_mobile(number: saved_mobile)).to eq(user)
  109. end
  110. end
  111. context 'searching for the E.164 number without prefixed +' do
  112. it 'finds the user (through CTI lookup)' do
  113. expect(described_class.by_mobile(number: '4912341234')).to eq(user)
  114. end
  115. end
  116. end
  117. context 'with a number saved without prefixed +' do
  118. let(:saved_mobile) { '4912341234' }
  119. context 'searching for the same mobile number' do
  120. it 'finds the user (by direct lookup)' do
  121. expect(described_class.by_mobile(number: saved_mobile)).to eq(user)
  122. end
  123. end
  124. context 'searching for the number prefixed with +' do
  125. it 'finds the user (through CTI lookup)' do
  126. expect(described_class.by_mobile(number: '+4912341234')).to eq(user)
  127. end
  128. end
  129. end
  130. context 'with a non-matching number' do
  131. it 'does not find the user' do
  132. expect(described_class.by_mobile(number: '99999999999')).to be_nil
  133. end
  134. end
  135. end
  136. end
  137. describe 'Instance methods:' do
  138. describe '#by_reset_token' do
  139. subject(:user) { token.user }
  140. let(:token) { create(:token_password_reset) }
  141. context 'with a valid token' do
  142. it 'returns the matching user' do
  143. expect(described_class.by_reset_token(token.token)).to eq(user)
  144. end
  145. end
  146. context 'with an invalid token' do
  147. it 'returns nil' do
  148. expect(described_class.by_reset_token('not-existing')).to be_nil
  149. end
  150. end
  151. end
  152. describe '#password_reset_via_token' do
  153. subject(:user) { token.user }
  154. let!(:token) { create(:token_password_reset) }
  155. it 'changes the password of the token user and destroys the token' do
  156. expect { described_class.password_reset_via_token(token.token, 'VYxesRc6O2') }
  157. .to change { user.reload.password }
  158. .and change(Token, :count).by(-1)
  159. end
  160. end
  161. describe '#admin_password_auth_new_token' do
  162. context 'with user role agent' do
  163. subject(:user) { create(:agent) }
  164. it 'returns no token' do
  165. expect(described_class.admin_password_auth_new_token(user.login)).to be_nil
  166. end
  167. end
  168. context 'with user role admin' do
  169. subject(:user) { create(:admin) }
  170. it 'returns token' do
  171. expect(described_class.admin_password_auth_new_token(user.login).keys).to include(:user, :token)
  172. end
  173. it 'delete existing tokens when creating multiple times' do
  174. described_class.admin_password_auth_new_token(user.login)
  175. described_class.admin_password_auth_new_token(user.login)
  176. expect(Token.where(action: 'AdminAuth', user_id: user.id).count).to eq(1)
  177. end
  178. end
  179. end
  180. describe '#admin_password_auth_via_token' do
  181. context 'with invalid token' do
  182. it 'returns nil' do
  183. expect(described_class.admin_password_auth_via_token('not-existing')).to be_nil
  184. end
  185. end
  186. context 'with valid token' do
  187. let(:user) { create(:admin) }
  188. it 'returns the matching user' do
  189. result = described_class.admin_password_auth_new_token(user.login)
  190. token = result[:token].token
  191. expect(described_class.admin_password_auth_via_token(token)).to match(user)
  192. end
  193. it 'destroys token' do
  194. result = described_class.admin_password_auth_new_token(user.login)
  195. token = result[:token].token
  196. expect { described_class.admin_password_auth_via_token(token) }.to change(Token, :count).by(-1)
  197. end
  198. end
  199. end
  200. describe '#locale' do
  201. subject(:user) { create(:user, preferences: preferences) }
  202. context 'with no #preferences[:locale]' do
  203. let(:preferences) { {} }
  204. context 'with default locale' do
  205. before { Setting.set('locale_default', 'foo') }
  206. it 'returns the system-wide default locale' do
  207. expect(user.locale).to eq('foo')
  208. end
  209. end
  210. context 'without default locale' do
  211. before { Setting.set('locale_default', nil) }
  212. it 'returns en-us' do
  213. expect(user.locale).to eq('en-us')
  214. end
  215. end
  216. end
  217. context 'with a #preferences[:locale]' do
  218. let(:preferences) { { locale: 'bar' } }
  219. it 'returns the user’s configured locale' do
  220. expect(user.locale).to eq('bar')
  221. end
  222. end
  223. end
  224. describe '#check_login' do
  225. let(:agent) { create(:agent) }
  226. it 'does use the origin login' do
  227. new_agent = create(:agent)
  228. expect(new_agent.login).not_to end_with('1')
  229. end
  230. it 'does number up agent logins (1)' do
  231. new_agent = create(:agent, login: agent.login)
  232. expect(new_agent.login).to eq("#{agent.login}1")
  233. end
  234. it 'does number up agent logins (5)' do
  235. new_agent = create(:agent, login: agent.login)
  236. 4.times do
  237. new_agent = create(:agent, login: agent.login)
  238. end
  239. expect(new_agent.login).to eq("#{agent.login}5")
  240. end
  241. it 'does backup with uuid in cases of many duplicates' do
  242. new_agent = create(:agent, login: agent.login)
  243. 20.times do
  244. new_agent = create(:agent, login: agent.login)
  245. end
  246. expect(new_agent.login.sub!(agent.login, '')).to be_a_uuid
  247. end
  248. end
  249. describe '#check_name' do
  250. it 'guesses user first/last name with non-ASCII characters' do
  251. user = create(:user, firstname: 'perkūnas ąžuolas', lastname: '')
  252. expect(user).to have_attributes(firstname: 'Perkūnas', lastname: 'Ąžuolas')
  253. end
  254. end
  255. end
  256. describe 'Attributes:' do
  257. describe '#login_failed' do
  258. before { user.update(login_failed: 1) }
  259. it 'is reset to 0 when password is updated' do
  260. expect { user.update(password: Faker::Internet.password) }
  261. .to change(user, :login_failed).to(0)
  262. end
  263. end
  264. describe '#password' do
  265. let(:password) { Faker::Internet.password }
  266. context 'when set to plaintext password' do
  267. it 'hashes password before saving to DB' do
  268. user.password = password
  269. expect { user.save }
  270. .to change { PasswordHash.crypted?(user.password) }
  271. end
  272. end
  273. context 'for existing user records' do
  274. before do
  275. user.update(password: password)
  276. allow(user).to receive(:ensured_password).and_call_original
  277. end
  278. context 'when changed to empty string' do
  279. it 'keeps previous password' do
  280. expect { user.update!(password: '') }
  281. .not_to change(user, :password)
  282. end
  283. it 'calls #ensured_password' do
  284. user.update!(password: '')
  285. expect(user).to have_received(:ensured_password)
  286. end
  287. end
  288. context 'when changed to nil' do
  289. it 'keeps previous password' do
  290. expect { user.update!(password: nil) }
  291. .not_to change(user, :password)
  292. end
  293. it 'calls #ensured_password' do
  294. user.update!(password: nil)
  295. expect(user).to have_received(:ensured_password)
  296. end
  297. end
  298. context 'when changed another attribute' do
  299. it 'keeps previous password' do
  300. expect { user.update!(email: "123#{user.email}") }
  301. .not_to change(user, :password)
  302. end
  303. it 'does not call #ensured_password' do
  304. user.update!(email: "123#{user.email}")
  305. expect(user).not_to have_received(:ensured_password)
  306. end
  307. end
  308. end
  309. context 'for new user records' do
  310. context 'when passed as an empty string' do
  311. let(:another_user) { create(:user, password: '') }
  312. it 'sets password to nil' do
  313. expect(another_user.password).to be_nil
  314. end
  315. end
  316. context 'when passed as nil' do
  317. let(:another_user) { create(:user, password: nil) }
  318. it 'sets password to nil' do
  319. expect(another_user.password).to be_nil
  320. end
  321. end
  322. end
  323. context 'when set to SHA2 digest (to facilitate OTRS imports)' do
  324. it 'does not re-hash before saving' do
  325. user.password = "{sha2}#{Digest::SHA2.hexdigest(password)}"
  326. expect { user.save }.not_to change(user, :password)
  327. end
  328. end
  329. context 'when set to Argon2 digest' do
  330. it 'does not re-hash before saving' do
  331. user.password = PasswordHash.crypt(password)
  332. expect { user.save }.not_to change(user, :password)
  333. end
  334. end
  335. context 'when creating two users with the same password' do
  336. before { user.update(password: password) }
  337. let(:another_user) { create(:user, password: password) }
  338. it 'does not generate the same password hash' do
  339. expect(user.password).not_to eq(another_user.password)
  340. end
  341. end
  342. context 'when saving a very long password' do
  343. let(:long_string) { "asd1ASDasd!#{Faker::Lorem.characters(number: 1_000)}" }
  344. it 'marks object as invalid by adding error' do
  345. user.update(password: long_string)
  346. expect(user.errors.first.full_message).to eq('Password is too long')
  347. end
  348. end
  349. end
  350. describe '#phone' do
  351. subject(:user) { create(:user, phone: orig_number) }
  352. context 'when included on create' do
  353. let(:orig_number) { '1234567890' }
  354. it 'adds corresponding CallerId record' do
  355. expect { user }
  356. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
  357. end
  358. end
  359. context 'when added on update' do
  360. let(:orig_number) { nil }
  361. let(:new_number) { '1234567890' }
  362. before { user } # create user
  363. it 'adds corresponding CallerId record' do
  364. expect { user.update(phone: new_number) }
  365. .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  366. end
  367. end
  368. context 'when falsely added on update (change: [nil, ""])' do
  369. let(:orig_number) { nil }
  370. let(:new_number) { '' }
  371. before { user } # create user
  372. it 'does not attempt to update CallerId record' do
  373. allow(Cti::CallerId).to receive(:build).with(any_args)
  374. expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
  375. .to eq(0)
  376. expect { user.update(phone: new_number) }
  377. .not_to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }
  378. expect(Cti::CallerId).not_to have_received(:build)
  379. end
  380. end
  381. context 'when removed on update' do
  382. let(:orig_number) { '1234567890' }
  383. let(:new_number) { nil }
  384. before { user } # create user
  385. it 'removes corresponding CallerId record' do
  386. expect { user.update(phone: nil) }
  387. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  388. end
  389. end
  390. context 'when changed on update' do
  391. let(:orig_number) { '1234567890' }
  392. let(:new_number) { orig_number.next }
  393. before { user } # create user
  394. it 'replaces CallerId record' do
  395. expect { user.update(phone: new_number) }
  396. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  397. .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  398. end
  399. end
  400. end
  401. describe '#preferences' do
  402. describe '"mail_delivery_failed{,_data}" keys' do
  403. before do
  404. user.update(
  405. preferences: {
  406. mail_delivery_failed: true,
  407. mail_delivery_failed_data: Time.current
  408. }
  409. )
  410. end
  411. it 'deletes "mail_delivery_failed"' do
  412. expect { user.update(email: Faker::Internet.email) }
  413. .to change { user.preferences.key?(:mail_delivery_failed) }.to(false)
  414. end
  415. it 'leaves "mail_delivery_failed_data" untouched' do
  416. expect { user.update(email: Faker::Internet.email) }
  417. .to not_change { user.preferences[:mail_delivery_failed_data] }
  418. end
  419. end
  420. end
  421. describe '#image' do
  422. describe 'when value is invalid' do
  423. let(:value) { 'Th1515n0t4v4l1dh45h' }
  424. it 'prevents create' do
  425. expect { create(:user, image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  426. end
  427. it 'prevents update' do
  428. expect { create(:user).update!(image: value) }.to raise_error(Exceptions::UnprocessableEntity, %r{#{value}})
  429. end
  430. end
  431. end
  432. describe '#image_source' do
  433. describe 'when value is invalid' do
  434. let(:value) { 'Th1515n0t4v4l1dh45h' }
  435. let(:escaped) { Regexp.escape(value) }
  436. it 'valid create' do
  437. expect(create(:user, image_source: 'https://zammad.org/avatar.png').image_source).not_to be_nil
  438. end
  439. it 'removes invalid image source of create' do
  440. expect(create(:user, image_source: value).image_source).to be_nil
  441. end
  442. it 'removes invalid image source of update' do
  443. user = create(:user)
  444. user.update!(image_source: value)
  445. expect(user.image_source).to be_nil
  446. end
  447. end
  448. end
  449. describe 'fetch_avatar_for_email', performs_jobs: true do
  450. it 'enqueues avatar job when creating a user with email' do
  451. expect { create(:user) }.to have_enqueued_job AvatarCreateJob
  452. end
  453. it 'does not enqueue avatar job when creating a user without email' do
  454. expect { create(:user, :without_email) }.not_to have_enqueued_job AvatarCreateJob
  455. end
  456. context 'with an existing user' do
  457. before do
  458. agent
  459. clear_jobs
  460. end
  461. it 'enqueues avatar job when updating a user with email' do
  462. expect { agent.update! email: 'avatar@example.com' }.to have_enqueued_job AvatarCreateJob
  463. end
  464. it 'does not enqueue avatar job when updating a user without email' do
  465. expect { agent.update! login: 'avatar_login', email: nil }.not_to have_enqueued_job AvatarCreateJob
  466. end
  467. it 'does not enqueue avatar job when updating a user having email' do
  468. expect { agent.update! firstname: 'no avatar update' }.not_to have_enqueued_job AvatarCreateJob
  469. end
  470. end
  471. end
  472. end
  473. describe 'Associations:' do
  474. subject(:user) { create(:agent, groups: [group_subject]) }
  475. let!(:group_subject) { create(:group) }
  476. it 'does remove references before destroy' do
  477. refs_known = {
  478. 'Group' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  479. 'Token' => { 'user_id' => 1 },
  480. 'Ticket::Article' => { 'created_by_id' => 1, 'updated_by_id' => 1, 'origin_by_id' => 1 },
  481. 'Ticket::StateType' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  482. 'Ticket::Article::Sender' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  483. 'Ticket::Article::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  484. 'Ticket::Article::Flag' => { 'created_by_id' => 0 },
  485. 'Ticket::Priority' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  486. 'Ticket::SharedDraftStart' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  487. 'Ticket::SharedDraftZoom' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  488. 'Ticket::TimeAccounting' => { 'created_by_id' => 0 },
  489. 'Ticket::TimeAccounting::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  490. 'Ticket::State' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  491. 'PostmasterFilter' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  492. 'PublicLink' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  493. 'User::TwoFactorPreference' => { 'created_by_id' => 1, 'updated_by_id' => 1, 'user_id' => 1 },
  494. 'OnlineNotification' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  495. 'Ticket' => { 'created_by_id' => 0, 'updated_by_id' => 0, 'owner_id' => 1, 'customer_id' => 3 },
  496. 'Template' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  497. 'Avatar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  498. 'Scheduler' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  499. 'Chat' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  500. 'HttpLog' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  501. 'EmailAddress' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  502. 'Taskbar' => { 'user_id' => 1 },
  503. 'Sla' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  504. 'UserDevice' => { 'user_id' => 1 },
  505. 'Chat::Message' => { 'created_by_id' => 1 },
  506. 'Chat::Agent' => { 'created_by_id' => 1, 'updated_by_id' => 1 },
  507. 'Chat::Session' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  508. 'Tag' => { 'created_by_id' => 0 },
  509. 'RecentView' => { 'created_by_id' => 1 },
  510. 'KnowledgeBase::Answer::Translation' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  511. 'LdapSource' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  512. 'KnowledgeBase::Answer' => { 'archived_by_id' => 1, 'published_by_id' => 1, 'internal_by_id' => 1 },
  513. 'Report::Profile' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  514. 'Package' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  515. 'Job' => { 'created_by_id' => 0, 'updated_by_id' => 1 },
  516. 'Store' => { 'created_by_id' => 0 },
  517. 'Cti::CallerId' => { 'user_id' => 1 },
  518. 'DataPrivacyTask' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  519. 'Trigger' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  520. 'Translation' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  521. 'ObjectManager::Attribute' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  522. 'User' => { 'created_by_id' => 2, 'out_of_office_replacement_id' => 1, 'updated_by_id' => 2 },
  523. 'User::OverviewSorting' => { 'created_by_id' => 0, 'updated_by_id' => 0, 'user_id' => 1 },
  524. 'Organization' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  525. 'Macro' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  526. 'CoreWorkflow' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  527. 'Mention' => { 'created_by_id' => 1, 'updated_by_id' => 0, 'user_id' => 1 },
  528. 'Channel' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  529. 'Role' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  530. 'History' => { 'created_by_id' => 6 },
  531. 'Webhook' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  532. 'Overview' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  533. 'PGPKey' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  534. 'ActivityStream' => { 'created_by_id' => 0 },
  535. 'StatsStore' => { 'created_by_id' => 0 },
  536. 'TextModule' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  537. 'Calendar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  538. 'UserGroup' => { 'user_id' => 1 },
  539. 'Signature' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  540. 'Authorization' => { 'user_id' => 1 },
  541. 'SystemReport' => { 'created_by_id' => 0 },
  542. 'Checklist' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  543. 'Checklist::Item' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  544. 'ChecklistTemplate' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  545. 'ChecklistTemplate::Item' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  546. }
  547. # delete objects
  548. token = create(:token, user: user)
  549. online_notification = create(:online_notification, user: user)
  550. taskbar = create(:taskbar, user: user)
  551. user_device = create(:user_device, user: user)
  552. cti_caller_id = create(:cti_caller_id, user: user)
  553. authorization = create(:twitter_authorization, user: user)
  554. recent_view = create(:recent_view, created_by: user)
  555. avatar = create(:avatar, o_id: user.id)
  556. overview = create(:overview, created_by_id: user.id, user_ids: [user.id])
  557. mention = build(:mention, mentionable: create(:ticket), user: user).tap { |elem| elem.save!(validate: false) }
  558. mention_created_by = build(:mention, mentionable: create(:ticket), user: create(:agent), created_by: user).tap { |elem| elem.save!(validate: false) }
  559. user_created_by = create(:customer, created_by_id: user.id, updated_by_id: user.id, out_of_office_replacement_id: user.id)
  560. chat_session = create(:'chat/session', user: user)
  561. chat_message = create(:'chat/message', chat_session: chat_session)
  562. chat_message2 = create(:'chat/message', chat_session: chat_session, created_by: user)
  563. draft_start = create(:ticket_shared_draft_start, created_by: user)
  564. draft_zoom = create(:ticket_shared_draft_zoom, created_by: user)
  565. public_link = create(:public_link, created_by: user)
  566. user_two_factor_preference = create(:user_two_factor_preference, :authenticator_app, user: user)
  567. user_overview_sorting = create(:'user/overview_sorting', user: user)
  568. expect(overview.reload.user_ids).to eq([user.id])
  569. # create a chat agent for admin user (id=1) before agent user
  570. # to be sure that the data gets removed and not mapped which
  571. # would result in a foreign key because of the unique key on the
  572. # created_by_id and updated_by_id.
  573. create(:'chat/agent')
  574. chat_agent_user = create(:'chat/agent', created_by_id: user.id, updated_by_id: user.id)
  575. # invalid user (by email) which has been updated by the user which
  576. # will get deleted (#3935)
  577. invalid_user = build(:user, email: 'abc', created_by_id: user.id, updated_by_id: user.id)
  578. invalid_user.save!(validate: false)
  579. # move ownership objects
  580. group = create(:group, created_by_id: user.id)
  581. job = create(:job, updated_by_id: user.id)
  582. ticket = create(:ticket, group: group_subject, owner: user)
  583. ticket_article = create(:ticket_article, ticket: ticket, created_by_id: user.id, updated_by_id: user.id, origin_by_id: user.id)
  584. customer_ticket1 = create(:ticket, group: group_subject, customer: user)
  585. customer_ticket2 = create(:ticket, group: group_subject, customer: user)
  586. customer_ticket3 = create(:ticket, group: group_subject, customer: user)
  587. knowledge_base_answer = create(:knowledge_base_answer, archived_by_id: user.id, published_by_id: user.id, internal_by_id: user.id)
  588. refs_user = Models.references('User', user.id, true)
  589. expect(refs_user).to eq(refs_known)
  590. user.destroy
  591. expect { token.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  592. expect { online_notification.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  593. expect { taskbar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  594. expect { user_device.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  595. expect { cti_caller_id.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  596. expect { authorization.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  597. expect { recent_view.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  598. expect { avatar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  599. expect { customer_ticket1.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  600. expect { customer_ticket2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  601. expect { customer_ticket3.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  602. expect { chat_agent_user.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  603. expect { mention.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  604. expect(mention_created_by.reload.created_by_id).not_to eq(user.id)
  605. expect(overview.reload.user_ids).to eq([])
  606. expect { chat_session.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  607. expect { chat_message.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  608. expect { chat_message2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  609. expect { user_two_factor_preference.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  610. expect { user_overview_sorting.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  611. # move ownership objects
  612. expect { group.reload }.to change(group, :created_by_id).to(1)
  613. expect { job.reload }.to change(job, :updated_by_id).to(1)
  614. expect { ticket.reload }.to change(ticket, :owner_id).to(1)
  615. expect { ticket_article.reload }
  616. .to change(ticket_article, :origin_by_id).to(1)
  617. .and change(ticket_article, :updated_by_id).to(1)
  618. .and change(ticket_article, :created_by_id).to(1)
  619. expect { knowledge_base_answer.reload }
  620. .to change(knowledge_base_answer, :archived_by_id).to(1)
  621. .and change(knowledge_base_answer, :published_by_id).to(1)
  622. .and change(knowledge_base_answer, :internal_by_id).to(1)
  623. expect { user_created_by.reload }
  624. .to change(user_created_by, :created_by_id).to(1)
  625. .and change(user_created_by, :updated_by_id).to(1)
  626. .and change(user_created_by, :out_of_office_replacement_id).to(1)
  627. expect { draft_start.reload }.to change(draft_start, :created_by_id).to(1)
  628. expect { draft_zoom.reload }.to change(draft_zoom, :created_by_id).to(1)
  629. expect { invalid_user.reload }.to change(invalid_user, :created_by_id).to(1)
  630. expect { public_link.reload }.to change(public_link, :created_by_id).to(1)
  631. end
  632. it 'does delete cache after user deletion' do
  633. online_notification = create(:online_notification, created_by_id: user.id)
  634. online_notification.attributes_with_association_ids
  635. user.destroy
  636. expect(online_notification.reload.attributes_with_association_ids['created_by_id']).to eq(1)
  637. end
  638. it 'does return an exception on blocking dependencies' do
  639. expect { user.send(:destroy_move_dependency_ownership) }.to raise_error(RuntimeError, 'Failed deleting references! Check logic for UserGroup->user_id.')
  640. end
  641. describe '#organization' do
  642. describe 'email domain-based assignment' do
  643. subject(:user) { build(:user) }
  644. context 'when not set on creation' do
  645. before { user.assign_attributes(organization: nil) }
  646. context 'and #email domain matches an existing Organization#domain' do
  647. before { user.assign_attributes(email: 'user@example.com') }
  648. let(:organization) { create(:organization, domain: 'example.com') }
  649. context 'and Organization#domain_assignment is false (default)' do
  650. before { organization.update(domain_assignment: false) }
  651. it 'remains nil' do
  652. expect { user.save }.not_to change(user, :organization)
  653. end
  654. end
  655. context 'and Organization#domain_assignment is true' do
  656. before { organization.update(domain_assignment: true) }
  657. it 'is automatically set to matching Organization' do
  658. expect { user.save }
  659. .to change(user, :organization).to(organization)
  660. end
  661. end
  662. end
  663. context 'and #email domain doesn’t match any Organization#domain' do
  664. before { user.assign_attributes(email: 'user@example.net') }
  665. let(:organization) { create(:organization, domain: 'example.com') }
  666. context 'and Organization#domain_assignment is true' do
  667. before { organization.update(domain_assignment: true) }
  668. it 'remains nil' do
  669. expect { user.save }.not_to change(user, :organization)
  670. end
  671. end
  672. end
  673. end
  674. context 'when set on creation' do
  675. before { user.assign_attributes(organization: specified_organization) }
  676. let(:specified_organization) { create(:organization, domain: 'example.net') }
  677. context 'and #email domain matches a DIFFERENT Organization#domain' do
  678. before { user.assign_attributes(email: 'user@example.com') }
  679. let!(:matching_organization) { create(:organization, domain: 'example.com') }
  680. context 'and Organization#domain_assignment is true' do
  681. before { matching_organization.update(domain_assignment: true) }
  682. it 'is NOT automatically set to matching Organization' do
  683. expect { user.save }
  684. .not_to change(user, :organization).from(specified_organization)
  685. end
  686. end
  687. end
  688. end
  689. end
  690. end
  691. end
  692. describe 'Callbacks, Observers, & Async Transactions -' do
  693. describe 'System-wide agent limit checks:' do
  694. let(:agent_role) { Role.lookup(name: 'Agent') }
  695. let(:admin_role) { Role.lookup(name: 'Admin') }
  696. let(:current_agents) { described_class.with_permissions('ticket.agent') }
  697. describe '#validate_agent_limit_by_role' do
  698. context 'for Integer value of system_agent_limit' do
  699. context 'before exceeding the agent limit' do
  700. before { Setting.set('system_agent_limit', current_agents.count + 1) }
  701. it 'grants agent creation' do
  702. expect { create(:agent) }
  703. .to change(current_agents, :count).by(1)
  704. end
  705. it 'grants role change' do
  706. future_agent = create(:customer)
  707. expect { future_agent.roles = [agent_role] }
  708. .to change(current_agents, :count).by(1)
  709. end
  710. describe 'role updates' do
  711. let(:agent) { create(:agent) }
  712. it 'grants update by instances' do
  713. expect { agent.roles = [admin_role, agent_role] }
  714. .not_to raise_error
  715. end
  716. it 'grants update by id (Integer)' do
  717. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  718. .not_to raise_error
  719. end
  720. it 'grants update by id (String)' do
  721. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  722. .not_to raise_error
  723. end
  724. end
  725. end
  726. context 'when exceeding the agent limit' do
  727. it 'creation of new agents' do
  728. Setting.set('system_agent_limit', current_agents.count + 2)
  729. create_list(:agent, 2)
  730. expect { create(:agent) }
  731. .to raise_error(Exceptions::UnprocessableEntity)
  732. .and not_change(current_agents, :count)
  733. end
  734. it 'prevents role change' do
  735. Setting.set('system_agent_limit', current_agents.count)
  736. future_agent = create(:customer)
  737. expect { future_agent.roles = [agent_role] }
  738. .to raise_error(Exceptions::UnprocessableEntity)
  739. .and not_change(current_agents, :count)
  740. end
  741. end
  742. end
  743. context 'for String value of system_agent_limit' do
  744. context 'before exceeding the agent limit' do
  745. before { Setting.set('system_agent_limit', (current_agents.count + 1).to_s) }
  746. it 'grants agent creation' do
  747. expect { create(:agent) }
  748. .to change(current_agents, :count).by(1)
  749. end
  750. it 'grants role change' do
  751. future_agent = create(:customer)
  752. expect { future_agent.roles = [agent_role] }
  753. .to change(current_agents, :count).by(1)
  754. end
  755. describe 'role updates' do
  756. let(:agent) { create(:agent) }
  757. it 'grants update by instances' do
  758. expect { agent.roles = [admin_role, agent_role] }
  759. .not_to raise_error
  760. end
  761. it 'grants update by id (Integer)' do
  762. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  763. .not_to raise_error
  764. end
  765. it 'grants update by id (String)' do
  766. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  767. .not_to raise_error
  768. end
  769. end
  770. end
  771. context 'when exceeding the agent limit' do
  772. it 'creation of new agents' do
  773. Setting.set('system_agent_limit', (current_agents.count + 2).to_s)
  774. create_list(:agent, 2)
  775. expect { create(:agent) }
  776. .to raise_error(Exceptions::UnprocessableEntity)
  777. .and not_change(current_agents, :count)
  778. end
  779. it 'prevents role change' do
  780. Setting.set('system_agent_limit', current_agents.count.to_s)
  781. future_agent = create(:customer)
  782. expect { future_agent.roles = [agent_role] }
  783. .to raise_error(Exceptions::UnprocessableEntity)
  784. .and not_change(current_agents, :count)
  785. end
  786. end
  787. context 'when limit was exceeded but users where removed' do
  788. let(:agent_1) { create(:agent) }
  789. let(:agent_2) { create(:agent) }
  790. before do
  791. agent_1 && agent_2
  792. Setting.set('system_agent_limit', current_agents.count)
  793. end
  794. it 'allows to create a new agent after destroying agents to be under the limit' do
  795. agent_1.destroy!
  796. agent_2.destroy!
  797. expect { create(:agent) }
  798. .not_to raise_error
  799. end
  800. end
  801. end
  802. end
  803. describe '#validate_agent_limit_by_attributes' do
  804. context 'for Integer value of system_agent_limit' do
  805. before { Setting.set('system_agent_limit', current_agents.count) }
  806. context 'when exceeding the agent limit' do
  807. it 'prevents re-activation of agents' do
  808. inactive_agent = create(:agent, active: false)
  809. expect { inactive_agent.update!(active: true) }
  810. .to raise_error(Exceptions::UnprocessableEntity)
  811. .and not_change(current_agents, :count)
  812. end
  813. end
  814. end
  815. context 'for String value of system_agent_limit' do
  816. before { Setting.set('system_agent_limit', current_agents.count.to_s) }
  817. context 'when exceeding the agent limit' do
  818. it 'prevents re-activation of agents' do
  819. inactive_agent = create(:agent, active: false)
  820. expect { inactive_agent.update!(active: true) }
  821. .to raise_error(Exceptions::UnprocessableEntity)
  822. .and not_change(current_agents, :count)
  823. end
  824. end
  825. end
  826. end
  827. end
  828. describe 'Touching associations on update:' do
  829. subject!(:user) { create(:customer) }
  830. let!(:organization) { create(:organization) }
  831. context 'when a customer gets a organization' do
  832. it 'touches its organization' do
  833. expect { user.update(organization: organization) }
  834. .to change { organization.reload.updated_at }
  835. end
  836. end
  837. end
  838. describe 'Cti::CallerId syncing:' do
  839. context 'with a #phone attribute' do
  840. subject(:user) { build(:user, phone: '1234567890') }
  841. it 'adds CallerId record on creation (via Cti::CallerId.build)' do
  842. expect(Cti::CallerId).to receive(:build).with(user)
  843. user.save
  844. end
  845. it 'does not update CallerId record on touch/update (via Cti::CallerId.build)' do
  846. expect(Cti::CallerId).to receive(:build).with(user)
  847. user.save
  848. expect(Cti::CallerId).not_to receive(:build).with(user)
  849. user.touch
  850. end
  851. it 'destroys CallerId record on deletion' do
  852. user.save
  853. expect { user.destroy }
  854. .to change(Cti::CallerId, :count).by(-1)
  855. end
  856. end
  857. end
  858. describe 'Cti::Log syncing:' do
  859. context 'with existing Log records', performs_jobs: true do
  860. context 'for incoming calls from an unknown number' do
  861. let!(:log) { create(:'cti/log', :with_preferences, from: '1234567890', direction: 'in') }
  862. context 'when creating a new user with that number' do
  863. subject(:user) { build(:user, phone: log.from) }
  864. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  865. expect do
  866. user.save
  867. perform_enqueued_jobs commit_transaction: true
  868. end.to change { log.reload.preferences[:from]&.first }
  869. .to(hash_including('caller_id' => user.phone))
  870. end
  871. end
  872. context 'when updating a user with that number' do
  873. subject(:user) { create(:user) }
  874. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  875. expect do
  876. user.update(phone: log.from)
  877. perform_enqueued_jobs commit_transaction: true
  878. end.to change { log.reload.preferences[:from]&.first }
  879. .to(hash_including('object' => 'User', 'o_id' => user.id))
  880. end
  881. end
  882. context 'when creating a new user with an empty number' do
  883. subject(:user) { build(:user, phone: '') }
  884. it 'does not modify any Log records' do
  885. expect do
  886. user.save
  887. perform_enqueued_jobs commit_transaction: true
  888. end.not_to change { log.reload.attributes }
  889. end
  890. end
  891. context 'when creating a new user with no number' do
  892. subject(:user) { build(:user, phone: nil) }
  893. it 'does not modify any Log records' do
  894. expect do
  895. user.save
  896. perform_enqueued_jobs commit_transaction: true
  897. end.not_to change { log.reload.attributes }
  898. end
  899. end
  900. end
  901. context 'for incoming calls from the given user' do
  902. subject(:user) { create(:user, phone: '1234567890') }
  903. let!(:logs) { create_list(:'cti/log', 5, :with_preferences, from: user.phone, direction: 'in') }
  904. context 'when updating #phone attribute' do
  905. context 'to another number' do
  906. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  907. expect do
  908. user.update(phone: '0123456789')
  909. perform_enqueued_jobs commit_transaction: true
  910. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  911. .to(Array.new(5) { nil })
  912. end
  913. end
  914. context 'to an empty string' do
  915. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  916. expect do
  917. user.update(phone: '')
  918. perform_enqueued_jobs commit_transaction: true
  919. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  920. .to(Array.new(5) { nil })
  921. end
  922. end
  923. context 'to nil' do
  924. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  925. expect do
  926. user.update(phone: nil)
  927. perform_enqueued_jobs commit_transaction: true
  928. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  929. .to(Array.new(5) { nil })
  930. end
  931. end
  932. end
  933. context 'when updating attributes other than #phone' do
  934. it 'does not modify any Log records' do
  935. expect do
  936. user.update(mobile: '2345678901')
  937. perform_enqueued_jobs commit_transaction: true
  938. end.not_to change { logs.map { |x| x.reload.attributes } }
  939. end
  940. end
  941. end
  942. end
  943. end
  944. end
  945. describe 'Assign user to multiple organizations #1573' do
  946. context 'when importing users via csv' do
  947. let(:organization1) { create(:organization) }
  948. let(:organization2) { create(:organization) }
  949. let(:organization3) { create(:organization) }
  950. let(:organization4) { create(:organization) }
  951. let(:user) { create(:agent, organization: organization1, organizations: [organization2, organization3]) }
  952. def csv_import(string)
  953. User.csv_import(
  954. string: string,
  955. parse_params: {
  956. col_sep: ',',
  957. },
  958. try: false,
  959. delete: false,
  960. )
  961. end
  962. before do
  963. user
  964. end
  965. it 'does not change user on re-import' do
  966. expect { csv_import(described_class.csv_example) }.not_to change { user.reload.updated_at }
  967. end
  968. it 'does not change user on different organization order' do
  969. string = described_class.csv_example
  970. string.sub!(organization3.name, organization2.name)
  971. string.sub!(organization2.name, organization3.name)
  972. expect { csv_import(string) }.not_to change { user.reload.updated_at }
  973. end
  974. it 'does change user on different organizations' do
  975. string = described_class.csv_example
  976. string.sub!(organization2.name, organization4.name)
  977. expect { csv_import(string) }.to change { user.reload.updated_at }
  978. end
  979. end
  980. context 'when creating users' do
  981. it 'does not allow creation without primary organization but secondary organizations' do
  982. 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.')
  983. end
  984. it 'does not allow creation with more than 250 organizations' do
  985. 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.')
  986. end
  987. end
  988. end
  989. describe 'Check default agent notifications preferences' do
  990. context 'when creating users' do
  991. it 'does apply default agent notification to agent preferences' do
  992. user = create(:agent)
  993. expect(user.reload.preferences[:notification_config][:matrix]).to eq(Setting.get('ticket_agent_default_notifications'))
  994. end
  995. it 'does not apply default agent notification to customer preferences' do
  996. user = create(:customer)
  997. expect(user.reload.preferences[:notification_config]).to be_blank
  998. end
  999. end
  1000. context 'when adding role to existing user' do
  1001. it 'does apply default agent notification to agent preferences (without "ticket.agent" permission before)' do
  1002. future_agent = create(:customer)
  1003. expect { future_agent.roles = [Role.lookup(name: 'Agent')] }
  1004. .to change { future_agent.reload.preferences.dig('notification_config', 'matrix') }
  1005. .to Setting.get('ticket_agent_default_notifications')
  1006. end
  1007. it 'does not apply default agent notification to agent preferences (with "ticket.agent" permission before)' do
  1008. agent = create(:agent)
  1009. expect { agent.roles = [Role.lookup(name: 'Customer')] }
  1010. .not_to change { agent.reload.preferences.dig('notification_config', 'matrix') }
  1011. end
  1012. end
  1013. end
  1014. describe 'Sanitizes name attributes for offending URLs' do
  1015. shared_examples 'sanitizing user name attributes' do |firstname, lastname|
  1016. it 'sanitizes user name attributes' do
  1017. expect(user).to have_attributes(firstname: firstname, lastname: lastname)
  1018. end
  1019. end
  1020. context 'with firstname attribute only' do
  1021. let(:user) { create(:customer, firstname: value, lastname: nil, email: Faker::Internet.unique.email) }
  1022. context 'when equaling a URL with a scheme' do
  1023. let(:value) { 'https://zammad.org/participate' }
  1024. it_behaves_like 'sanitizing user name attributes', 'zammad.org/participate'
  1025. end
  1026. context 'when equaling a URL without a scheme' do
  1027. let(:value) { 'zammad.org' }
  1028. it_behaves_like 'sanitizing user name attributes', 'zammad.org'
  1029. end
  1030. context 'when containing a URL with a scheme' do
  1031. let(:value) { 'Click here to confirm https://zammad.org/participate then log in' }
  1032. it_behaves_like 'sanitizing user name attributes', 'Click', 'here to confirm zammad.org/participate then log in'
  1033. end
  1034. context 'when containing a URL with an invalid scheme' do
  1035. let(:value) { 'A: Testing' }
  1036. it_behaves_like 'sanitizing user name attributes', 'A:', 'Testing'
  1037. end
  1038. end
  1039. context 'with lastname attribute only' do
  1040. let(:user) { create(:customer, firstname: nil, lastname: value, email: Faker::Internet.unique.email) }
  1041. context 'when equaling a URL with a scheme' do
  1042. let(:value) { 'https://zammad.org/participate' }
  1043. it_behaves_like 'sanitizing user name attributes', nil, 'zammad.org/participate'
  1044. end
  1045. context 'when equaling a URL without a scheme' do
  1046. let(:value) { 'zammad.org' }
  1047. it_behaves_like 'sanitizing user name attributes', nil, 'zammad.org'
  1048. end
  1049. context 'when containing a URL with a scheme' do
  1050. let(:value) { 'Click here to confirm https://zammad.org/participate then log in' }
  1051. it_behaves_like 'sanitizing user name attributes', 'Click', 'here to confirm zammad.org/participate then log in'
  1052. end
  1053. end
  1054. context 'with both firstname and lastname attribute' do
  1055. let(:user) { create(:customer, firstname: firstname, lastname: lastname, email: Faker::Internet.unique.email) }
  1056. context 'when equaling a URL with a scheme' do
  1057. let(:firstname) { 'Click here to confirm' }
  1058. let(:lastname) { 'https://zammad.org/participate' }
  1059. it_behaves_like 'sanitizing user name attributes', 'Click here to confirm', 'zammad.org/participate'
  1060. end
  1061. context 'when equaling a URL without a scheme' do
  1062. let(:firstname) { 'zammad.org' }
  1063. let(:lastname) { 'Foundation' }
  1064. it_behaves_like 'sanitizing user name attributes', 'zammad.org', 'Foundation'
  1065. end
  1066. context 'when containing a URL with a scheme' do
  1067. let(:firstname) { 'Click here to confirm' }
  1068. let(:lastname) { 'https://zammad.org/participate then log in' }
  1069. it_behaves_like 'sanitizing user name attributes', 'Click here to confirm', 'zammad.org/participate then log in'
  1070. end
  1071. context 'when containing a URL with an invalid scheme' do
  1072. let(:firstname) { 'Dummy R: Berlin' }
  1073. let(:lastname) { 'Mail' }
  1074. it_behaves_like 'sanitizing user name attributes', 'Dummy R: Berlin', 'Mail'
  1075. end
  1076. end
  1077. end
  1078. end