diff --git a/src/DataFixtures/APITokenFixtures.php b/src/DataFixtures/APITokenFixtures.php new file mode 100644 index 00000000..4bcf3a60 --- /dev/null +++ b/src/DataFixtures/APITokenFixtures.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + + +namespace App\DataFixtures; + +use App\Entity\UserSystem\ApiToken; +use App\Entity\UserSystem\ApiTokenLevel; +use App\Entity\UserSystem\User; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Doctrine\Persistence\ObjectManager; + +class APITokenFixtures extends Fixture implements DependentFixtureInterface +{ + public const TOKEN_READONLY = 'tcp_readonly'; + public const TOKEN_EDIT = 'tcp_edit'; + public const TOKEN_ADMIN = 'tcp_admin'; + public const TOKEN_FULL = 'tcp_full'; + public const TOKEN_EXPIRED = 'tcp_expired'; + + public function load(ObjectManager $manager): void + { + /** @var User $admin_user */ + $admin_user = $this->getReference(UserFixtures::ADMIN); + + $read_only_token = new ApiToken(); + $read_only_token->setUser($admin_user); + $read_only_token->setLevel(ApiTokenLevel::READ_ONLY); + $read_only_token->setName('read-only'); + $this->setTokenSecret($read_only_token, self::TOKEN_READONLY); + $manager->persist($read_only_token); + + $editor_token = new ApiToken(); + $editor_token->setUser($admin_user); + $editor_token->setLevel(ApiTokenLevel::EDIT); + $editor_token->setName('edit'); + $this->setTokenSecret($editor_token, self::TOKEN_EDIT); + $manager->persist($editor_token); + + $admin_token = new ApiToken(); + $admin_token->setUser($admin_user); + $admin_token->setLevel(ApiTokenLevel::ADMIN); + $admin_token->setName('admin'); + $this->setTokenSecret($admin_token, self::TOKEN_ADMIN); + $manager->persist($admin_token); + + $full_token = new ApiToken(); + $full_token->setUser($admin_user); + $full_token->setLevel(ApiTokenLevel::FULL); + $full_token->setName('full'); + $this->setTokenSecret($full_token, self::TOKEN_FULL); + $manager->persist($full_token); + + $expired_token = new ApiToken(); + $expired_token->setUser($admin_user); + $expired_token->setLevel(ApiTokenLevel::FULL); + $expired_token->setName('expired'); + $expired_token->setValidUntil(new \DateTimeImmutable('-1 day')); + $this->setTokenSecret($expired_token, self::TOKEN_EXPIRED); + $manager->persist($expired_token); + + $manager->flush(); + } + + private function setTokenSecret(ApiToken $token, string $secret): void + { + //Access private property + $reflection = new \ReflectionClass($token); + $property = $reflection->getProperty('token'); + $property->setValue($token, $secret); + } + + public function getDependencies(): array + { + return [UserFixtures::class]; + } +} \ No newline at end of file diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php index d34a83cd..0571a4b0 100644 --- a/src/DataFixtures/UserFixtures.php +++ b/src/DataFixtures/UserFixtures.php @@ -31,6 +31,8 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; class UserFixtures extends Fixture implements DependentFixtureInterface { + public const ADMIN = 'user-admin'; + public function __construct(protected UserPasswordHasherInterface $encoder, protected EntityManagerInterface $em) { } @@ -50,6 +52,7 @@ class UserFixtures extends Fixture implements DependentFixtureInterface $admin->setNeedPwChange(false); $admin->setGroup($this->getReference(GroupFixtures::ADMINS)); $manager->persist($admin); + $this->addReference(self::ADMIN, $admin); $user = new User(); $user->setName('user'); diff --git a/src/Entity/UserSystem/User.php b/src/Entity/UserSystem/User.php index e50a90ba..0404b406 100644 --- a/src/Entity/UserSystem/User.php +++ b/src/Entity/UserSystem/User.php @@ -1023,6 +1023,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe */ public function addApiToken(ApiToken $apiToken): void { + $apiToken->setUser($this); $this->api_tokens->add($apiToken); } diff --git a/tests/API/APITokenAuthenticationTest.php b/tests/API/APITokenAuthenticationTest.php new file mode 100644 index 00000000..3e1ca599 --- /dev/null +++ b/tests/API/APITokenAuthenticationTest.php @@ -0,0 +1,103 @@ +. + */ + +declare(strict_types=1); + + +namespace API; + +use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use App\DataFixtures\APITokenFixtures; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use ApiPlatform\Symfony\Bundle\Test\Client; +class APITokenAuthenticationTest extends ApiTestCase +{ + public function testUnauthenticated(): void + { + self::ensureKernelShutdown(); + $client = static::createClient(); + $client->request('GET', '/api/parts'); + self::assertResponseStatusCodeSame(401); + } + + public function testExpiredToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_EXPIRED); + $client->request('GET', '/api/parts'); + self::assertResponseStatusCodeSame(401); + } + + public function testReadOnlyToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_READONLY); + + //Read should be possible + $client->request('GET', '/api/parts'); + self::assertResponseIsSuccessful(); + + //Trying to list all users and create a new footprint should fail + $client->request('GET', '/api/users'); + self::assertResponseStatusCodeSame(403); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + self::assertResponseStatusCodeSame(403); + } + + public function testEditToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_EDIT); + + //Read should be possible + $client->request('GET', '/api/parts'); + self::assertResponseIsSuccessful(); + + //Trying to list all users + $client->request('GET', '/api/users'); + self::assertResponseStatusCodeSame(403); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + self::assertResponseIsSuccessful(); + } + + public function testAdminToken(): void + { + self::ensureKernelShutdown(); + $client = $this->createClientWithCredentials(APITokenFixtures::TOKEN_ADMIN ); + + //Read should be possible + $client->request('GET', '/api/parts'); + self::assertResponseIsSuccessful(); + + //Trying to list all users + $client->request('GET', '/api/users'); + self::assertResponseIsSuccessful(); + + $client->request('POST', '/api/footprints', ['json' => ['name' => 'post test']]); + self::assertResponseIsSuccessful(); + } + + protected function createClientWithCredentials(string $token): Client + { + return static::createClient([], ['headers' => ['authorization' => 'Bearer '.$token]]); + } +} \ No newline at end of file