2018-10-08 19:30:37 +02:00
|
|
|
<?php
|
|
|
|
|
2023-02-03 20:41:59 +01:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2018-10-08 19:30:37 +02:00
|
|
|
namespace Engelsystem\Test\Unit\Helpers;
|
|
|
|
|
|
|
|
use Engelsystem\Helpers\Authenticator;
|
2023-01-28 00:41:29 +01:00
|
|
|
use Engelsystem\Http\Request;
|
2022-11-06 12:41:52 +01:00
|
|
|
use Engelsystem\Models\Group;
|
|
|
|
use Engelsystem\Models\Privilege;
|
2018-10-08 19:30:37 +02:00
|
|
|
use Engelsystem\Models\User\User;
|
2018-11-27 12:01:36 +01:00
|
|
|
use Engelsystem\Test\Unit\HasDatabase;
|
2018-10-08 19:30:37 +02:00
|
|
|
use Engelsystem\Test\Unit\Helpers\Stub\UserModelImplementation;
|
|
|
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
2023-11-15 18:33:34 +01:00
|
|
|
use Illuminate\Support\Str;
|
2018-10-08 19:30:37 +02:00
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
2018-10-11 01:26:34 +02:00
|
|
|
use Psr\Http\Message\ServerRequestInterface;
|
2018-10-08 19:30:37 +02:00
|
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
2023-01-28 00:41:29 +01:00
|
|
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
2018-10-08 19:30:37 +02:00
|
|
|
|
|
|
|
class AuthenticatorTest extends ServiceProviderTest
|
|
|
|
{
|
2018-11-27 12:01:36 +01:00
|
|
|
use HasDatabase;
|
|
|
|
|
2023-09-24 23:37:34 +02:00
|
|
|
protected static ?string $passwordHashTesting = null;
|
|
|
|
|
|
|
|
public static function setUpBeforeClass(): void
|
|
|
|
{
|
|
|
|
parent::setUpBeforeClass();
|
|
|
|
self::$passwordHashTesting = password_hash('testing', PASSWORD_ARGON2I, ['memory_cost' => 100]);
|
|
|
|
}
|
|
|
|
|
2018-10-08 19:30:37 +02:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::user
|
2023-01-28 00:41:29 +01:00
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::__construct
|
2018-10-08 19:30:37 +02:00
|
|
|
*/
|
2023-01-28 00:41:29 +01:00
|
|
|
public function testUserNotAuthorized(): void
|
2018-10-08 19:30:37 +02:00
|
|
|
{
|
2023-01-28 00:41:29 +01:00
|
|
|
$request = new Request();
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
2018-10-08 19:30:37 +02:00
|
|
|
/** @var UserModelImplementation|MockObject $userRepository */
|
|
|
|
$userRepository = new UserModelImplementation();
|
2023-01-28 00:41:29 +01:00
|
|
|
$this->app->instance('request', $request);
|
2018-10-08 19:30:37 +02:00
|
|
|
|
2018-10-11 01:26:34 +02:00
|
|
|
$auth = new Authenticator($request, $session, $userRepository);
|
2023-01-28 00:41:29 +01:00
|
|
|
$user = $auth->user();
|
|
|
|
|
|
|
|
$this->assertNull($user);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::user
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userFromSession
|
|
|
|
*/
|
|
|
|
public function testUserViaFromSession(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
2018-10-08 19:30:37 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$request = new Request();
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
2018-10-08 19:30:37 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$session->set('user_id', 42);
|
|
|
|
User::factory()->create(['id' => 42]);
|
2018-10-08 19:30:37 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
$user = $auth->user();
|
2018-10-08 19:30:37 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$this->assertInstanceOf(User::class, $user);
|
|
|
|
$this->assertEquals(42, $user->id);
|
|
|
|
|
|
|
|
// Cached in user()
|
|
|
|
$user2 = $auth->user();
|
|
|
|
$this->assertEquals($user, $user2);
|
|
|
|
|
|
|
|
// Cached in userFromSession()
|
|
|
|
$user3 = $auth->userFromSession();
|
|
|
|
$this->assertEquals($user, $user3);
|
2018-10-08 19:30:37 +02:00
|
|
|
}
|
2018-10-11 01:26:34 +02:00
|
|
|
|
|
|
|
/**
|
2023-01-28 00:41:29 +01:00
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::user
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userFromApi
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userByHeaders
|
2018-10-11 01:26:34 +02:00
|
|
|
*/
|
2023-01-28 00:41:29 +01:00
|
|
|
public function testUserViaFromApi(): void
|
2018-10-11 01:26:34 +02:00
|
|
|
{
|
2023-01-28 00:41:29 +01:00
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
|
|
|
|
|
|
|
$request = $request->withHeader('Authorization', 'Bearer F00Bar');
|
|
|
|
$request = $request->withAttribute('route-api', true);
|
|
|
|
$this->app->instance('request', $request);
|
|
|
|
User::factory()->create(['api_key' => 'F00Bar']);
|
2018-10-11 01:26:34 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
$user = $auth->user();
|
2018-10-11 01:26:34 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$this->assertInstanceOf(User::class, $user);
|
|
|
|
$this->assertEquals('F00Bar', $user->api_key);
|
2018-10-11 01:26:34 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
// Cached in userFromApi()
|
|
|
|
$user2 = $auth->userFromApi();
|
|
|
|
$this->assertEquals($user, $user2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userFromSession
|
|
|
|
*/
|
|
|
|
public function testUserFromSessionNotFound(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
|
|
|
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
|
|
|
|
$user = $auth->userFromSession();
|
|
|
|
$this->assertNull($user);
|
|
|
|
|
|
|
|
$session->set('user_id', 42);
|
|
|
|
$user2 = $auth->userFromSession();
|
|
|
|
$this->assertNull($user2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userFromApi
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userByQueryParam
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userByApiKey
|
|
|
|
*/
|
|
|
|
public function testUserFromApiByQueryParam(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
|
|
|
|
|
|
|
$request = $request->withQueryParams(['key' => 'F00Bar']);
|
|
|
|
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
|
|
|
|
// User not found
|
|
|
|
$user = $auth->userFromApi();
|
|
|
|
$this->assertNull($user);
|
|
|
|
|
|
|
|
// User exists
|
|
|
|
User::factory()->create(['api_key' => 'F00Bar']);
|
|
|
|
$user2 = $auth->userFromApi();
|
|
|
|
$this->assertInstanceOf(User::class, $user2);
|
|
|
|
$this->assertEquals('F00Bar', $user2->api_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::userByHeaders
|
|
|
|
*/
|
|
|
|
public function testUserByHeaders(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
2018-10-11 01:26:34 +02:00
|
|
|
|
2023-01-28 00:41:29 +01:00
|
|
|
$request = new Request();
|
|
|
|
$request = $request->withAttribute('route-api', true);
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
|
|
|
$this->app->instance('request', $request);
|
|
|
|
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
|
|
|
|
// Header not set
|
|
|
|
$user = $auth->userFromApi();
|
|
|
|
$this->assertNull($user);
|
|
|
|
|
|
|
|
// User not found
|
|
|
|
$request = $request->withHeader('x-api-key', 'SomeWrongKey');
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
$user = $auth->userFromApi();
|
|
|
|
$this->assertNull($user);
|
|
|
|
|
|
|
|
$request = $request->withHeader('x-api-key', 'F00Bar');
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
User::factory()->create(['api_key' => 'F00Bar']);
|
|
|
|
$user = $auth->user();
|
|
|
|
$this->assertInstanceOf(User::class, $user);
|
|
|
|
$this->assertEquals('F00Bar', $user->api_key);
|
2018-10-11 01:26:34 +02:00
|
|
|
}
|
2018-11-12 14:41:23 +01:00
|
|
|
|
2023-11-15 18:33:34 +01:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::resetApiKey
|
|
|
|
*/
|
|
|
|
public function testResetApiKey(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
$oldKey = $user->api_key;
|
|
|
|
|
|
|
|
$auth = new Authenticator(new Request(), new Session(new MockArraySessionStorage()), new User());
|
|
|
|
$auth->resetApiKey($user);
|
|
|
|
|
|
|
|
$updatedUser = User::all()->last();
|
|
|
|
$newApiKey = $updatedUser->api_key;
|
|
|
|
|
|
|
|
$this->assertNotEquals($oldKey, $newApiKey);
|
|
|
|
$this->assertTrue(Str::isAscii($newApiKey));
|
|
|
|
$this->assertEquals(64, Str::length($newApiKey));
|
|
|
|
}
|
|
|
|
|
2018-11-12 14:41:23 +01:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::can
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testCan(): void
|
2018-11-12 14:41:23 +01:00
|
|
|
{
|
2022-11-06 12:41:52 +01:00
|
|
|
$this->initDatabase();
|
|
|
|
|
2023-10-23 00:40:49 +02:00
|
|
|
$request = new Request();
|
|
|
|
$this->app->instance('request', $request);
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
2022-11-06 12:41:52 +01:00
|
|
|
/** @var User $user */
|
|
|
|
$user = User::factory()->create();
|
|
|
|
/** @var Group $group */
|
|
|
|
$group = Group::factory()->create();
|
|
|
|
/** @var Privilege $privilege */
|
|
|
|
$privilege = Privilege::factory()->create(['name' => 'bar']);
|
|
|
|
|
|
|
|
$user->groups()->attach($group);
|
|
|
|
$group->privileges()->attach($privilege);
|
2018-11-12 14:41:23 +01:00
|
|
|
|
2023-10-23 00:40:49 +02:00
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
$session->set('user_id', $user->id);
|
2018-11-12 14:41:23 +01:00
|
|
|
// User exists, has permissions
|
|
|
|
$this->assertTrue($auth->can('bar'));
|
|
|
|
|
|
|
|
// Permissions cached
|
|
|
|
$this->assertTrue($auth->can('bar'));
|
|
|
|
}
|
2023-10-23 00:40:49 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::can
|
|
|
|
*/
|
|
|
|
public function testCanUnauthorized(): void
|
|
|
|
{
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
$request = new Request();
|
|
|
|
$this->app->instance('request', $request);
|
|
|
|
$session = new Session(new MockArraySessionStorage());
|
|
|
|
|
|
|
|
$auth = new Authenticator($request, $session, new User());
|
|
|
|
$session->set('user_id', 42);
|
|
|
|
|
|
|
|
// No user, no permissions
|
|
|
|
$this->assertFalse($auth->can('foo'));
|
|
|
|
// Old/invalid user id got removed
|
|
|
|
$this->assertNull($session->get('user_id'));
|
|
|
|
}
|
2018-11-27 12:01:36 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::authenticate
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testAuthenticate(): void
|
2018-11-27 12:01:36 +01:00
|
|
|
{
|
|
|
|
$this->initDatabase();
|
|
|
|
|
|
|
|
/** @var ServerRequestInterface|MockObject $request */
|
|
|
|
$request = $this->getMockForAbstractClass(ServerRequestInterface::class);
|
|
|
|
/** @var Session|MockObject $session */
|
|
|
|
$session = $this->createMock(Session::class);
|
|
|
|
$userRepository = new User();
|
|
|
|
|
2021-06-29 00:27:57 +02:00
|
|
|
User::factory([
|
2018-11-27 12:01:36 +01:00
|
|
|
'name' => 'lorem',
|
2023-09-24 23:37:34 +02:00
|
|
|
'password' => self::$passwordHashTesting,
|
2018-11-27 12:01:36 +01:00
|
|
|
'email' => 'lorem@foo.bar',
|
2021-06-29 00:27:57 +02:00
|
|
|
])->create();
|
|
|
|
User::factory([
|
2018-11-27 12:01:36 +01:00
|
|
|
'name' => 'ipsum',
|
|
|
|
'password' => '',
|
2021-06-29 00:27:57 +02:00
|
|
|
])->create();
|
2018-11-27 12:01:36 +01:00
|
|
|
|
|
|
|
$auth = new Authenticator($request, $session, $userRepository);
|
|
|
|
$this->assertNull($auth->authenticate('not-existing', 'foo'));
|
|
|
|
$this->assertNull($auth->authenticate('ipsum', 'wrong-password'));
|
|
|
|
$this->assertInstanceOf(User::class, $auth->authenticate('lorem', 'testing'));
|
|
|
|
$this->assertInstanceOf(User::class, $auth->authenticate('lorem@foo.bar', 'testing'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::verifyPassword
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testVerifyPassword(): void
|
2018-11-27 12:01:36 +01:00
|
|
|
{
|
|
|
|
$this->initDatabase();
|
2023-09-24 23:37:34 +02:00
|
|
|
$password = self::$passwordHashTesting;
|
|
|
|
|
2021-06-29 00:27:57 +02:00
|
|
|
/** @var User $user */
|
|
|
|
$user = User::factory([
|
2018-11-27 12:01:36 +01:00
|
|
|
'name' => 'lorem',
|
|
|
|
'password' => $password,
|
2021-06-29 00:27:57 +02:00
|
|
|
])->create();
|
2018-11-27 12:01:36 +01:00
|
|
|
|
|
|
|
/** @var Authenticator|MockObject $auth */
|
|
|
|
$auth = $this->getMockBuilder(Authenticator::class)
|
|
|
|
->disableOriginalConstructor()
|
2019-11-06 12:29:58 +01:00
|
|
|
->onlyMethods(['setPassword'])
|
2018-11-27 12:01:36 +01:00
|
|
|
->getMock();
|
|
|
|
|
|
|
|
$auth->expects($this->once())
|
|
|
|
->method('setPassword')
|
|
|
|
->with($user, 'testing');
|
|
|
|
$auth->setPasswordAlgorithm(PASSWORD_BCRYPT);
|
|
|
|
|
|
|
|
$this->assertFalse($auth->verifyPassword($user, 'randomStuff'));
|
|
|
|
$this->assertTrue($auth->verifyPassword($user, 'testing'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::setPassword
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testSetPassword(): void
|
2018-11-27 12:01:36 +01:00
|
|
|
{
|
|
|
|
$this->initDatabase();
|
2021-06-29 00:27:57 +02:00
|
|
|
/** @var User $user */
|
|
|
|
$user = User::factory([
|
2018-11-27 12:01:36 +01:00
|
|
|
'name' => 'ipsum',
|
|
|
|
'password' => '',
|
2021-06-29 00:27:57 +02:00
|
|
|
])->create();
|
2018-11-27 12:01:36 +01:00
|
|
|
$user->save();
|
|
|
|
|
|
|
|
$auth = $this->getAuthenticator();
|
2023-09-24 23:37:34 +02:00
|
|
|
$auth->setPasswordAlgorithm(PASSWORD_BCRYPT);
|
2018-11-27 12:01:36 +01:00
|
|
|
|
|
|
|
$auth->setPassword($user, 'FooBar');
|
|
|
|
$this->assertTrue($user->isClean());
|
|
|
|
|
|
|
|
$this->assertTrue(password_verify('FooBar', $user->password));
|
2023-09-24 23:37:34 +02:00
|
|
|
$this->assertFalse(password_needs_rehash($user->password, PASSWORD_BCRYPT));
|
2018-11-27 12:01:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::setPasswordAlgorithm
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::getPasswordAlgorithm
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testPasswordAlgorithm(): void
|
2018-11-27 12:01:36 +01:00
|
|
|
{
|
|
|
|
$auth = $this->getAuthenticator();
|
|
|
|
|
|
|
|
$auth->setPasswordAlgorithm(PASSWORD_ARGON2I);
|
|
|
|
$this->assertEquals(PASSWORD_ARGON2I, $auth->getPasswordAlgorithm());
|
|
|
|
}
|
|
|
|
|
2022-11-06 12:41:52 +01:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::setDefaultRole
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::getDefaultRole
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testDefaultRole(): void
|
2022-11-06 12:41:52 +01:00
|
|
|
{
|
|
|
|
$auth = $this->getAuthenticator();
|
|
|
|
|
|
|
|
$auth->setDefaultRole(1337);
|
|
|
|
$this->assertEquals(1337, $auth->getDefaultRole());
|
|
|
|
}
|
|
|
|
|
2019-07-28 15:33:01 +02:00
|
|
|
/**
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::setGuestRole
|
|
|
|
* @covers \Engelsystem\Helpers\Authenticator::getGuestRole
|
|
|
|
*/
|
2022-12-14 19:15:20 +01:00
|
|
|
public function testGuestRole(): void
|
2019-07-28 15:33:01 +02:00
|
|
|
{
|
|
|
|
$auth = $this->getAuthenticator();
|
|
|
|
|
|
|
|
$auth->setGuestRole(42);
|
|
|
|
$this->assertEquals(42, $auth->getGuestRole());
|
|
|
|
}
|
|
|
|
|
2022-12-14 19:15:20 +01:00
|
|
|
protected function getAuthenticator(): Authenticator
|
2018-11-27 12:01:36 +01:00
|
|
|
{
|
2022-11-06 12:41:52 +01:00
|
|
|
return new class extends Authenticator {
|
2018-11-27 12:01:36 +01:00
|
|
|
/** @noinspection PhpMissingParentConstructorInspection */
|
2019-11-10 23:26:23 +01:00
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
}
|
2018-11-27 12:01:36 +01:00
|
|
|
};
|
|
|
|
}
|
2018-10-08 19:30:37 +02:00
|
|
|
}
|