Skip to content

Commit b3f8471

Browse files
committed
Project wide changes to fix authentication and ensure all tests are passing.
1 parent 8ca0932 commit b3f8471

14 files changed

+162
-27
lines changed

.env

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
###> symfony/framework-bundle ###
1717
APP_DEBUG=false
1818
APP_ENV=dev
19-
APP_SECRET=9971165787381817cca7e6ce4161c760
19+
APP_SECRET=s3cr3t
2020
###< symfony/framework-bundle ###
2121

2222
###> doctrine/doctrine-bundle ###
@@ -25,7 +25,7 @@ APP_SECRET=9971165787381817cca7e6ce4161c760
2525
#
2626
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
2727
# DATABASE_URL="postgresql://symfony:[email protected]:5432/app?serverVersion=13&charset=utf8"
28-
DATABASE_URL="mysql://root:root@localhost:3306/api?serverVersion=5.7"
28+
DATABASE_URL="mysql://root:root@localhost:3306/api?serverVersion=8.0"
2929
###< doctrine/doctrine-bundle ###
3030

3131
###> nelmio/cors-bundle ###
@@ -35,10 +35,16 @@ CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
3535
###> lexik/jwt-authentication-bundle ###
3636
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
3737
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
38-
JWT_PASSPHRASE=2fde84aa1be4b44e4b7f674d1fb2e5cd
38+
JWT_PASSPHRASE=changeMe
3939
JWT_TOKEN_TTL=900
4040
###< lexik/jwt-authentication-bundle ###
4141

4242
###> dant89/ix-api-framework ###
4343
APP_VERSION=2.0.0
44-
###< dant89/ix-api-framework ###
44+
###< dant89/ix-api-framework ###
45+
46+
###> docker ###
47+
HTTP_PORT=80
48+
HTTPS_PORT=433
49+
MYSQL_PORT=3303
50+
###< docker ###

.env.test

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ SYMFONY_DEPRECATIONS_HELPER=999999
88
PANTHER_APP_ENV=panther
99
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
1010
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
11-
DATABASE_URL="mysql://user:password@database:3306/ix_api_framework?serverVersion=5.7"
11+
DATABASE_URL="mysql://root:root@localhost:3306/api?serverVersion=8.0"
1212
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
1313
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
14-
JWT_PASSPHRASE=
14+
JWT_PASSPHRASE=test
1515
JWT_TOKEN_TTL=900
16+
HTTP_PORT=80
17+
HTTPS_PORT=443
18+
MYSQL_PORT=3303

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.idea/
22
/bin/
3+
/docker/mysql/
34

45
###> symfony/framework-bundle ###
56
/.env.local

README.md

+23-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
- IX-API V2 Schema: https://docs.ix-api.net/v2/redoc
55

66
## What is this framework?
7-
This project provides a base template for IX-API to make it simpler and faster to implement IX-API.
7+
The project provides a base template for IX-API to make it simpler and faster to implement.
88

9-
The project is written in PHP using the Symfony [API Platform](https://api-platform.com/) framework.
9+
The implementation framework is written in PHP using [Symfony](https://symfony.com/), [API Platform](https://api-platform.com/) framework.
10+
API Platform has extensive documentation which it is recommended to be familiar with to understand the inner workings.
1011

1112
## How does this framework work?
1213
API Platform maps class based entities to RESTful API endpoints, this suits the IX-API schema.
@@ -21,16 +22,34 @@ business logic to and from these classes to have a functioning implementation.
2122
- Swagger UI auto generated via API Platform
2223
- Docker image for quick development setup
2324

25+
## Installation
26+
1. Setup JWT keys with a password, **keep a note of this for the next step**
27+
```bash
28+
openssl genpkey -out config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096`
29+
openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout
30+
```
31+
2. Run `cp .env .env.local`
32+
3. Setup your local environment variables
33+
* Change `DATABASE_URL` if required, defaults to local Docker
34+
* Update `JWT_PASSPHRASE=` according to the step **1**.
35+
4. Run `docker-compose up`
36+
5. Browse to `https://localhost`
37+
2438
## Swagger UI
2539
![alt text](public/images/example1.png)
2640

2741
## How to enable certain entities only?
28-
It is possible to toggle enabled entities via the `config/packages/api_platform.yaml` config file:
42+
It is possible to toggle enabled entities via `config/packages/api_platform.yaml` config file:
2943
```yaml
3044
api_platform:
3145
mapping:
3246
paths:
3347
```
3448

49+
## Tests
50+
Tests can be run via the script: `run_tests.sh`.
51+
52+
Tests run on a separate `api_test` database that is populated with fixtures found in `/fixtures`.
53+
3554
## Version Support
36-
This branch supports the V2 schema, future branches will support further versions.
55+
This branch supports V2 schema, future branches will support further versions.

composer.lock

+12-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/bootstrap.php

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
use Symfony\Component\Dotenv\Dotenv;
4+
5+
require dirname(__DIR__) . '/vendor/autoload.php';
6+
7+
// Load cached env vars if the .env.local.php file exists
8+
// Run "composer dump-env prod" to create it (requires symfony/flex >=1.2)
9+
if (is_array($env = @include dirname(__DIR__) . '/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
10+
foreach ($env as $k => $v) {
11+
$_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v);
12+
}
13+
} elseif (!class_exists(Dotenv::class)) {
14+
throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
15+
} else {
16+
// load all the .env files
17+
(new Dotenv(false))->loadEnv(dirname(__DIR__) . '/.env');
18+
}
19+
20+
$_SERVER += $_ENV;
21+
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
22+
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
23+
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';

config/packages/api_platform.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ api_platform:
2626
enabled: false
2727

2828
title: 'IX-API Framework'
29-
description: 'IX-API Implementation Framework for Internet Exchanges'
29+
description: 'IX-API Implementation Framework'
3030
version: '%app_version%'
3131

3232
name_converter: Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter

config/services.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ services:
8585
- "%gesdinet_jwt_refresh_token.ttl_update%"
8686
- "@event_dispatcher"
8787

88+
App\Security\Authentication\RoleVoter:
89+
tags:
90+
- { name: security.voter }
91+
8892
gesdinet.jwtrefreshtoken.authenticator:
8993
class: App\Security\Authenticator\ApiKeyRoleAwareRefreshTokenAuthenticator
9094
arguments:

docker-compose.test.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: "3.4"
2+
3+
# Test environment override
4+
services:
5+
php:
6+
environment:
7+
APP_ENV: test

docker-compose.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ services:
6363
MYSQL_USER: ${MYSQL_USER:-api-platform}
6464
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-!ChangeMe!}
6565
volumes:
66-
- db_data:/var/lib/mysql:rw
66+
- ./docker/mysql:/var/lib/mysql:rw
6767
ports:
68-
- 3306:3306
68+
- ${MYSQL_PORT:-3306}:3306
6969
entrypoint:
7070
- /bin/bash
7171
- -c

docker/php/docker-entrypoint.sh

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
3636
echo "The database is now ready and reachable"
3737
fi
3838

39-
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
39+
if [ "$( find ./src/Migrations -iname '*.php' -print -quit )" ]; then
4040
php bin/console doctrine:migrations:migrate --no-interaction
4141
fi
42+
43+
if [ "$APP_ENV" != 'prod' ]; then
44+
php bin/console doctrine:database:create --env=test --if-not-exists
45+
php bin/console doctrine:migrations:migrate --env=test
46+
fi
4247
fi
4348
fi
4449

run_tests.sh

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
set -e
3+
4+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml up -d --build --wait
5+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml exec php bin/console doctrine:database:create --if-not-exists
6+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml exec php bin/console doctrine:migrations:migrate --no-interaction
7+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml exec php bin/console hautelook:fixtures:load --no-interaction
8+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml exec php bin/codecept run tests/functional/ProductOfferingsTest.php
9+
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.test.yml down
10+
11+
exit 1
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace App\Security\Authentication;
4+
5+
use App\Security\Role\RoleFactory;
6+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
7+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
8+
use Symfony\Component\Security\Core\Role\Role;
9+
10+
/**
11+
* Custom RoleVoter to handle our roles that do not require a ROLE_ prefix.
12+
*
13+
* Class RoleVoter
14+
* @package App\Security\Authentication
15+
*/
16+
class RoleVoter implements VoterInterface
17+
{
18+
/**
19+
* @var RoleFactory
20+
*/
21+
private $roleFactory;
22+
23+
public function __construct(RoleFactory $roleFactory)
24+
{
25+
$this->roleFactory = $roleFactory;
26+
}
27+
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function vote(TokenInterface $token, $subject, array $attributes)
32+
{
33+
$result = VoterInterface::ACCESS_ABSTAIN;
34+
$roleNames = $this->extractRoles($token);
35+
36+
foreach ($attributes as $attribute) {
37+
if (!$this->roleFactory->valid($attribute)) {
38+
continue;
39+
}
40+
41+
$result = VoterInterface::ACCESS_DENIED;
42+
foreach ($roleNames as $roleName) {
43+
if ($attribute === $roleName) {
44+
return VoterInterface::ACCESS_GRANTED;
45+
}
46+
}
47+
}
48+
49+
return $result;
50+
}
51+
52+
protected function extractRoles(TokenInterface $token)
53+
{
54+
return $token->getRoleNames();
55+
}
56+
}

tests/functional/ProductOfferingsTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ public function testGetProductOfferings()
2121

2222
$this->assertEquals(200, $response->getStatusCode());
2323

24-
# TODO add further test coverage to suite your requirements
24+
# TODO add further test coverage to suite requirements
2525
}
2626
}

0 commit comments

Comments
 (0)