TwoFAccountModelTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. <?php
  2. namespace Tests\Feature\Models;
  3. use App\Models\TwoFAccount;
  4. use App\Models\User;
  5. use Illuminate\Http\Testing\FileFactory;
  6. use Illuminate\Support\Facades\Http;
  7. use Illuminate\Support\Facades\Storage;
  8. use Tests\Data\HttpRequestTestData;
  9. use Tests\Data\OtpTestData;
  10. use Tests\FeatureTestCase;
  11. /**
  12. * @covers \App\Models\TwoFAccount
  13. */
  14. class TwoFAccountModelTest extends FeatureTestCase
  15. {
  16. /**
  17. * @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
  18. */
  19. protected $user;
  20. protected $anotherUser;
  21. /**
  22. * @var \App\Models\TwoFAccount
  23. */
  24. protected $customTotpTwofaccount;
  25. protected $customHotpTwofaccount;
  26. protected $customSteamTotpTwofaccount;
  27. /**
  28. * Helpers $helpers;
  29. */
  30. protected $helpers;
  31. /**
  32. * @test
  33. */
  34. public function setUp() : void
  35. {
  36. parent::setUp();
  37. $this->user = User::factory()->create();
  38. $this->customTotpTwofaccount = TwoFAccount::factory()->for($this->user)->create([
  39. 'legacy_uri' => OtpTestData::TOTP_FULL_CUSTOM_URI,
  40. 'service' => OtpTestData::SERVICE,
  41. 'account' => OtpTestData::ACCOUNT,
  42. 'icon' => OtpTestData::ICON_PNG,
  43. 'otp_type' => 'totp',
  44. 'secret' => OtpTestData::SECRET,
  45. 'digits' => OtpTestData::DIGITS_CUSTOM,
  46. 'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
  47. 'period' => OtpTestData::PERIOD_CUSTOM,
  48. 'counter' => null,
  49. ]);
  50. $this->customHotpTwofaccount = TwoFAccount::factory()->for($this->user)->create([
  51. 'legacy_uri' => OtpTestData::HOTP_FULL_CUSTOM_URI,
  52. 'service' => OtpTestData::SERVICE,
  53. 'account' => OtpTestData::ACCOUNT,
  54. 'icon' => OtpTestData::ICON_PNG,
  55. 'otp_type' => 'hotp',
  56. 'secret' => OtpTestData::SECRET,
  57. 'digits' => OtpTestData::DIGITS_CUSTOM,
  58. 'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
  59. 'period' => null,
  60. 'counter' => OtpTestData::COUNTER_CUSTOM,
  61. ]);
  62. $this->customSteamTotpTwofaccount = TwoFAccount::factory()->for($this->user)->create([
  63. 'legacy_uri' => OtpTestData::STEAM_TOTP_URI,
  64. 'service' => OtpTestData::STEAM,
  65. 'account' => OtpTestData::ACCOUNT,
  66. 'otp_type' => 'steamtotp',
  67. 'secret' => OtpTestData::STEAM_SECRET,
  68. 'digits' => OtpTestData::DIGITS_STEAM,
  69. 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
  70. 'period' => OtpTestData::PERIOD_DEFAULT,
  71. 'counter' => null,
  72. ]);
  73. }
  74. /**
  75. * @test
  76. */
  77. public function test_fill_with_custom_totp_uri_returns_correct_value()
  78. {
  79. $file = (new FileFactory)->image('file.png', 10, 10);
  80. Http::preventStrayRequests();
  81. Http::fake([
  82. 'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response($file->tempFile, 200),
  83. ]);
  84. Storage::fake('imagesLink');
  85. Storage::fake('icons');
  86. $twofaccount = new TwoFAccount;
  87. $twofaccount->fillWithURI(OtpTestData::TOTP_FULL_CUSTOM_URI);
  88. $this->assertEquals('totp', $twofaccount->otp_type);
  89. $this->assertEquals(OtpTestData::TOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
  90. $this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
  91. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  92. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  93. $this->assertEquals(OtpTestData::DIGITS_CUSTOM, $twofaccount->digits);
  94. $this->assertEquals(OtpTestData::PERIOD_CUSTOM, $twofaccount->period);
  95. $this->assertEquals(null, $twofaccount->counter);
  96. $this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
  97. $this->assertNotNull($twofaccount->icon);
  98. Storage::disk('icons')->assertExists($twofaccount->icon);
  99. Storage::disk('imagesLink')->assertMissing($twofaccount->icon);
  100. }
  101. /**
  102. * @test
  103. */
  104. public function test_fill_with_basic_totp_uri_returns_default_value()
  105. {
  106. $twofaccount = new TwoFAccount;
  107. $twofaccount->fillWithURI(OtpTestData::TOTP_SHORT_URI);
  108. $this->assertEquals('totp', $twofaccount->otp_type);
  109. $this->assertEquals(OtpTestData::TOTP_SHORT_URI, $twofaccount->legacy_uri);
  110. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  111. $this->assertEquals(null, $twofaccount->service);
  112. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  113. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  114. $this->assertEquals(OtpTestData::PERIOD_DEFAULT, $twofaccount->period);
  115. $this->assertEquals(null, $twofaccount->counter);
  116. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  117. $this->assertEquals(null, $twofaccount->icon);
  118. }
  119. /**
  120. * @test
  121. */
  122. public function test_fill_with_custom_hotp_uri_returns_correct_value()
  123. {
  124. $file = (new FileFactory)->image('file.png', 10, 10);
  125. Http::preventStrayRequests();
  126. Http::fake([
  127. 'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response($file->tempFile, 200),
  128. ]);
  129. Storage::fake('imagesLink');
  130. Storage::fake('icons');
  131. $twofaccount = new TwoFAccount;
  132. $twofaccount->fillWithURI(OtpTestData::HOTP_FULL_CUSTOM_URI);
  133. $this->assertEquals('hotp', $twofaccount->otp_type);
  134. $this->assertEquals(OtpTestData::HOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
  135. $this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
  136. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  137. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  138. $this->assertEquals(OtpTestData::DIGITS_CUSTOM, $twofaccount->digits);
  139. $this->assertEquals(null, $twofaccount->period);
  140. $this->assertEquals(OtpTestData::COUNTER_CUSTOM, $twofaccount->counter);
  141. $this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
  142. $this->assertNotNull($twofaccount->icon);
  143. Storage::disk('icons')->assertExists($twofaccount->icon);
  144. Storage::disk('imagesLink')->assertMissing($twofaccount->icon);
  145. }
  146. /**
  147. * @test
  148. */
  149. public function test_fill_with_basic_hotp_uri_returns_default_value()
  150. {
  151. $twofaccount = new TwoFAccount;
  152. $twofaccount->fillWithURI(OtpTestData::HOTP_SHORT_URI);
  153. $this->assertEquals('hotp', $twofaccount->otp_type);
  154. $this->assertEquals(OtpTestData::HOTP_SHORT_URI, $twofaccount->legacy_uri);
  155. $this->assertEquals(null, $twofaccount->service);
  156. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  157. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  158. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  159. $this->assertEquals(null, $twofaccount->period);
  160. $this->assertEquals(OtpTestData::COUNTER_DEFAULT, $twofaccount->counter);
  161. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  162. $this->assertEquals(null, $twofaccount->icon);
  163. }
  164. /**
  165. * @test
  166. */
  167. public function test_filled_with_uri_persists_correct_values_to_db()
  168. {
  169. $twofaccount = new TwoFAccount;
  170. $twofaccount->fillWithURI(OtpTestData::TOTP_SHORT_URI);
  171. $twofaccount->save();
  172. $this->assertDatabaseHas('twofaccounts', [
  173. 'otp_type' => 'totp',
  174. 'legacy_uri' => OtpTestData::TOTP_SHORT_URI,
  175. 'service' => null,
  176. 'account' => OtpTestData::ACCOUNT,
  177. 'secret' => OtpTestData::SECRET,
  178. 'digits' => OtpTestData::DIGITS_DEFAULT,
  179. 'period' => OtpTestData::PERIOD_DEFAULT,
  180. 'counter' => null,
  181. 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
  182. 'icon' => null,
  183. ]);
  184. }
  185. /**
  186. * @test
  187. */
  188. public function test_fill_with_invalid_uri_returns_ValidationException()
  189. {
  190. $this->expectException(\Illuminate\Validation\ValidationException::class);
  191. $twofaccount = new TwoFAccount;
  192. $twofaccount->fillWithURI(OtpTestData::INVALID_OTPAUTH_URI);
  193. }
  194. /**
  195. * @test
  196. */
  197. public function test_fill_with_uri_without_label_returns_ValidationException()
  198. {
  199. $this->expectException(\Illuminate\Validation\ValidationException::class);
  200. $twofaccount = new TwoFAccount;
  201. $twofaccount->fillWithURI('otpauth://totp/?secret=' . OtpTestData::SECRET);
  202. }
  203. /**
  204. * @test
  205. */
  206. public function test_create_custom_totp_from_parameters_returns_correct_value()
  207. {
  208. $twofaccount = new TwoFAccount;
  209. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP);
  210. $this->assertEquals('totp', $twofaccount->otp_type);
  211. $this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
  212. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  213. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  214. $this->assertEquals(OtpTestData::DIGITS_CUSTOM, $twofaccount->digits);
  215. $this->assertEquals(OtpTestData::PERIOD_CUSTOM, $twofaccount->period);
  216. $this->assertEquals(null, $twofaccount->counter);
  217. $this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
  218. $this->assertStringEndsWith('.png', $twofaccount->icon);
  219. }
  220. /**
  221. * @test
  222. */
  223. public function test_create_basic_totp_from_parameters_returns_correct_value()
  224. {
  225. $twofaccount = new TwoFAccount;
  226. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
  227. $this->assertEquals('totp', $twofaccount->otp_type);
  228. $this->assertEquals(null, $twofaccount->service);
  229. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  230. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  231. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  232. $this->assertEquals(OtpTestData::PERIOD_DEFAULT, $twofaccount->period);
  233. $this->assertEquals(null, $twofaccount->counter);
  234. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  235. $this->assertEquals(null, $twofaccount->icon);
  236. }
  237. /**
  238. * @test
  239. */
  240. public function test_create_custom_hotp_from_parameters_returns_correct_value()
  241. {
  242. $twofaccount = new TwoFAccount;
  243. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP);
  244. $this->assertEquals('hotp', $twofaccount->otp_type);
  245. $this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
  246. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  247. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  248. $this->assertEquals(OtpTestData::DIGITS_CUSTOM, $twofaccount->digits);
  249. $this->assertEquals(null, $twofaccount->period);
  250. $this->assertEquals(OtpTestData::COUNTER_CUSTOM, $twofaccount->counter);
  251. $this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
  252. $this->assertStringEndsWith('.png', $twofaccount->icon);
  253. }
  254. /**
  255. * @test
  256. */
  257. public function test_create_basic_hotp_from_parameters_returns_correct_value()
  258. {
  259. $twofaccount = new TwoFAccount;
  260. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP);
  261. $this->assertEquals('hotp', $twofaccount->otp_type);
  262. $this->assertEquals(null, $twofaccount->service);
  263. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  264. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  265. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  266. $this->assertEquals(null, $twofaccount->period);
  267. $this->assertEquals(OtpTestData::COUNTER_DEFAULT, $twofaccount->counter);
  268. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  269. $this->assertEquals(null, $twofaccount->icon);
  270. }
  271. /**
  272. * @test
  273. */
  274. public function test_create_from_parameters_persists_correct_values_to_db()
  275. {
  276. $twofaccount = new TwoFAccount;
  277. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
  278. $twofaccount->save();
  279. $this->assertDatabaseHas('twofaccounts', [
  280. 'otp_type' => 'totp',
  281. 'legacy_uri' => OtpTestData::TOTP_SHORT_URI,
  282. 'service' => null,
  283. 'account' => OtpTestData::ACCOUNT,
  284. 'secret' => OtpTestData::SECRET,
  285. 'digits' => OtpTestData::DIGITS_DEFAULT,
  286. 'period' => OtpTestData::PERIOD_DEFAULT,
  287. 'counter' => null,
  288. 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
  289. 'icon' => null,
  290. ]);
  291. }
  292. /**
  293. * @test
  294. */
  295. public function test_create_from_unsupported_parameters_returns_unsupportedOtpTypeException()
  296. {
  297. $this->expectException(\App\Exceptions\UnsupportedOtpTypeException::class);
  298. $twofaccount = new TwoFAccount;
  299. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE);
  300. }
  301. /**
  302. * @test
  303. */
  304. public function test_create_from_invalid_parameters_type_returns_InvalidOtpParameterException()
  305. {
  306. $this->expectException(\App\Exceptions\InvalidOtpParameterException::class);
  307. $twofaccount = new TwoFAccount;
  308. $twofaccount->fillWithOtpParameters([
  309. 'account' => OtpTestData::ACCOUNT,
  310. 'otp_type' => 'totp',
  311. 'digits' => 'notsupported',
  312. ]);
  313. }
  314. /**
  315. * @test
  316. */
  317. public function test_create_from_invalid_parameters_returns_InvalidOtpParameterException()
  318. {
  319. $this->expectException(\App\Exceptions\InvalidOtpParameterException::class);
  320. $twofaccount = new TwoFAccount;
  321. $twofaccount->fillWithOtpParameters([
  322. 'account' => OtpTestData::ACCOUNT,
  323. 'otp_type' => 'totp',
  324. 'algorithm' => 'notsupported',
  325. ]);
  326. }
  327. /**
  328. * @test
  329. */
  330. public function test_update_totp_returns_updated_model()
  331. {
  332. $twofaccount = $this->customTotpTwofaccount;
  333. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
  334. $this->assertEquals('totp', $twofaccount->otp_type);
  335. $this->assertEquals(null, $twofaccount->service);
  336. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  337. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  338. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  339. $this->assertEquals(OtpTestData::PERIOD_DEFAULT, $twofaccount->period);
  340. $this->assertEquals(null, $twofaccount->counter);
  341. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  342. $this->assertEquals(null, $twofaccount->counter);
  343. $this->assertEquals(null, $twofaccount->icon);
  344. }
  345. /**
  346. * @test
  347. */
  348. public function test_update_hotp_returns_updated_model()
  349. {
  350. $twofaccount = $this->customTotpTwofaccount;
  351. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP);
  352. $this->assertEquals('hotp', $twofaccount->otp_type);
  353. $this->assertEquals(null, $twofaccount->service);
  354. $this->assertEquals(OtpTestData::ACCOUNT, $twofaccount->account);
  355. $this->assertEquals(OtpTestData::SECRET, $twofaccount->secret);
  356. $this->assertEquals(OtpTestData::DIGITS_DEFAULT, $twofaccount->digits);
  357. $this->assertEquals(null, $twofaccount->period);
  358. $this->assertEquals(OtpTestData::COUNTER_DEFAULT, $twofaccount->counter);
  359. $this->assertEquals(OtpTestData::ALGORITHM_DEFAULT, $twofaccount->algorithm);
  360. $this->assertEquals(null, $twofaccount->counter);
  361. $this->assertEquals(null, $twofaccount->icon);
  362. }
  363. /**
  364. * @test
  365. */
  366. public function test_update_totp_persists_updated_model()
  367. {
  368. $twofaccount = $this->customTotpTwofaccount;
  369. $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP);
  370. $twofaccount->save();
  371. $this->assertDatabaseHas('twofaccounts', [
  372. 'otp_type' => 'totp',
  373. 'service' => null,
  374. 'account' => OtpTestData::ACCOUNT,
  375. 'secret' => OtpTestData::SECRET,
  376. 'digits' => OtpTestData::DIGITS_DEFAULT,
  377. 'period' => OtpTestData::PERIOD_DEFAULT,
  378. 'counter' => null,
  379. 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
  380. 'icon' => null,
  381. ]);
  382. }
  383. /**
  384. * @test
  385. */
  386. public function test_getOTP_for_totp_returns_the_same_password()
  387. {
  388. Http::preventStrayRequests();
  389. Http::fake([
  390. 'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response(HttpRequestTestData::ICON_PNG, 200),
  391. ]);
  392. Storage::fake('imagesLink');
  393. Storage::fake('icons');
  394. $twofaccount = new TwoFAccount;
  395. $otp_from_model = $this->customTotpTwofaccount->getOTP();
  396. $otp_from_uri = $twofaccount->fillWithURI(OtpTestData::TOTP_FULL_CUSTOM_URI)->getOTP();
  397. if ($otp_from_model->generated_at === $otp_from_uri->generated_at) {
  398. $this->assertEquals($otp_from_model, $otp_from_uri);
  399. }
  400. $otp_from_model = $this->customTotpTwofaccount->getOTP();
  401. $otp_from_parameters = $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP)->getOTP();
  402. if ($otp_from_model->generated_at === $otp_from_parameters->generated_at) {
  403. $this->assertEquals($otp_from_model, $otp_from_parameters);
  404. }
  405. }
  406. /**
  407. * @test
  408. */
  409. public function test_getOTP_for_hotp_returns_the_same_password()
  410. {
  411. Http::preventStrayRequests();
  412. Http::fake([
  413. 'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response(HttpRequestTestData::ICON_PNG, 200),
  414. ]);
  415. Storage::fake('imagesLink');
  416. Storage::fake('icons');
  417. $twofaccount = new TwoFAccount;
  418. $otp_from_model = $this->customHotpTwofaccount->getOTP();
  419. $otp_from_uri = $twofaccount->fillWithURI(OtpTestData::HOTP_FULL_CUSTOM_URI)->getOTP();
  420. $this->assertEquals($otp_from_model, $otp_from_uri);
  421. $otp_from_parameters = $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP)->getOTP();
  422. $this->assertEquals($otp_from_model, $otp_from_parameters);
  423. }
  424. /**
  425. * @test
  426. */
  427. public function test_getOTP_for_steamtotp_returns_the_same_password()
  428. {
  429. $twofaccount = new TwoFAccount;
  430. $otp_from_model = $this->customSteamTotpTwofaccount->getOTP();
  431. $otp_from_uri = $twofaccount->fillWithURI(OtpTestData::STEAM_TOTP_URI)->getOTP();
  432. if ($otp_from_model->generated_at === $otp_from_uri->generated_at) {
  433. $this->assertEquals($otp_from_model, $otp_from_uri);
  434. }
  435. $otp_from_model = $this->customSteamTotpTwofaccount->getOTP();
  436. $otp_from_parameters = $twofaccount->fillWithOtpParameters(OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_STEAM_TOTP)->getOTP();
  437. if ($otp_from_model->generated_at === $otp_from_parameters->generated_at) {
  438. $this->assertEquals($otp_from_model, $otp_from_parameters);
  439. }
  440. }
  441. /**
  442. * @test
  443. */
  444. public function test_getOTP_for_totp_with_invalid_secret_returns_InvalidSecretException()
  445. {
  446. $twofaccount = new TwoFAccount;
  447. $this->expectException(\App\Exceptions\InvalidSecretException::class);
  448. $otp_from_uri = $twofaccount->fillWithURI('otpauth://totp/' . OtpTestData::ACCOUNT . '?secret=1.0')->getOTP();
  449. }
  450. /**
  451. * @test
  452. */
  453. public function test_getOTP_for_totp_with_undecipherable_secret_returns_UndecipherableException()
  454. {
  455. $twofaccount = new TwoFAccount;
  456. $this->expectException(\App\Exceptions\UndecipherableException::class);
  457. $otp_from_uri = $twofaccount->fillWithOtpParameters([
  458. 'account' => OtpTestData::ACCOUNT,
  459. 'otp_type' => 'totp',
  460. 'secret' => __('errors.indecipherable'),
  461. ])->getOTP();
  462. }
  463. /**
  464. * @test
  465. */
  466. public function test_getURI_for_custom_totp_model_returns_uri()
  467. {
  468. $uri = $this->customTotpTwofaccount->getURI();
  469. $this->assertStringContainsString('otpauth://totp/', $uri);
  470. $this->assertStringContainsString(OtpTestData::SERVICE, $uri);
  471. $this->assertStringContainsString(OtpTestData::ACCOUNT, $uri);
  472. $this->assertStringContainsString('secret=' . OtpTestData::SECRET, $uri);
  473. $this->assertStringContainsString('digits=' . OtpTestData::DIGITS_CUSTOM, $uri);
  474. $this->assertStringContainsString('period=' . OtpTestData::PERIOD_CUSTOM, $uri);
  475. $this->assertStringContainsString('algorithm=' . OtpTestData::ALGORITHM_CUSTOM, $uri);
  476. }
  477. /**
  478. * @test
  479. */
  480. public function test_getURI_for_custom_hotp_model_returns_uri()
  481. {
  482. $uri = $this->customHotpTwofaccount->getURI();
  483. $this->assertStringContainsString('otpauth://hotp/', $uri);
  484. $this->assertStringContainsString(OtpTestData::SERVICE, $uri);
  485. $this->assertStringContainsString(OtpTestData::ACCOUNT, $uri);
  486. $this->assertStringContainsString('secret=' . OtpTestData::SECRET, $uri);
  487. $this->assertStringContainsString('digits=' . OtpTestData::DIGITS_CUSTOM, $uri);
  488. $this->assertStringContainsString('counter=' . OtpTestData::COUNTER_CUSTOM, $uri);
  489. $this->assertStringContainsString('algorithm=' . OtpTestData::ALGORITHM_CUSTOM, $uri);
  490. }
  491. /**
  492. * @test
  493. */
  494. public function test_fill_succeed_when_image_fetching_fails()
  495. {
  496. Http::preventStrayRequests();
  497. Storage::fake('imagesLink');
  498. Storage::fake('icons');
  499. $twofaccount = new TwoFAccount;
  500. $twofaccount->fillWithURI(OtpTestData::TOTP_FULL_CUSTOM_URI);
  501. Storage::disk('icons')->assertDirectoryEmpty('/');
  502. Storage::disk('imagesLink')->assertDirectoryEmpty('/');
  503. }
  504. /**
  505. * @test
  506. */
  507. public function test_saving_totp_without_period_set_default_one()
  508. {
  509. $twofaccount = new TwoFAccount;
  510. $twofaccount->service = OtpTestData::SERVICE;
  511. $twofaccount->account = OtpTestData::ACCOUNT;
  512. $twofaccount->otp_type = TwoFAccount::TOTP;
  513. $twofaccount->secret = OtpTestData::SECRET;
  514. $twofaccount->save();
  515. $account = TwoFAccount::find($twofaccount->id);
  516. $this->assertEquals(TwoFAccount::DEFAULT_PERIOD, $account->period);
  517. }
  518. /**
  519. * @test
  520. */
  521. public function test_saving_hotp_without_counter_set_default_one()
  522. {
  523. $twofaccount = new TwoFAccount;
  524. $twofaccount->service = OtpTestData::SERVICE;
  525. $twofaccount->account = OtpTestData::ACCOUNT;
  526. $twofaccount->otp_type = TwoFAccount::HOTP;
  527. $twofaccount->secret = OtpTestData::SECRET;
  528. $twofaccount->save();
  529. $account = TwoFAccount::find($twofaccount->id);
  530. $this->assertEquals(TwoFAccount::DEFAULT_COUNTER, $account->counter);
  531. }
  532. /**
  533. * @test
  534. */
  535. public function test_equals_returns_true()
  536. {
  537. $twofaccount = new TwoFAccount;
  538. $twofaccount->legacy_uri = OtpTestData::TOTP_FULL_CUSTOM_URI;
  539. $twofaccount->service = OtpTestData::SERVICE;
  540. $twofaccount->account = OtpTestData::ACCOUNT;
  541. $twofaccount->icon = OtpTestData::ICON_PNG;
  542. $twofaccount->otp_type = 'totp';
  543. $twofaccount->secret = OtpTestData::SECRET;
  544. $twofaccount->digits = OtpTestData::DIGITS_CUSTOM;
  545. $twofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
  546. $twofaccount->period = OtpTestData::PERIOD_CUSTOM;
  547. $twofaccount->counter = null;
  548. $twofaccount->save();
  549. $this->assertTrue($twofaccount->equals($this->customTotpTwofaccount));
  550. }
  551. /**
  552. * @test
  553. */
  554. public function test_equals_returns_false()
  555. {
  556. $twofaccount = new TwoFAccount;
  557. $twofaccount->legacy_uri = OtpTestData::TOTP_FULL_CUSTOM_URI;
  558. $twofaccount->service = OtpTestData::SERVICE;
  559. $twofaccount->account = OtpTestData::ACCOUNT;
  560. $twofaccount->icon = OtpTestData::ICON_PNG;
  561. $twofaccount->otp_type = 'totp';
  562. $twofaccount->secret = OtpTestData::SECRET;
  563. $twofaccount->digits = OtpTestData::DIGITS_CUSTOM;
  564. $twofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
  565. $twofaccount->period = OtpTestData::PERIOD_CUSTOM;
  566. $twofaccount->counter = null;
  567. $twofaccount->save();
  568. $this->assertFalse($twofaccount->equals($this->customHotpTwofaccount));
  569. }
  570. /**
  571. * @test
  572. *
  573. * @dataProvider iconResourceProvider
  574. */
  575. public function test_set_icon_stores_and_set_the_icon($res, $ext)
  576. {
  577. Storage::fake('imagesLink');
  578. Storage::fake('icons');
  579. $previousIcon = $this->customTotpTwofaccount->icon;
  580. $this->customTotpTwofaccount->setIcon($res, $ext);
  581. $this->assertNotEquals($previousIcon, $this->customTotpTwofaccount->icon);
  582. Storage::disk('icons')->assertExists($this->customTotpTwofaccount->icon);
  583. Storage::disk('imagesLink')->assertMissing($this->customTotpTwofaccount->icon);
  584. }
  585. /**
  586. * Provide data for Icon store tests
  587. */
  588. public function iconResourceProvider()
  589. {
  590. return [
  591. 'PNG' => [
  592. base64_decode(OtpTestData::ICON_PNG_DATA),
  593. 'png',
  594. ],
  595. 'JPG' => [
  596. base64_decode(OtpTestData::ICON_JPEG_DATA),
  597. 'jpg',
  598. ],
  599. 'WEBP' => [
  600. base64_decode(OtpTestData::ICON_WEBP_DATA),
  601. 'webp',
  602. ],
  603. 'BMP' => [
  604. base64_decode(OtpTestData::ICON_BMP_DATA),
  605. 'bmp',
  606. ],
  607. 'SVG' => [
  608. OtpTestData::ICON_SVG_DATA,
  609. 'svg',
  610. ],
  611. ];
  612. }
  613. /**
  614. * @test
  615. *
  616. * @dataProvider invalidIconResourceProvider
  617. */
  618. public function test_set_invalid_icon_ends_without_error($res, $ext)
  619. {
  620. Storage::fake('imagesLink');
  621. Storage::fake('icons');
  622. $previousIcon = $this->customTotpTwofaccount->icon;
  623. $this->customTotpTwofaccount->setIcon($res, $ext);
  624. $this->assertEquals($previousIcon, $this->customTotpTwofaccount->icon);
  625. Storage::disk('icons')->assertMissing($this->customTotpTwofaccount->icon);
  626. Storage::disk('imagesLink')->assertMissing($this->customTotpTwofaccount->icon);
  627. }
  628. /**
  629. * Provide data for Icon store tests
  630. */
  631. public function invalidIconResourceProvider()
  632. {
  633. return [
  634. 'INVALID_PNG' => [
  635. 'lkjdslfkjslkdfjlskdjflksjf',
  636. 'png',
  637. ],
  638. ];
  639. }
  640. }