user_spec.rb 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296
  1. require 'rails_helper'
  2. require 'models/application_model_examples'
  3. require 'models/concerns/has_groups_examples'
  4. require 'models/concerns/has_history_examples'
  5. require 'models/concerns/has_roles_examples'
  6. require 'models/concerns/has_groups_permissions_examples'
  7. require 'models/concerns/has_xss_sanitized_note_examples'
  8. require 'models/concerns/can_be_imported_examples'
  9. require 'models/concerns/has_object_manager_attributes_validation_examples'
  10. RSpec.describe User, type: :model do
  11. subject(:user) { create(:user) }
  12. let(:customer) { create(:customer_user) }
  13. let(:agent) { create(:agent_user) }
  14. let(:admin) { create(:admin_user) }
  15. it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
  16. it_behaves_like 'HasGroups', group_access_factory: :agent_user
  17. it_behaves_like 'HasHistory'
  18. it_behaves_like 'HasRoles', group_access_factory: :agent_user
  19. it_behaves_like 'HasXssSanitizedNote', model_factory: :user
  20. it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
  21. it_behaves_like 'CanBeImported'
  22. it_behaves_like 'HasObjectManagerAttributesValidation'
  23. describe 'Class methods:' do
  24. describe '.authenticate' do
  25. subject(:user) { create(:user, password: password) }
  26. let(:password) { Faker::Internet.password }
  27. context 'with valid credentials' do
  28. context 'using #login' do
  29. it 'returns the matching user' do
  30. expect(described_class.authenticate(user.login, password))
  31. .to eq(user)
  32. end
  33. it 'is not case-sensitive' do
  34. expect(described_class.authenticate(user.login.upcase, password))
  35. .to eq(user)
  36. end
  37. end
  38. context 'using #email' do
  39. it 'returns the matching user' do
  40. expect(described_class.authenticate(user.email, password))
  41. .to eq(user)
  42. end
  43. it 'is not case-sensitive' do
  44. expect(described_class.authenticate(user.email.upcase, password))
  45. .to eq(user)
  46. end
  47. end
  48. context 'but exceeding failed login limit' do
  49. before { user.update(login_failed: 999) }
  50. it 'returns nil' do
  51. expect(described_class.authenticate(user.login, password))
  52. .to be(nil)
  53. end
  54. end
  55. context 'when previous login was' do
  56. context 'never' do
  57. it 'updates #last_login and #updated_at' do
  58. expect { described_class.authenticate(user.login, password) }
  59. .to change { user.reload.last_login }
  60. .and change { user.reload.updated_at }
  61. end
  62. end
  63. context 'less than 10 minutes ago' do
  64. before do
  65. described_class.authenticate(user.login, password)
  66. travel 9.minutes
  67. end
  68. it 'does not update #last_login and #updated_at' do
  69. expect { described_class.authenticate(user.login, password) }
  70. .to not_change { user.reload.last_login }
  71. .and not_change { user.reload.updated_at }
  72. end
  73. end
  74. context 'more than 10 minutes ago' do
  75. before do
  76. described_class.authenticate(user.login, password)
  77. travel 11.minutes
  78. end
  79. it 'updates #last_login and #updated_at' do
  80. expect { described_class.authenticate(user.login, password) }
  81. .to change { user.reload.last_login }
  82. .and change { user.reload.updated_at }
  83. end
  84. end
  85. end
  86. end
  87. context 'with valid user and invalid password' do
  88. it 'increments failed login count' do
  89. expect(described_class).to receive(:sleep).with(1)
  90. expect { described_class.authenticate(user.login, password.next) }
  91. .to change { user.reload.login_failed }.by(1)
  92. end
  93. it 'returns nil' do
  94. expect(described_class).to receive(:sleep).with(1)
  95. expect(described_class.authenticate(user.login, password.next)).to be(nil)
  96. end
  97. end
  98. context 'with inactive user’s login' do
  99. before { user.update(active: false) }
  100. it 'returns nil' do
  101. expect(described_class.authenticate(user.login, password)).to be(nil)
  102. end
  103. end
  104. context 'with non-existent user login' do
  105. it 'returns nil' do
  106. expect(described_class.authenticate('john.doe', password)).to be(nil)
  107. end
  108. end
  109. context 'with empty login string' do
  110. it 'returns nil' do
  111. expect(described_class.authenticate('', password)).to be(nil)
  112. end
  113. end
  114. context 'with empty password string' do
  115. it 'returns nil' do
  116. expect(described_class.authenticate(user.login, '')).to be(nil)
  117. end
  118. end
  119. context 'with empty password string when the stored password is an empty string' do
  120. before { user.update_column(:password, '') }
  121. context 'when password is an empty string' do
  122. it 'returns nil' do
  123. expect(described_class.authenticate(user.login, '')).to be(nil)
  124. end
  125. end
  126. context 'when password is nil' do
  127. it 'returns nil' do
  128. expect(described_class.authenticate(user.login, nil)).to be(nil)
  129. end
  130. end
  131. end
  132. context 'with empty password string when the stored hash represents an empty string' do
  133. before { user.update(password: PasswordHash.crypt('')) }
  134. context 'when password is an empty string' do
  135. it 'returns nil' do
  136. expect(described_class.authenticate(user.login, '')).to be(nil)
  137. end
  138. end
  139. context 'when password is nil' do
  140. it 'returns nil' do
  141. expect(described_class.authenticate(user.login, nil)).to be(nil)
  142. end
  143. end
  144. end
  145. end
  146. describe '.identify' do
  147. it 'returns users by given login' do
  148. expect(described_class.identify(user.login)).to eq(user)
  149. end
  150. it 'returns users by given email' do
  151. expect(described_class.identify(user.email)).to eq(user)
  152. end
  153. end
  154. end
  155. describe 'Instance methods:' do
  156. describe '#max_login_failed?' do
  157. it { is_expected.to respond_to(:max_login_failed?) }
  158. context 'with "password_max_login_failed" setting' do
  159. before do
  160. Setting.set('password_max_login_failed', 5)
  161. user.update(login_failed: 5)
  162. end
  163. it 'returns true once user’s #login_failed count exceeds the setting' do
  164. expect { user.update(login_failed: 6) }
  165. .to change(user, :max_login_failed?).to(true)
  166. end
  167. end
  168. context 'without password_max_login_failed setting' do
  169. before do
  170. Setting.set('password_max_login_failed', nil)
  171. user.update(login_failed: 0)
  172. end
  173. it 'defaults to 0' do
  174. expect { user.update(login_failed: 1) }
  175. .to change(user, :max_login_failed?).to(true)
  176. end
  177. end
  178. end
  179. describe '#out_of_office?' do
  180. context 'without any out_of_office_* attributes set' do
  181. it 'returns false' do
  182. expect(agent.out_of_office?).to be(false)
  183. end
  184. end
  185. context 'with valid #out_of_office_* attributes' do
  186. before do
  187. agent.update(
  188. out_of_office_start_at: Time.current.yesterday,
  189. out_of_office_end_at: Time.current.tomorrow,
  190. out_of_office_replacement_id: 1
  191. )
  192. end
  193. context 'but #out_of_office: false' do
  194. before { agent.update(out_of_office: false) }
  195. it 'returns false' do
  196. expect(agent.out_of_office?).to be(false)
  197. end
  198. end
  199. context 'and #out_of_office: true' do
  200. before { agent.update(out_of_office: true) }
  201. it 'returns true' do
  202. expect(agent.out_of_office?).to be(true)
  203. end
  204. context 'after the #out_of_office_end_at time has passed' do
  205. before { travel 2.days }
  206. it 'returns false (even though #out_of_office has not changed)' do
  207. expect(agent.out_of_office).to be(true)
  208. expect(agent.out_of_office?).to be(false)
  209. end
  210. end
  211. end
  212. end
  213. end
  214. describe '#out_of_office_agent' do
  215. it { is_expected.to respond_to(:out_of_office_agent) }
  216. context 'when user has no designated substitute' do
  217. it 'returns nil' do
  218. expect(user.out_of_office_agent).to be(nil)
  219. end
  220. end
  221. context 'when user has designated substitute' do
  222. subject(:user) do
  223. create(:user,
  224. out_of_office: out_of_office,
  225. out_of_office_start_at: Time.zone.yesterday,
  226. out_of_office_end_at: Time.zone.tomorrow,
  227. out_of_office_replacement_id: substitute.id,)
  228. end
  229. let(:substitute) { create(:user) }
  230. context 'but is not out of office' do
  231. let(:out_of_office) { false }
  232. it 'returns nil' do
  233. expect(user.out_of_office_agent).to be(nil)
  234. end
  235. end
  236. context 'and is out of office' do
  237. let(:out_of_office) { true }
  238. it 'returns the designated substitute' do
  239. expect(user.out_of_office_agent).to eq(substitute)
  240. end
  241. end
  242. end
  243. end
  244. describe '#out_of_office_agent_of' do
  245. context 'when no other agents are out-of-office' do
  246. it 'returns an empty ActiveRecord::Relation' do
  247. expect(agent.out_of_office_agent_of)
  248. .to be_an(ActiveRecord::Relation)
  249. .and be_empty
  250. end
  251. end
  252. context 'when designated as the substitute' do
  253. let!(:agent_on_holiday) do
  254. create(
  255. :agent_user,
  256. out_of_office_start_at: Time.current.yesterday,
  257. out_of_office_end_at: Time.current.tomorrow,
  258. out_of_office_replacement_id: agent.id,
  259. out_of_office: out_of_office
  260. )
  261. end
  262. context 'of an in-office agent' do
  263. let(:out_of_office) { false }
  264. it 'returns an empty ActiveRecord::Relation' do
  265. expect(agent.out_of_office_agent_of)
  266. .to be_an(ActiveRecord::Relation)
  267. .and be_empty
  268. end
  269. end
  270. context 'of an out-of-office agent' do
  271. let(:out_of_office) { true }
  272. it 'returns an ActiveRecord::Relation including that agent' do
  273. expect(agent.out_of_office_agent_of)
  274. .to match_array([agent_on_holiday])
  275. end
  276. end
  277. end
  278. end
  279. describe '#by_reset_token' do
  280. subject(:user) { token.user }
  281. let(:token) { create(:token_password_reset) }
  282. context 'with a valid token' do
  283. it 'returns the matching user' do
  284. expect(described_class.by_reset_token(token.name)).to eq(user)
  285. end
  286. end
  287. context 'with an invalid token' do
  288. it 'returns nil' do
  289. expect(described_class.by_reset_token('not-existing')).to be(nil)
  290. end
  291. end
  292. end
  293. describe '#password_reset_via_token' do
  294. subject(:user) { token.user }
  295. let!(:token) { create(:token_password_reset) }
  296. it 'changes the password of the token user and destroys the token' do
  297. expect { described_class.password_reset_via_token(token.name, Faker::Internet.password) }
  298. .to change { user.reload.password }
  299. .and change(Token, :count).by(-1)
  300. end
  301. end
  302. describe '#access?' do
  303. context 'when an admin' do
  304. subject(:user) { create(:user, roles: [partial_admin_role]) }
  305. context 'with "admin.user" privileges' do
  306. let(:partial_admin_role) do
  307. create(:role).tap { |role| role.permission_grant('admin.user') }
  308. end
  309. context 'wants to read, change, or delete any user' do
  310. it 'returns true' do
  311. expect(admin.access?(user, 'read')).to be(true)
  312. expect(admin.access?(user, 'change')).to be(true)
  313. expect(admin.access?(user, 'delete')).to be(true)
  314. expect(agent.access?(user, 'read')).to be(true)
  315. expect(agent.access?(user, 'change')).to be(true)
  316. expect(agent.access?(user, 'delete')).to be(true)
  317. expect(customer.access?(user, 'read')).to be(true)
  318. expect(customer.access?(user, 'change')).to be(true)
  319. expect(customer.access?(user, 'delete')).to be(true)
  320. expect(user.access?(user, 'read')).to be(true)
  321. expect(user.access?(user, 'change')).to be(true)
  322. expect(user.access?(user, 'delete')).to be(true)
  323. end
  324. end
  325. end
  326. context 'without "admin.user" privileges' do
  327. let(:partial_admin_role) do
  328. create(:role).tap { |role| role.permission_grant('admin.tag') }
  329. end
  330. context 'wants to read any user' do
  331. it 'returns true' do
  332. expect(admin.access?(user, 'read')).to be(true)
  333. expect(agent.access?(user, 'read')).to be(true)
  334. expect(customer.access?(user, 'read')).to be(true)
  335. expect(user.access?(user, 'read')).to be(true)
  336. end
  337. end
  338. context 'wants to change or delete any user' do
  339. it 'returns false' do
  340. expect(admin.access?(user, 'change')).to be(false)
  341. expect(admin.access?(user, 'delete')).to be(false)
  342. expect(agent.access?(user, 'change')).to be(false)
  343. expect(agent.access?(user, 'delete')).to be(false)
  344. expect(customer.access?(user, 'change')).to be(false)
  345. expect(customer.access?(user, 'delete')).to be(false)
  346. expect(user.access?(user, 'change')).to be(false)
  347. expect(user.access?(user, 'delete')).to be(false)
  348. end
  349. end
  350. end
  351. end
  352. context 'when an agent' do
  353. subject(:user) { create(:agent_user) }
  354. context 'wants to read any user' do
  355. it 'returns true' do
  356. expect(admin.access?(user, 'read')).to be(true)
  357. expect(agent.access?(user, 'read')).to be(true)
  358. expect(customer.access?(user, 'read')).to be(true)
  359. expect(user.access?(user, 'read')).to be(true)
  360. end
  361. end
  362. context 'wants to change' do
  363. context 'any admin or agent' do
  364. it 'returns false' do
  365. expect(admin.access?(user, 'change')).to be(false)
  366. expect(agent.access?(user, 'change')).to be(false)
  367. expect(user.access?(user, 'change')).to be(false)
  368. end
  369. end
  370. context 'any customer' do
  371. it 'returns true' do
  372. expect(customer.access?(user, 'change')).to be(true)
  373. end
  374. end
  375. end
  376. context 'wants to delete any user' do
  377. it 'returns false' do
  378. expect(admin.access?(user, 'delete')).to be(false)
  379. expect(agent.access?(user, 'delete')).to be(false)
  380. expect(customer.access?(user, 'delete')).to be(false)
  381. expect(user.access?(user, 'delete')).to be(false)
  382. end
  383. end
  384. end
  385. context 'when a customer' do
  386. subject(:user) { create(:customer_user, :with_org) }
  387. let(:colleague) { create(:customer_user, organization: user.organization) }
  388. context 'wants to read' do
  389. context 'any admin, agent, or customer from a different organization' do
  390. it 'returns false' do
  391. expect(admin.access?(user, 'read')).to be(false)
  392. expect(agent.access?(user, 'read')).to be(false)
  393. expect(customer.access?(user, 'read')).to be(false)
  394. end
  395. end
  396. context 'any customer from the same organization' do
  397. it 'returns true' do
  398. expect(user.access?(user, 'read')).to be(true)
  399. expect(colleague.access?(user, 'read')).to be(true)
  400. end
  401. end
  402. end
  403. context 'wants to change or delete any user' do
  404. it 'returns false' do
  405. expect(admin.access?(user, 'change')).to be(false)
  406. expect(admin.access?(user, 'delete')).to be(false)
  407. expect(agent.access?(user, 'change')).to be(false)
  408. expect(agent.access?(user, 'delete')).to be(false)
  409. expect(customer.access?(user, 'change')).to be(false)
  410. expect(customer.access?(user, 'delete')).to be(false)
  411. expect(colleague.access?(user, 'change')).to be(false)
  412. expect(colleague.access?(user, 'delete')).to be(false)
  413. expect(user.access?(user, 'change')).to be(false)
  414. expect(user.access?(user, 'delete')).to be(false)
  415. end
  416. end
  417. end
  418. end
  419. describe '#permissions?' do
  420. subject(:user) { create(:user, roles: [role]) }
  421. let(:role) { create(:role, permissions: [permission]) }
  422. let(:permission) { create(:permission, name: permission_name) }
  423. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  424. let(:permission_name) { 'foo' }
  425. context 'when given that exact permission' do
  426. it 'returns true' do
  427. expect(user.permissions?('foo')).to be(true)
  428. end
  429. end
  430. context 'when given a sub-permission (i.e., child permission)' do
  431. let(:subpermission) { create(:permission, name: 'foo.bar') }
  432. context 'that exists' do
  433. before { subpermission }
  434. it 'returns true' do
  435. expect(user.permissions?('foo.bar')).to be(true)
  436. end
  437. end
  438. context 'that is inactive' do
  439. before { subpermission.update(active: false) }
  440. it 'returns false' do
  441. expect(user.permissions?('foo.bar')).to be(false)
  442. end
  443. end
  444. context 'that does not exist' do
  445. it 'returns true' do
  446. expect(user.permissions?('foo.bar')).to be(true)
  447. end
  448. end
  449. end
  450. context 'when given a glob' do
  451. context 'matching that permission' do
  452. it 'returns true' do
  453. expect(user.permissions?('foo.*')).to be(true)
  454. end
  455. end
  456. context 'NOT matching that permission' do
  457. it 'returns false' do
  458. expect(user.permissions?('bar.*')).to be(false)
  459. end
  460. end
  461. end
  462. end
  463. context 'with privileges for a sub-permission (e.g., "foo.bar", not "foo")' do
  464. let(:permission_name) { 'foo.bar' }
  465. context 'when given that exact sub-permission' do
  466. it 'returns true' do
  467. expect(user.permissions?('foo.bar')).to be(true)
  468. end
  469. context 'but the permission is inactive' do
  470. before { permission.update(active: false) }
  471. it 'returns false' do
  472. expect(user.permissions?('foo.bar')).to be(false)
  473. end
  474. end
  475. end
  476. context 'when given a sibling sub-permission' do
  477. let(:sibling_permission) { create(:permission, name: 'foo.baz') }
  478. context 'that exists' do
  479. before { sibling_permission }
  480. it 'returns false' do
  481. expect(user.permissions?('foo.baz')).to be(false)
  482. end
  483. end
  484. context 'that does not exist' do
  485. it 'returns false' do
  486. expect(user.permissions?('foo.baz')).to be(false)
  487. end
  488. end
  489. end
  490. context 'when given the parent permission' do
  491. it 'returns false' do
  492. expect(user.permissions?('foo')).to be(false)
  493. end
  494. end
  495. context 'when given a glob' do
  496. context 'matching that sub-permission' do
  497. it 'returns true' do
  498. expect(user.permissions?('foo.*')).to be(true)
  499. end
  500. context 'but the permission is inactive' do
  501. before { permission.update(active: false) }
  502. it 'returns false' do
  503. expect(user.permissions?('foo.bar')).to be(false)
  504. end
  505. end
  506. end
  507. context 'NOT matching that sub-permission' do
  508. it 'returns false' do
  509. expect(user.permissions?('bar.*')).to be(false)
  510. end
  511. end
  512. end
  513. end
  514. end
  515. describe '#permissions_with_child_ids' do
  516. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  517. subject(:user) { create(:user, roles: [role]) }
  518. let(:role) { create(:role, permissions: [permission]) }
  519. let!(:permission) { create(:permission, name: 'foo') }
  520. let!(:child_permission) { create(:permission, name: 'foo.bar') }
  521. let!(:inactive_child_permission) { create(:permission, name: 'foo.baz', active: false) }
  522. it 'includes the IDs of user’s explicit permissions' do
  523. expect(user.permissions_with_child_ids)
  524. .to include(permission.id)
  525. end
  526. it 'includes the IDs of user’s active sub-permissions' do
  527. expect(user.permissions_with_child_ids)
  528. .to include(child_permission.id)
  529. .and not_include(inactive_child_permission.id)
  530. end
  531. end
  532. end
  533. describe '#locale' do
  534. subject(:user) { create(:user, preferences: preferences) }
  535. context 'with no #preferences[:locale]' do
  536. let(:preferences) { {} }
  537. before { Setting.set('locale_default', 'foo') }
  538. it 'returns the system-wide default locale' do
  539. expect(user.locale).to eq('foo')
  540. end
  541. end
  542. context 'with a #preferences[:locale]' do
  543. let(:preferences) { { locale: 'bar' } }
  544. it 'returns the user’s configured locale' do
  545. expect(user.locale).to eq('bar')
  546. end
  547. end
  548. end
  549. end
  550. describe 'Attributes:' do
  551. describe '#out_of_office' do
  552. context 'with #out_of_office_start_at: nil' do
  553. before { agent.update(out_of_office_start_at: nil, out_of_office_end_at: Time.current) }
  554. it 'cannot be set to true' do
  555. expect { agent.update(out_of_office: true) }
  556. .to raise_error(Exceptions::UnprocessableEntity)
  557. end
  558. end
  559. context 'with #out_of_office_end_at: nil' do
  560. before { agent.update(out_of_office_start_at: Time.current, out_of_office_end_at: nil) }
  561. it 'cannot be set to true' do
  562. expect { agent.update(out_of_office: true) }
  563. .to raise_error(Exceptions::UnprocessableEntity)
  564. end
  565. end
  566. context 'when #out_of_office_start_at is AFTER #out_of_office_end_at' do
  567. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.next_month) }
  568. it 'cannot be set to true' do
  569. expect { agent.update(out_of_office: true) }
  570. .to raise_error(Exceptions::UnprocessableEntity)
  571. end
  572. end
  573. context 'when #out_of_office_start_at is AFTER Time.current' do
  574. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.yesterday) }
  575. it 'cannot be set to true' do
  576. expect { agent.update(out_of_office: true) }
  577. .to raise_error(Exceptions::UnprocessableEntity)
  578. end
  579. end
  580. context 'when #out_of_office_end_at is BEFORE Time.current' do
  581. before { agent.update(out_of_office_start_at: Time.current.last_month, out_of_office_end_at: Time.current.yesterday) }
  582. it 'cannot be set to true' do
  583. expect { agent.update(out_of_office: true) }
  584. .to raise_error(Exceptions::UnprocessableEntity)
  585. end
  586. end
  587. end
  588. describe '#out_of_office_replacement_id' do
  589. it 'cannot be set to invalid user ID' do
  590. expect { agent.update(out_of_office_replacement_id: described_class.pluck(:id).max.next) }
  591. .to raise_error(ActiveRecord::InvalidForeignKey)
  592. end
  593. it 'can be set to a valid user ID' do
  594. expect { agent.update(out_of_office_replacement_id: 1) }
  595. .not_to raise_error
  596. end
  597. end
  598. describe '#login_failed' do
  599. before { user.update(login_failed: 1) }
  600. it 'is reset to 0 when password is updated' do
  601. expect { user.update(password: Faker::Internet.password) }
  602. .to change(user, :login_failed).to(0)
  603. end
  604. end
  605. describe '#password' do
  606. let(:password) { Faker::Internet.password }
  607. context 'when set to plaintext password' do
  608. it 'hashes password before saving to DB' do
  609. user.password = password
  610. expect { user.save }
  611. .to change { PasswordHash.crypted?(user.password) }
  612. end
  613. end
  614. context 'for existing user records' do
  615. context 'when changed to empty string' do
  616. before { user.update(password: password) }
  617. it 'keeps previous password' do
  618. expect { user.update!(password: '') }
  619. .not_to change(user, :password)
  620. end
  621. end
  622. context 'when changed to nil' do
  623. before { user.update(password: password) }
  624. it 'keeps previous password' do
  625. expect { user.update!(password: nil) }
  626. .not_to change(user, :password)
  627. end
  628. end
  629. end
  630. context 'for new user records' do
  631. context 'when passed as an empty string' do
  632. let(:another_user) { create(:user, password: '') }
  633. it 'sets password to nil' do
  634. expect(another_user.password).to eq(nil)
  635. end
  636. end
  637. context 'when passed as nil' do
  638. let(:another_user) { create(:user, password: nil) }
  639. it 'sets password to nil' do
  640. expect(another_user.password).to eq(nil)
  641. end
  642. end
  643. end
  644. context 'when set to SHA2 digest (to facilitate OTRS imports)' do
  645. it 'does not re-hash before saving' do
  646. user.password = "{sha2}#{Digest::SHA2.hexdigest(password)}"
  647. expect { user.save }.not_to change(user, :password)
  648. end
  649. end
  650. context 'when set to Argon2 digest' do
  651. it 'does not re-hash before saving' do
  652. user.password = PasswordHash.crypt(password)
  653. expect { user.save }.not_to change(user, :password)
  654. end
  655. end
  656. context 'when creating two users with the same password' do
  657. before { user.update(password: password) }
  658. let(:another_user) { create(:user, password: password) }
  659. it 'does not generate the same password hash' do
  660. expect(user.password).not_to eq(another_user.password)
  661. end
  662. end
  663. end
  664. describe '#phone' do
  665. subject(:user) { create(:user, phone: orig_number) }
  666. context 'when included on create' do
  667. let(:orig_number) { '1234567890' }
  668. it 'adds corresponding CallerId record' do
  669. expect { user }
  670. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
  671. end
  672. end
  673. context 'when added on update' do
  674. let(:orig_number) { nil }
  675. let(:new_number) { '1234567890' }
  676. before { user } # create user
  677. it 'adds corresponding CallerId record' do
  678. expect { user.update(phone: new_number) }
  679. .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  680. end
  681. end
  682. context 'when falsely added on update (change: [nil, ""])' do
  683. let(:orig_number) { nil }
  684. let(:new_number) { '' }
  685. before { user } # create user
  686. it 'does not attempt to update CallerId record' do
  687. allow(Cti::CallerId).to receive(:build).with(any_args)
  688. expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
  689. .to eq(0)
  690. expect { user.update(phone: new_number) }
  691. .to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }.by(0)
  692. expect(Cti::CallerId).not_to have_received(:build)
  693. end
  694. end
  695. context 'when removed on update' do
  696. let(:orig_number) { '1234567890' }
  697. let(:new_number) { nil }
  698. before { user } # create user
  699. it 'removes corresponding CallerId record' do
  700. expect { user.update(phone: nil) }
  701. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  702. end
  703. end
  704. context 'when changed on update' do
  705. let(:orig_number) { '1234567890' }
  706. let(:new_number) { orig_number.next }
  707. before { user } # create user
  708. it 'replaces CallerId record' do
  709. expect { user.update(phone: new_number) }
  710. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  711. .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  712. end
  713. end
  714. end
  715. describe '#preferences' do
  716. describe '"mail_delivery_failed{,_data}" keys' do
  717. before do
  718. user.update(
  719. preferences: {
  720. mail_delivery_failed: true,
  721. mail_delivery_failed_data: Time.current
  722. }
  723. )
  724. end
  725. it 'deletes "mail_delivery_failed"' do
  726. expect { user.update(email: Faker::Internet.email) }
  727. .to change { user.preferences.key?(:mail_delivery_failed) }.to(false)
  728. end
  729. it 'leaves "mail_delivery_failed_data" untouched' do
  730. expect { user.update(email: Faker::Internet.email) }
  731. .to not_change { user.preferences[:mail_delivery_failed_data] }
  732. end
  733. end
  734. end
  735. end
  736. describe 'Associations:' do
  737. describe '#organization' do
  738. describe 'email domain-based assignment' do
  739. subject(:user) { build(:user) }
  740. context 'when not set on creation' do
  741. before { user.assign_attributes(organization: nil) }
  742. context 'and #email domain matches an existing Organization#domain' do
  743. before { user.assign_attributes(email: 'user@example.com') }
  744. let(:organization) { create(:organization, domain: 'example.com') }
  745. context 'and Organization#domain_assignment is false (default)' do
  746. before { organization.update(domain_assignment: false) }
  747. it 'remains nil' do
  748. expect { user.save }.not_to change(user, :organization)
  749. end
  750. end
  751. context 'and Organization#domain_assignment is true' do
  752. before { organization.update(domain_assignment: true) }
  753. it 'is automatically set to matching Organization' do
  754. expect { user.save }
  755. .to change(user, :organization).to(organization)
  756. end
  757. end
  758. end
  759. context 'and #email domain doesn’t match any Organization#domain' do
  760. before { user.assign_attributes(email: 'user@example.net') }
  761. let(:organization) { create(:organization, domain: 'example.com') }
  762. context 'and Organization#domain_assignment is true' do
  763. before { organization.update(domain_assignment: true) }
  764. it 'remains nil' do
  765. expect { user.save }.not_to change(user, :organization)
  766. end
  767. end
  768. end
  769. end
  770. context 'when set on creation' do
  771. before { user.assign_attributes(organization: specified_organization) }
  772. let(:specified_organization) { create(:organization, domain: 'example.net') }
  773. context 'and #email domain matches a DIFFERENT Organization#domain' do
  774. before { user.assign_attributes(email: 'user@example.com') }
  775. let!(:matching_organization) { create(:organization, domain: 'example.com') }
  776. context 'and Organization#domain_assignment is true' do
  777. before { matching_organization.update(domain_assignment: true) }
  778. it 'is NOT automatically set to matching Organization' do
  779. expect { user.save }
  780. .not_to change(user, :organization).from(specified_organization)
  781. end
  782. end
  783. end
  784. end
  785. end
  786. end
  787. end
  788. describe 'Callbacks, Observers, & Async Transactions -' do
  789. describe 'System-wide agent limit checks:' do
  790. let(:agent_role) { Role.lookup(name: 'Agent') }
  791. let(:admin_role) { Role.lookup(name: 'Admin') }
  792. let(:current_agents) { described_class.with_permissions('ticket.agent') }
  793. describe '#validate_agent_limit_by_role' do
  794. context 'for Integer value of system_agent_limit' do
  795. context 'before exceeding the agent limit' do
  796. before { Setting.set('system_agent_limit', current_agents.count + 1) }
  797. it 'grants agent creation' do
  798. expect { create(:agent_user) }
  799. .to change(current_agents, :count).by(1)
  800. end
  801. it 'grants role change' do
  802. future_agent = create(:customer_user)
  803. expect { future_agent.roles = [agent_role] }
  804. .to change(current_agents, :count).by(1)
  805. end
  806. describe 'role updates' do
  807. let(:agent) { create(:agent_user) }
  808. it 'grants update by instances' do
  809. expect { agent.roles = [admin_role, agent_role] }
  810. .not_to raise_error
  811. end
  812. it 'grants update by id (Integer)' do
  813. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  814. .not_to raise_error
  815. end
  816. it 'grants update by id (String)' do
  817. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  818. .not_to raise_error
  819. end
  820. end
  821. end
  822. context 'when exceeding the agent limit' do
  823. it 'creation of new agents' do
  824. Setting.set('system_agent_limit', current_agents.count + 2)
  825. create_list(:agent_user, 2)
  826. expect { create(:agent_user) }
  827. .to raise_error(Exceptions::UnprocessableEntity)
  828. .and change(current_agents, :count).by(0)
  829. end
  830. it 'prevents role change' do
  831. Setting.set('system_agent_limit', current_agents.count)
  832. future_agent = create(:customer_user)
  833. expect { future_agent.roles = [agent_role] }
  834. .to raise_error(Exceptions::UnprocessableEntity)
  835. .and change(current_agents, :count).by(0)
  836. end
  837. end
  838. end
  839. context 'for String value of system_agent_limit' do
  840. context 'before exceeding the agent limit' do
  841. before { Setting.set('system_agent_limit', (current_agents.count + 1).to_s) }
  842. it 'grants agent creation' do
  843. expect { create(:agent_user) }
  844. .to change(current_agents, :count).by(1)
  845. end
  846. it 'grants role change' do
  847. future_agent = create(:customer_user)
  848. expect { future_agent.roles = [agent_role] }
  849. .to change(current_agents, :count).by(1)
  850. end
  851. describe 'role updates' do
  852. let(:agent) { create(:agent_user) }
  853. it 'grants update by instances' do
  854. expect { agent.roles = [admin_role, agent_role] }
  855. .not_to raise_error
  856. end
  857. it 'grants update by id (Integer)' do
  858. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  859. .not_to raise_error
  860. end
  861. it 'grants update by id (String)' do
  862. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  863. .not_to raise_error
  864. end
  865. end
  866. end
  867. context 'when exceeding the agent limit' do
  868. it 'creation of new agents' do
  869. Setting.set('system_agent_limit', (current_agents.count + 2).to_s)
  870. create_list(:agent_user, 2)
  871. expect { create(:agent_user) }
  872. .to raise_error(Exceptions::UnprocessableEntity)
  873. .and change(current_agents, :count).by(0)
  874. end
  875. it 'prevents role change' do
  876. Setting.set('system_agent_limit', current_agents.count.to_s)
  877. future_agent = create(:customer_user)
  878. expect { future_agent.roles = [agent_role] }
  879. .to raise_error(Exceptions::UnprocessableEntity)
  880. .and change(current_agents, :count).by(0)
  881. end
  882. end
  883. end
  884. end
  885. describe '#validate_agent_limit_by_attributes' do
  886. context 'for Integer value of system_agent_limit' do
  887. before { Setting.set('system_agent_limit', current_agents.count) }
  888. context 'when exceeding the agent limit' do
  889. it 'prevents re-activation of agents' do
  890. inactive_agent = create(:agent_user, active: false)
  891. expect { inactive_agent.update!(active: true) }
  892. .to raise_error(Exceptions::UnprocessableEntity)
  893. .and change(current_agents, :count).by(0)
  894. end
  895. end
  896. end
  897. context 'for String value of system_agent_limit' do
  898. before { Setting.set('system_agent_limit', current_agents.count.to_s) }
  899. context 'when exceeding the agent limit' do
  900. it 'prevents re-activation of agents' do
  901. inactive_agent = create(:agent_user, active: false)
  902. expect { inactive_agent.update!(active: true) }
  903. .to raise_error(Exceptions::UnprocessableEntity)
  904. .and change(current_agents, :count).by(0)
  905. end
  906. end
  907. end
  908. end
  909. end
  910. describe 'Touching associations on update:' do
  911. subject(:user) { create(:customer_user, organization: organization) }
  912. let(:organization) { create(:organization) }
  913. let(:other_customer) { create(:customer_user) }
  914. context 'when basic attributes are updated' do
  915. it 'touches its organization' do
  916. expect { user.update(firstname: 'foo') }
  917. .to change { organization.reload.updated_at }
  918. end
  919. end
  920. context 'when organization has 100+ other members' do
  921. let!(:other_members) { create_list(:user, 100, organization: organization) }
  922. context 'and basic attributes are updated' do
  923. it 'does not touch its organization' do
  924. expect { user.update(firstname: 'foo') }
  925. .to not_change { organization.reload.updated_at }
  926. end
  927. end
  928. end
  929. end
  930. describe 'Cti::CallerId syncing:' do
  931. context 'with a #phone attribute' do
  932. subject(:user) { build(:user, phone: '1234567890') }
  933. it 'adds CallerId record on creation (via Cti::CallerId.build)' do
  934. expect(Cti::CallerId).to receive(:build).with(user)
  935. user.save
  936. end
  937. it 'updates CallerId record on touch/update (via Cti::CallerId.build)' do
  938. user.save
  939. expect(Cti::CallerId).to receive(:build).with(user)
  940. user.touch
  941. end
  942. it 'destroys CallerId record on deletion' do
  943. user.save
  944. expect { user.destroy }
  945. .to change { Cti::CallerId.count }.by(-1)
  946. end
  947. end
  948. end
  949. describe 'Cti::Log syncing:' do
  950. context 'with existing Log records' do
  951. context 'for incoming calls from an unknown number' do
  952. let!(:log) { create(:'cti/log', :with_preferences, from: '1234567890', direction: 'in') }
  953. context 'when creating a new user with that number' do
  954. subject(:user) { build(:user, phone: log.from) }
  955. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  956. expect do
  957. user.save
  958. Observer::Transaction.commit
  959. Scheduler.worker(true)
  960. end.to change { log.reload.preferences[:from]&.first }
  961. .to(hash_including('caller_id' => user.phone))
  962. end
  963. end
  964. context 'when updating a user with that number' do
  965. subject(:user) { create(:user) }
  966. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  967. expect do
  968. user.update(phone: log.from)
  969. Observer::Transaction.commit
  970. Scheduler.worker(true)
  971. end.to change { log.reload.preferences[:from]&.first }
  972. .to(hash_including('object' => 'User', 'o_id' => user.id))
  973. end
  974. end
  975. context 'when creating a new user with an empty number' do
  976. subject(:user) { build(:user, phone: '') }
  977. it 'does not modify any Log records' do
  978. expect do
  979. user.save
  980. Observer::Transaction.commit
  981. Scheduler.worker(true)
  982. end.not_to change { log.reload.attributes }
  983. end
  984. end
  985. context 'when creating a new user with no number' do
  986. subject(:user) { build(:user, phone: nil) }
  987. it 'does not modify any Log records' do
  988. expect do
  989. user.save
  990. Observer::Transaction.commit
  991. Scheduler.worker(true)
  992. end.not_to change { log.reload.attributes }
  993. end
  994. end
  995. end
  996. context 'for incoming calls from the given user' do
  997. subject(:user) { create(:user, phone: '1234567890') }
  998. let!(:logs) { create_list(:'cti/log', 5, :with_preferences, from: user.phone, direction: 'in') }
  999. context 'when updating #phone attribute' do
  1000. context 'to another number' do
  1001. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1002. expect do
  1003. user.update(phone: '0123456789')
  1004. Observer::Transaction.commit
  1005. Scheduler.worker(true)
  1006. end.to change { logs.map(&:reload).map(&:preferences) }
  1007. .to(Array.new(5) { {} })
  1008. end
  1009. end
  1010. context 'to an empty string' do
  1011. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1012. expect do
  1013. user.update(phone: '')
  1014. Observer::Transaction.commit
  1015. Scheduler.worker(true)
  1016. end.to change { logs.map(&:reload).map(&:preferences) }
  1017. .to(Array.new(5) { {} })
  1018. end
  1019. end
  1020. context 'to nil' do
  1021. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1022. expect do
  1023. user.update(phone: nil)
  1024. Observer::Transaction.commit
  1025. Scheduler.worker(true)
  1026. end.to change { logs.map(&:reload).map(&:preferences) }
  1027. .to(Array.new(5) { {} })
  1028. end
  1029. end
  1030. end
  1031. context 'when updating attributes other than #phone' do
  1032. it 'does not modify any Log records' do
  1033. expect do
  1034. user.update(mobile: '2345678901')
  1035. Observer::Transaction.commit
  1036. Scheduler.worker(true)
  1037. end.not_to change { logs.map(&:reload).map(&:attributes) }
  1038. end
  1039. end
  1040. end
  1041. end
  1042. end
  1043. end
  1044. end