AuthenticationTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <?php
  2. namespace Illuminate\Tests\Integration\Auth;
  3. use Illuminate\Auth\EloquentUserProvider;
  4. use Illuminate\Auth\Events\Attempting;
  5. use Illuminate\Auth\Events\Authenticated;
  6. use Illuminate\Auth\Events\Failed;
  7. use Illuminate\Auth\Events\Login;
  8. use Illuminate\Auth\Events\Logout;
  9. use Illuminate\Auth\Events\OtherDeviceLogout;
  10. use Illuminate\Auth\Events\Validated;
  11. use Illuminate\Auth\SessionGuard;
  12. use Illuminate\Database\Schema\Blueprint;
  13. use Illuminate\Events\Dispatcher;
  14. use Illuminate\Support\Facades\Auth;
  15. use Illuminate\Support\Facades\Event;
  16. use Illuminate\Support\Facades\Schema;
  17. use Illuminate\Support\Str;
  18. use Illuminate\Support\Testing\Fakes\EventFake;
  19. use Illuminate\Tests\Integration\Auth\Fixtures\AuthenticationTestUser;
  20. use InvalidArgumentException;
  21. use Orchestra\Testbench\TestCase;
  22. class AuthenticationTest extends TestCase
  23. {
  24. protected function getEnvironmentSetUp($app)
  25. {
  26. $app['config']->set('auth.providers.users.model', AuthenticationTestUser::class);
  27. $app['config']->set('hashing', ['driver' => 'bcrypt']);
  28. }
  29. protected function setUp(): void
  30. {
  31. parent::setUp();
  32. Schema::create('users', function (Blueprint $table) {
  33. $table->increments('id');
  34. $table->string('email');
  35. $table->string('username');
  36. $table->string('password');
  37. $table->string('remember_token')->default(null)->nullable();
  38. $table->tinyInteger('is_active')->default(0);
  39. });
  40. AuthenticationTestUser::create([
  41. 'username' => 'username',
  42. 'email' => 'email',
  43. 'password' => bcrypt('password'),
  44. 'is_active' => true,
  45. ]);
  46. $this->app->make('router')->get('basic', function () {
  47. return $this->app['auth']->guard()->basic()
  48. ?: $this->app['auth']->user()->toJson();
  49. });
  50. $this->app->make('router')->get('basicWithCondition', function () {
  51. return $this->app['auth']->guard()->basic('email', ['is_active' => true])
  52. ?: $this->app['auth']->user()->toJson();
  53. });
  54. }
  55. public function testBasicAuthProtectsRoute()
  56. {
  57. $this->get('basic')->assertStatus(401);
  58. }
  59. public function testBasicAuthPassesOnCorrectCredentials()
  60. {
  61. $response = $this->get('basic', [
  62. 'Authorization' => 'Basic '.base64_encode('email:password'),
  63. ]);
  64. $response->assertStatus(200);
  65. $this->assertSame('email', $response->json()['email']);
  66. }
  67. public function testBasicAuthRespectsAdditionalConditions()
  68. {
  69. AuthenticationTestUser::create([
  70. 'username' => 'username2',
  71. 'email' => 'email2',
  72. 'password' => bcrypt('password'),
  73. 'is_active' => false,
  74. ]);
  75. $this->get('basicWithCondition', [
  76. 'Authorization' => 'Basic '.base64_encode('email2:password2'),
  77. ])->assertStatus(401);
  78. $this->get('basicWithCondition', [
  79. 'Authorization' => 'Basic '.base64_encode('email:password'),
  80. ])->assertStatus(200);
  81. }
  82. public function testBasicAuthFailsOnWrongCredentials()
  83. {
  84. $this->get('basic', [
  85. 'Authorization' => 'Basic '.base64_encode('email:wrong_password'),
  86. ])->assertStatus(401);
  87. }
  88. public function testLoggingInFailsViaAttempt()
  89. {
  90. Event::fake();
  91. $this->assertFalse(
  92. $this->app['auth']->attempt(['email' => 'wrong', 'password' => 'password'])
  93. );
  94. $this->assertFalse($this->app['auth']->check());
  95. $this->assertNull($this->app['auth']->user());
  96. Event::assertDispatched(Attempting::class, function ($event) {
  97. $this->assertSame('web', $event->guard);
  98. $this->assertEquals(['email' => 'wrong', 'password' => 'password'], $event->credentials);
  99. return true;
  100. });
  101. Event::assertNotDispatched(Validated::class);
  102. Event::assertDispatched(Failed::class, function ($event) {
  103. $this->assertSame('web', $event->guard);
  104. $this->assertEquals(['email' => 'wrong', 'password' => 'password'], $event->credentials);
  105. $this->assertNull($event->user);
  106. return true;
  107. });
  108. }
  109. public function testLoggingInSucceedsViaAttempt()
  110. {
  111. Event::fake();
  112. $this->assertTrue(
  113. $this->app['auth']->attempt(['email' => 'email', 'password' => 'password'])
  114. );
  115. $this->assertInstanceOf(AuthenticationTestUser::class, $this->app['auth']->user());
  116. $this->assertTrue($this->app['auth']->check());
  117. Event::assertDispatched(Attempting::class, function ($event) {
  118. $this->assertSame('web', $event->guard);
  119. $this->assertEquals(['email' => 'email', 'password' => 'password'], $event->credentials);
  120. return true;
  121. });
  122. Event::assertDispatched(Validated::class, function ($event) {
  123. $this->assertSame('web', $event->guard);
  124. $this->assertEquals(1, $event->user->id);
  125. return true;
  126. });
  127. Event::assertDispatched(Login::class, function ($event) {
  128. $this->assertSame('web', $event->guard);
  129. $this->assertEquals(1, $event->user->id);
  130. return true;
  131. });
  132. Event::assertDispatched(Authenticated::class, function ($event) {
  133. $this->assertSame('web', $event->guard);
  134. $this->assertEquals(1, $event->user->id);
  135. return true;
  136. });
  137. }
  138. public function testLoggingInUsingId()
  139. {
  140. $this->app['auth']->loginUsingId(1);
  141. $this->assertEquals(1, $this->app['auth']->user()->id);
  142. $this->assertFalse($this->app['auth']->loginUsingId(1000));
  143. }
  144. public function testLoggingOut()
  145. {
  146. Event::fake();
  147. $this->app['auth']->loginUsingId(1);
  148. $this->assertEquals(1, $this->app['auth']->user()->id);
  149. $this->app['auth']->logout();
  150. $this->assertNull($this->app['auth']->user());
  151. Event::assertDispatched(Logout::class, function ($event) {
  152. $this->assertSame('web', $event->guard);
  153. $this->assertEquals(1, $event->user->id);
  154. return true;
  155. });
  156. }
  157. public function testLoggingOutOtherDevices()
  158. {
  159. Event::fake();
  160. $this->app['auth']->loginUsingId(1);
  161. $user = $this->app['auth']->user();
  162. $this->assertEquals(1, $user->id);
  163. $this->app['auth']->logoutOtherDevices('password');
  164. $this->assertEquals(1, $user->id);
  165. Event::assertDispatched(OtherDeviceLogout::class, function ($event) {
  166. $this->assertSame('web', $event->guard);
  167. $this->assertEquals(1, $event->user->id);
  168. return true;
  169. });
  170. }
  171. public function testPasswordMustBeValidToLogOutOtherDevices()
  172. {
  173. $this->expectException(InvalidArgumentException::class);
  174. $this->expectExceptionMessage('current password');
  175. $this->app['auth']->loginUsingId(1);
  176. $user = $this->app['auth']->user();
  177. $this->assertEquals(1, $user->id);
  178. $this->app['auth']->logoutOtherDevices('adifferentpassword');
  179. }
  180. public function testLoggingInOutViaAttemptRemembering()
  181. {
  182. $this->assertTrue(
  183. $this->app['auth']->attempt(['email' => 'email', 'password' => 'password'], true)
  184. );
  185. $this->assertInstanceOf(AuthenticationTestUser::class, $this->app['auth']->user());
  186. $this->assertTrue($this->app['auth']->check());
  187. $this->assertNotNull($this->app['auth']->user()->getRememberToken());
  188. $oldToken = $this->app['auth']->user()->getRememberToken();
  189. $user = $this->app['auth']->user();
  190. $this->app['auth']->logout();
  191. $this->assertNotNull($user->getRememberToken());
  192. $this->assertNotEquals($oldToken, $user->getRememberToken());
  193. }
  194. public function testLoggingInOutCurrentDeviceViaRemembering()
  195. {
  196. $this->assertTrue(
  197. $this->app['auth']->attempt(['email' => 'email', 'password' => 'password'], true)
  198. );
  199. $this->assertInstanceOf(AuthenticationTestUser::class, $this->app['auth']->user());
  200. $this->assertTrue($this->app['auth']->check());
  201. $this->assertNotNull($this->app['auth']->user()->getRememberToken());
  202. $oldToken = $this->app['auth']->user()->getRememberToken();
  203. $user = $this->app['auth']->user();
  204. $this->app['auth']->logoutCurrentDevice();
  205. $this->assertNotNull($user->getRememberToken());
  206. $this->assertEquals($oldToken, $user->getRememberToken());
  207. }
  208. public function testAuthViaAttemptRemembering()
  209. {
  210. $provider = new EloquentUserProvider(app('hash'), AuthenticationTestUser::class);
  211. $user = AuthenticationTestUser::create([
  212. 'username' => 'username2',
  213. 'email' => 'email2',
  214. 'password' => bcrypt('password'),
  215. 'remember_token' => $token = Str::random(),
  216. 'is_active' => false,
  217. ]);
  218. $this->assertEquals($user->id, $provider->retrieveByToken($user->id, $token)->id);
  219. $user->update([
  220. 'remember_token' => null,
  221. ]);
  222. $this->assertNull($provider->retrieveByToken($user->id, $token));
  223. }
  224. public function testDispatcherChangesIfThereIsOneOnTheAuthGuard()
  225. {
  226. $this->assertInstanceOf(SessionGuard::class, $this->app['auth']->guard());
  227. $this->assertInstanceOf(Dispatcher::class, $this->app['auth']->guard()->getDispatcher());
  228. Event::fake();
  229. $this->assertInstanceOf(SessionGuard::class, $this->app['auth']->guard());
  230. $this->assertInstanceOf(EventFake::class, $this->app['auth']->guard()->getDispatcher());
  231. }
  232. public function testDispatcherChangesIfThereIsOneOnTheCustomAuthGuard()
  233. {
  234. $this->app['config']['auth.guards.myGuard'] = [
  235. 'driver' => 'myCustomDriver',
  236. 'provider' => 'user',
  237. ];
  238. Auth::extend('myCustomDriver', function () {
  239. return new MyCustomGuardStub;
  240. });
  241. $this->assertInstanceOf(MyCustomGuardStub::class, $this->app['auth']->guard('myGuard'));
  242. $this->assertInstanceOf(Dispatcher::class, $this->app['auth']->guard()->getDispatcher());
  243. Event::fake();
  244. $this->assertInstanceOf(MyCustomGuardStub::class, $this->app['auth']->guard('myGuard'));
  245. $this->assertInstanceOf(EventFake::class, $this->app['auth']->guard()->getDispatcher());
  246. }
  247. public function testHasNoProblemIfThereIsNoDispatchingTheAuthCustomGuard()
  248. {
  249. $this->app['config']['auth.guards.myGuard'] = [
  250. 'driver' => 'myCustomDriver',
  251. 'provider' => 'user',
  252. ];
  253. Auth::extend('myCustomDriver', function () {
  254. return new MyDispatcherLessCustomGuardStub;
  255. });
  256. $this->assertInstanceOf(MyDispatcherLessCustomGuardStub::class, $this->app['auth']->guard('myGuard'));
  257. Event::fake();
  258. $this->assertInstanceOf(MyDispatcherLessCustomGuardStub::class, $this->app['auth']->guard('myGuard'));
  259. }
  260. }
  261. class MyCustomGuardStub
  262. {
  263. protected $events;
  264. public function __construct()
  265. {
  266. $this->setDispatcher(new Dispatcher);
  267. }
  268. public function setDispatcher(Dispatcher $events)
  269. {
  270. $this->events = $events;
  271. }
  272. public function getDispatcher()
  273. {
  274. return $this->events;
  275. }
  276. }
  277. class MyDispatcherLessCustomGuardStub
  278. {
  279. //
  280. }