UserModelTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. <?php
  2. namespace Tests\Feature\Models;
  3. use App\Models\AuthLog;
  4. use App\Models\Group;
  5. use App\Models\TwoFAccount;
  6. use App\Models\User;
  7. use App\Observers\UserObserver;
  8. use Database\Factories\AuthLogFactory;
  9. use Illuminate\Auth\Events\PasswordReset;
  10. use Illuminate\Support\Facades\Artisan;
  11. use Illuminate\Support\Facades\DB;
  12. use Illuminate\Support\Facades\Event;
  13. use Illuminate\Support\Facades\Password;
  14. use Illuminate\Support\Facades\Storage;
  15. use PHPUnit\Framework\Attributes\CoversClass;
  16. use PHPUnit\Framework\Attributes\Test;
  17. use Tests\Data\OtpTestData;
  18. use Tests\FeatureTestCase;
  19. /**
  20. * UserModelTest test class
  21. */
  22. #[CoversClass(User::class)]
  23. #[CoversClass(UserObserver::class)]
  24. class UserModelTest extends FeatureTestCase
  25. {
  26. #[Test]
  27. public function test_admin_scope_returns_only_admin()
  28. {
  29. User::factory()->count(4)->create();
  30. $firstAdmin = User::factory()->administrator()->create([
  31. 'name' => 'first',
  32. ]);
  33. $secondAdmin = User::factory()->administrator()->create([
  34. 'name' => 'secondAdmin',
  35. ]);
  36. $admins = User::admins()->get();
  37. $this->assertCount(2, $admins);
  38. $this->assertEquals($admins[0]->is_admin, true);
  39. $this->assertEquals($admins[1]->is_admin, true);
  40. $this->assertEquals($admins[0]->name, $firstAdmin->name);
  41. $this->assertEquals($admins[1]->name, $secondAdmin->name);
  42. }
  43. #[Test]
  44. public function test_isAdministrator_returns_correct_state()
  45. {
  46. $user = User::factory()->create();
  47. $admin = User::factory()->administrator()->create();
  48. $this->assertEquals($user->isAdministrator(), false);
  49. $this->assertEquals($admin->isAdministrator(), true);
  50. }
  51. #[Test]
  52. public function test_promoteToAdministrator_sets_administrator_status()
  53. {
  54. $user = User::factory()->create();
  55. $user->promoteToAdministrator();
  56. $this->assertEquals($user->isAdministrator(), true);
  57. }
  58. #[Test]
  59. public function test_promoteToAdministrator_demote_administrator_status()
  60. {
  61. $admin = User::factory()->administrator()->create();
  62. // We need another admin to prevent demoting event returning false
  63. // and blocking the demotion
  64. $another_admin = User::factory()->administrator()->create();
  65. $admin->promoteToAdministrator(false);
  66. $admin->save();
  67. $this->assertFalse($admin->isAdministrator());
  68. }
  69. #[Test]
  70. public function test_resetPassword_resets_password_with_success()
  71. {
  72. $user = User::factory()->create();
  73. $oldPassword = $user->password;
  74. $user->resetPassword();
  75. $this->assertNotEquals($user->password, $oldPassword);
  76. }
  77. #[Test]
  78. public function test_resetPassword_dispatch_event()
  79. {
  80. Event::fake();
  81. $user = User::factory()->create();
  82. Event::assertDispatched(
  83. PasswordReset::class,
  84. $user->resetPassword()
  85. );
  86. }
  87. #[Test]
  88. public function test_delete_removes_user_data()
  89. {
  90. Artisan::call('passport:install', [
  91. '--verbose' => 2,
  92. '--no-interaction' => 1,
  93. ]);
  94. $user = User::factory()->create();
  95. TwoFAccount::factory()->for($user)->create();
  96. AuthLog::factory()->for($user, 'authenticatable')->create();
  97. Group::factory()->for($user)->create();
  98. DB::table('webauthn_credentials')->insert([
  99. 'id' => '-VOLFKPY-_FuMI_sJ7gMllK76L3VoRUINj6lL_Z3qDg',
  100. 'authenticatable_type' => \App\Models\User::class,
  101. 'authenticatable_id' => $user->id,
  102. 'user_id' => 'e8af6f703f8042aa91c30cf72289aa07',
  103. 'counter' => 0,
  104. 'rp_id' => 'http://localhost',
  105. 'origin' => 'http://localhost',
  106. 'aaguid' => '00000000-0000-0000-0000-000000000000',
  107. 'attestation_format' => 'none',
  108. 'public_key' => 'eyJpdiI6Imp0U0NVeFNNbW45KzEvMXpad2p2SUE9PSIsInZhbHVlIjoic0VxZ2I1WnlHM2lJakhkWHVkK2kzMWtibk1IN2ZlaExGT01qOElXMDdRTjhnVlR0TDgwOHk1S0xQUy9BQ1JCWHRLNzRtenNsMml1dVQydWtERjFEU0h0bkJGT2RwUXE1M1JCcVpablE2Y2VGV2YvVEE2RGFIRUE5L0x1K0JIQXhLVE1aNVNmN3AxeHdjRUo2V0hwREZSRTJYaThNNnB1VnozMlVXZEVPajhBL3d3ODlkTVN3bW54RTEwSG0ybzRQZFFNNEFrVytUYThub2IvMFRtUlBZamoyZElWKzR1bStZQ1IwU3FXbkYvSm1FU2FlMTFXYUo0SG9kc1BDME9CNUNKeE9IelE5d2dmNFNJRXBKNUdlVzJ3VHUrQWJZRFluK0hib0xvVTdWQ0ZISjZmOWF3by83aVJES1dxbU9Zd1lhRTlLVmhZSUdlWmlBOUFtcTM2ZVBaRWNKNEFSQUhENk5EaC9hN3REdnVFbm16WkRxekRWOXd4cVcvZFdKa2tlWWJqZWlmZnZLS0F1VEVCZEZQcXJkTExiNWRyQmxsZWtaSDRlT3VVS0ZBSXFBRG1JMjRUMnBKRXZxOUFUa2xxMjg2TEplUzdscVo2UytoVU5SdXk1OE1lcFN6aU05ZkVXTkdIM2tKM3Q5bmx1TGtYb1F5bGxxQVR3K3BVUVlia1VybDFKRm9lZDViNzYraGJRdmtUb2FNTEVGZmZYZ3lYRDRiOUVjRnJpcTVvWVExOHJHSTJpMnVBZ3E0TmljbUlKUUtXY2lSWDh1dE5MVDNRUzVRSkQrTjVJUU8rSGhpeFhRRjJvSEdQYjBoVT0iLCJtYWMiOiI5MTdmNWRkZGE5OTEwNzQ3MjhkYWVhYjRlNjk0MWZlMmI5OTQ4YzlmZWI1M2I4OGVkMjE1MjMxNjUwOWRmZTU2IiwidGFnIjoiIn0=',
  109. 'updated_at' => now(),
  110. 'created_at' => now(),
  111. ]);
  112. $user->createToken('myToken', []);
  113. Password::broker('webauthn')->createToken($user);
  114. Password::broker()->createToken($user);
  115. $user->delete();
  116. $this->assertDatabaseMissing('twofaccounts', [
  117. 'user_id' => $user->id,
  118. ]);
  119. $this->assertDatabaseMissing('groups', [
  120. 'user_id' => $user->id,
  121. ]);
  122. $this->assertDatabaseMissing('webauthn_credentials', [
  123. 'authenticatable_id' => $user->id,
  124. ]);
  125. $this->assertDatabaseMissing(config('auth.passwords.webauthn.table'), [
  126. 'email' => $user->email,
  127. ]);
  128. $this->assertDatabaseMissing('oauth_access_tokens', [
  129. 'user_id' => $user->id,
  130. ]);
  131. $this->assertDatabaseMissing(config('auth.passwords.users.table'), [
  132. 'email' => $user->email,
  133. ]);
  134. $this->assertDatabaseMissing('auth_logs', [
  135. 'authenticatable_id' => $user->id,
  136. ]);
  137. }
  138. #[Test]
  139. public function test_delete_flushes_icons_of_user_twofaccounts()
  140. {
  141. Storage::fake('icons');
  142. $user = User::factory()->create();
  143. $twofaccount = TwoFAccount::factory()->for($user)->create();
  144. $twofaccount->setIcon(base64_decode(OtpTestData::ICON_PNG_DATA), 'png');
  145. $twofaccount->save();
  146. Storage::disk('icons')->assertExists($twofaccount->icon);
  147. $user->delete();
  148. Storage::disk('icons')->assertMissing($twofaccount->icon);
  149. }
  150. #[Test]
  151. public function test_delete_does_not_delete_the_only_admin()
  152. {
  153. $admin = User::factory()->administrator()->create();
  154. $this->assertEquals(1, User::admins()->count());
  155. $isDeleted = $admin->delete();
  156. $this->assertFalse($isDeleted);
  157. }
  158. #[Test]
  159. public function test_getFromCredentialId_retreives_the_user()
  160. {
  161. $user = User::factory()->create();
  162. DB::table('webauthn_credentials')->insert([
  163. 'id' => '-VOLFKPY-_FuMI_sJ7gMllK76L3VoRUINj6lL_Z3qDg',
  164. 'authenticatable_type' => \App\Models\User::class,
  165. 'authenticatable_id' => $user->id,
  166. 'user_id' => 'e8af6f703f8042aa91c30cf72289aa07',
  167. 'counter' => 0,
  168. 'rp_id' => 'http://localhost',
  169. 'origin' => 'http://localhost',
  170. 'aaguid' => '00000000-0000-0000-0000-000000000000',
  171. 'attestation_format' => 'none',
  172. 'public_key' => 'eyJpdiI6Imp0U0NVeFNNbW45KzEvMXpad2p2SUE9PSIsInZhbHVlIjoic0VxZ2I1WnlHM2lJakhkWHVkK2kzMWtibk1IN2ZlaExGT01qOElXMDdRTjhnVlR0TDgwOHk1S0xQUy9BQ1JCWHRLNzRtenNsMml1dVQydWtERjFEU0h0bkJGT2RwUXE1M1JCcVpablE2Y2VGV2YvVEE2RGFIRUE5L0x1K0JIQXhLVE1aNVNmN3AxeHdjRUo2V0hwREZSRTJYaThNNnB1VnozMlVXZEVPajhBL3d3ODlkTVN3bW54RTEwSG0ybzRQZFFNNEFrVytUYThub2IvMFRtUlBZamoyZElWKzR1bStZQ1IwU3FXbkYvSm1FU2FlMTFXYUo0SG9kc1BDME9CNUNKeE9IelE5d2dmNFNJRXBKNUdlVzJ3VHUrQWJZRFluK0hib0xvVTdWQ0ZISjZmOWF3by83aVJES1dxbU9Zd1lhRTlLVmhZSUdlWmlBOUFtcTM2ZVBaRWNKNEFSQUhENk5EaC9hN3REdnVFbm16WkRxekRWOXd4cVcvZFdKa2tlWWJqZWlmZnZLS0F1VEVCZEZQcXJkTExiNWRyQmxsZWtaSDRlT3VVS0ZBSXFBRG1JMjRUMnBKRXZxOUFUa2xxMjg2TEplUzdscVo2UytoVU5SdXk1OE1lcFN6aU05ZkVXTkdIM2tKM3Q5bmx1TGtYb1F5bGxxQVR3K3BVUVlia1VybDFKRm9lZDViNzYraGJRdmtUb2FNTEVGZmZYZ3lYRDRiOUVjRnJpcTVvWVExOHJHSTJpMnVBZ3E0TmljbUlKUUtXY2lSWDh1dE5MVDNRUzVRSkQrTjVJUU8rSGhpeFhRRjJvSEdQYjBoVT0iLCJtYWMiOiI5MTdmNWRkZGE5OTEwNzQ3MjhkYWVhYjRlNjk0MWZlMmI5OTQ4YzlmZWI1M2I4OGVkMjE1MjMxNjUwOWRmZTU2IiwidGFnIjoiIn0=',
  173. 'updated_at' => now(),
  174. 'created_at' => now(),
  175. ]);
  176. $searched = User::getFromCredentialId('-VOLFKPY-_FuMI_sJ7gMllK76L3VoRUINj6lL_Z3qDg');
  177. $this->assertEquals($user->id, $searched->id);
  178. }
  179. #[Test]
  180. public function test_authentications_returns_user_auth_logs_sorted_by_latest_id()
  181. {
  182. $user = User::factory()->create();
  183. $tenDaysAgoAuthLog = AuthLog::factory()->daysAgo(10)->for($user, 'authenticatable')->create();
  184. $fiveDaysAgoAuthLog = AuthLog::factory()->daysAgo(5)->for($user, 'authenticatable')->create();
  185. $lastAuthLog = AuthLog::factory()->daysAgo(1)->for($user, 'authenticatable')->create();
  186. $authentications = $user->authentications()->get();
  187. $this->assertCount(3, $authentications);
  188. $this->assertEquals($lastAuthLog->id, $authentications[0]->id);
  189. $this->assertEquals($fiveDaysAgoAuthLog->id, $authentications[1]->id);
  190. $this->assertEquals($tenDaysAgoAuthLog->id, $authentications[2]->id);
  191. }
  192. #[Test]
  193. public function test_authentications_returns_user_auth_logs_only()
  194. {
  195. $user = User::factory()->create();
  196. $anotherUser = User::factory()->create();
  197. $userAuthLog = AuthLog::factory()->daysAgo(10)->for($user, 'authenticatable')->create();
  198. AuthLog::factory()->daysAgo(5)->for($anotherUser, 'authenticatable')->create();
  199. $authentications = $user->authentications()->get();
  200. $this->assertCount(1, $authentications);
  201. $this->assertEquals($userAuthLog->id, $authentications[0]->id);
  202. }
  203. #[Test]
  204. public function test_authenticationsByPeriod_returns_last_month_auth_logs()
  205. {
  206. $user = User::factory()->create();
  207. $twoMonthsAgoAuthLog = AuthLog::factory()->duringLastThreeMonth()->for($user, 'authenticatable')->create();
  208. $duringLastMonthAuthLog = AuthLog::factory()->duringLastMonth()->for($user, 'authenticatable')->create();
  209. $authentications = $user->authenticationsByPeriod(1);
  210. $this->assertCount(1, $authentications);
  211. $this->assertEquals($duringLastMonthAuthLog->id, $authentications[0]->id);
  212. }
  213. #[Test]
  214. public function test_authenticationsByPeriod_returns_last_three_months_auth_logs()
  215. {
  216. $user = User::factory()->create();
  217. $sixMonthsAgoAuthLog = AuthLog::factory()->duringLastSixMonth()->for($user, 'authenticatable')->create();
  218. $threeMonthsAgoAuthLog = AuthLog::factory()->duringLastThreeMonth()->for($user, 'authenticatable')->create();
  219. $duringLastMonthAuthLog = AuthLog::factory()->duringLastMonth()->for($user, 'authenticatable')->create();
  220. $authentications = $user->authenticationsByPeriod(3);
  221. $this->assertCount(2, $authentications);
  222. $this->assertEquals($duringLastMonthAuthLog->id, $authentications[0]->id);
  223. $this->assertEquals($threeMonthsAgoAuthLog->id, $authentications[1]->id);
  224. }
  225. #[Test]
  226. public function test_latestAuthentication_returns_user_latest_auth_logs()
  227. {
  228. $user = User::factory()->create();
  229. $twoMonthsAgoAuthLog = AuthLog::factory()->duringLastThreeMonth()->for($user, 'authenticatable')->create();
  230. $duringLastMonthAuthLog = AuthLog::factory()->duringLastMonth()->for($user, 'authenticatable')->create();
  231. $authentications = $user->latestAuthentication()->get();
  232. $this->assertCount(1, $authentications);
  233. $this->assertEquals($duringLastMonthAuthLog->id, $authentications[0]->id);
  234. }
  235. #[Test]
  236. public function test_latestAuthentication_returns_user_latest_auth_logs_only()
  237. {
  238. $user = User::factory()->create();
  239. $anotherUser = User::factory()->create();
  240. $userAuthLog = AuthLog::factory()->duringLastThreeMonth()->for($user, 'authenticatable')->create();
  241. $anotherUserAuthLog = AuthLog::factory()->duringLastMonth()->for($anotherUser, 'authenticatable')->create();
  242. $authentications = $user->latestAuthentication()->get();
  243. $this->assertCount(1, $authentications);
  244. $this->assertEquals($userAuthLog->id, $authentications[0]->id);
  245. }
  246. #[Test]
  247. public function test_lastLoginAt_returns_user_last_auth_date()
  248. {
  249. $user = User::factory()->create();
  250. $now = now();
  251. $tenDaysAgoAuthLog = AuthLog::factory()->daysAgo(10)->for($user, 'authenticatable')->create();
  252. $fiveDaysAgoAuthLog = AuthLog::factory()->daysAgo(5)->for($user, 'authenticatable')->create();
  253. $lastAuthLog = AuthLog::factory()->at($now)->for($user, 'authenticatable')->create();
  254. $lastLoginAt = $user->lastLoginAt();
  255. $this->assertEquals($lastLoginAt->startOfSecond(), $now->startOfSecond());
  256. }
  257. #[Test]
  258. public function test_lastLoginAt_returns_null_if_user_has_no_login()
  259. {
  260. $user = User::factory()->create();
  261. AuthLog::factory()->logoutOnly()->for($user, 'authenticatable')->create();
  262. $lastLoginAt = $user->lastLoginAt();
  263. $this->assertNull($lastLoginAt);
  264. }
  265. #[Test]
  266. public function test_lastSuccessfulLoginAt_returns_user_last_successful_login_date()
  267. {
  268. $user = User::factory()->create();
  269. $now = now();
  270. AuthLog::factory()->at($now)->for($user, 'authenticatable')->create();
  271. $lastSuccessfulLoginAt = $user->lastSuccessfulLoginAt();
  272. $this->assertEquals($lastSuccessfulLoginAt->startOfSecond(), $now->startOfSecond());
  273. }
  274. #[Test]
  275. public function test_lastSuccessfulLoginAt_returns_null_if_user_has_no_successful_login()
  276. {
  277. $user = User::factory()->create();
  278. $now = now();
  279. AuthLog::factory()->at($now)->failedLogin()->for($user, 'authenticatable')->create();
  280. $lastSuccessfulLoginAt = $user->lastSuccessfulLoginAt();
  281. $this->assertNull($lastSuccessfulLoginAt);
  282. }
  283. #[Test]
  284. public function test_lastLoginIp_returns_user_last_login_ip()
  285. {
  286. $user = User::factory()->create();
  287. AuthLog::factory()->for($user, 'authenticatable')->create();
  288. $lastLoginIp = $user->lastLoginIp();
  289. $this->assertEquals(AuthLogFactory::IP, $lastLoginIp);
  290. }
  291. #[Test]
  292. public function test_lastLoginIp_returns_null_if_user_has_no_auth_log()
  293. {
  294. $user = User::factory()->create();
  295. $lastLoginIp = $user->lastLoginIp();
  296. $this->assertNull($lastLoginIp);
  297. }
  298. #[Test]
  299. public function test_lastSuccessfulLoginIp_returns_user_last_successful_login_ip()
  300. {
  301. $user = User::factory()->create();
  302. AuthLog::factory()->for($user, 'authenticatable')->create();
  303. $lastSuccessfulLoginIp = $user->lastSuccessfulLoginIp();
  304. $this->assertEquals(AuthLogFactory::IP, $lastSuccessfulLoginIp);
  305. }
  306. #[Test]
  307. public function test_lastSuccessfulLoginIp_returns_null_if_user_has_no_successful_login()
  308. {
  309. $user = User::factory()->create();
  310. AuthLog::factory()->failedLogin()->for($user, 'authenticatable')->create();
  311. $lastSuccessfulLoginIp = $user->lastSuccessfulLoginIp();
  312. $this->assertNull($lastSuccessfulLoginIp);
  313. }
  314. #[Test]
  315. public function test_previousLoginAt_returns_user_last_auth_date()
  316. {
  317. $user = User::factory()->create();
  318. $now = now();
  319. $yesterday = now()->subDay();
  320. $yesterdayAuthLog = AuthLog::factory()->at($yesterday)->for($user, 'authenticatable')->create();
  321. $lastAuthLog = AuthLog::factory()->at($now)->for($user, 'authenticatable')->create();
  322. $previousLoginAt = $user->previousLoginAt();
  323. $this->assertEquals($previousLoginAt->startOfSecond(), $yesterday->startOfSecond());
  324. }
  325. #[Test]
  326. public function test_previousLoginAt_returns_null_if_user_has_no_auth_log()
  327. {
  328. $user = User::factory()->create();
  329. $previousLoginAt = $user->previousLoginAt();
  330. $this->assertNull($previousLoginAt);
  331. }
  332. #[Test]
  333. public function test_previousLoginIp_returns_user_last_auth_ip()
  334. {
  335. $user = User::factory()->create();
  336. $yesterday = now()->subDay();
  337. AuthLog::factory()->for($user, 'authenticatable')->create();
  338. AuthLog::factory()->at($yesterday)->for($user, 'authenticatable')->create();
  339. $previousLoginIp = $user->previousLoginIp();
  340. $this->assertEquals(AuthLogFactory::IP, $previousLoginIp);
  341. }
  342. #[Test]
  343. public function test_previousLoginIp_returns_null_if_user_has_no_auth_log()
  344. {
  345. $user = User::factory()->create();
  346. $previousLoginIp = $user->previousLoginIp();
  347. $this->assertNull($previousLoginIp);
  348. }
  349. }