Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .docker/php-fpm/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ RUN { \
COPY --from=builder --chown=www-data:www-data /var/www/html ./

ARG SENTRY_RELEASE=""
ENV APP_ENV=prod APP_DEBUG=0 SENTRY_RELEASE=${SENTRY_RELEASE}
ARG APP_VERSION=""
ENV APP_ENV=prod APP_DEBUG=0 SENTRY_RELEASE=${SENTRY_RELEASE} APP_VERSION=${APP_VERSION}

# Create minimal .env for Symfony bootstrap, placeholder for public key, remove dev files
RUN printf "APP_ENV=prod\nSENTRY_DSN=\nJWKS_URI=https://auth.izeebot.top/.well-known/jwks.json\n" > .env \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ jobs:
labels: ${{ steps.meta-php.outputs.labels }}
build-args: |
SENTRY_RELEASE=${{ vars.SENTRY_PROJECT }}@${{ needs.version.outputs.new_version }}
APP_VERSION=v${{ needs.version.outputs.new_version }}
no-cache: true

- name: Build and push Caddy
Expand Down
5 changes: 4 additions & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ composer: ## Install PHP dependencies
cache: ## Clear Symfony cache
@docker compose exec php bin/console c:c

test-build:
test-build: ## Build test images
@$(TEST_COMPOSE) build
test: ## Run PHPUnit tests (isolated test containers)
@echo "Starting test containers..."
Expand Down Expand Up @@ -167,5 +167,8 @@ network: ## Create Docker network
t: ## Quick test filter (uses test containers): make t -- CorrectFlagEndpointTest
@$(TEST_COMPOSE) exec php vendor/bin/phpunit --filter "$(filter-out $@,$(MAKECMDGOALS) $(MAKEFLAGS))"

cc: ## Delete all cache folders
rm -rf var/cache

%:
@
1 change: 1 addition & 0 deletions app/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
jwks_uri: '%env(JWKS_URI)%'
app.version: '%env(default::APP_VERSION)%'

services:
# default configuration for services in *this* file
Expand Down
3 changes: 0 additions & 3 deletions app/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,6 @@
<UnusedClass>
<code><![CDATA[HqAuthAuthenticator]]></code>
</UnusedClass>
<UnusedVariable>
<code><![CDATA[$expiresIn]]></code>
</UnusedVariable>
</file>
<file src="src/Flags/Security/JwksJwtEncoder.php">
<MissingOverrideAttribute>
Expand Down
14 changes: 13 additions & 1 deletion app/src/Flags/Controller/HealthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ final class HealthController extends AbstractController
#[Route('/health', name: 'health_check', methods: ['GET'])]
public function health(): JsonResponse
{
return new JsonResponse(['status' => 'ok'], Response::HTTP_OK);
return new JsonResponse([
'status' => 'ok',
'version' => $this->getVersion(),
], Response::HTTP_OK);
}

#[Route('/health/ready', name: 'health_ready', methods: ['GET'])]
Expand All @@ -32,12 +35,21 @@ public function ready(Connection $connection): JsonResponse

return new JsonResponse([
'status' => 'ok' === $dbStatus ? 'ok' : 'degraded',
'version' => $this->getVersion(),
'checks' => [
'database' => $dbStatus,
],
], $status);
}

private function getVersion(): array
{
return [
'version' => $this->getParameter('app.version'),
'environment' => $this->getParameter('kernel.environment'),
];
}

#[Route('/robots.txt', name: 'robots_txt', methods: ['GET'])]
public function robots(): Response
{
Expand Down
17 changes: 2 additions & 15 deletions app/src/Flags/Security/HqAuthAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public function authenticate(Request $request): Passport
#[\Override]
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// dd($request->toArray());
// Get the JWT access token
$accessToken = $request->attributes->get('oauth_access_token');
$refreshToken = $request->attributes->get('oauth_refresh_token');
Expand All @@ -81,23 +80,11 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token,
window.opener.postMessage({
type: 'oauth_success',
access_token: '$accessToken',
refresh_token: '$refreshToken'
refresh_token: '$refreshToken',
expires_in: '$expiresIn',
}, '*');
window.close();
</script>");

// Return JSON with the tokens for the frontend
// return new JsonResponse([
// 'success' => true,
// 'access_token' => $accessToken,
// 'refresh_token' => $refreshToken,
// 'expires_in' => $expiresIn,
// 'token_type' => 'Bearer',
// 'user' => [
// // 'email' => $token->getUser()->getEmail(),
// 'roles' => $token->getUser()->getRoles(),
// ]
// ]);
}

#[\Override]
Expand Down
34 changes: 28 additions & 6 deletions app/tests/Unit/Controller/HealthControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,53 @@

use App\Flags\Controller\HealthController;
use Doctrine\DBAL\Connection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Response;

class HealthControllerTest extends TestCase
class HealthControllerTest extends KernelTestCase
{
private Container $container;

protected function setUp(): void
{
self::bootKernel();
$this->container = static::getContainer();
}

public function testHealthReturnsOk(): void
{
$controller = new HealthController();
/** @var HealthController $controller */
$controller = $this->container->get(HealthController::class);
$controller->setContainer($this->container);

$response = $controller->health();

$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$this->assertEquals('{"status":"ok"}', $response->getContent());

$content = json_decode($response->getContent(), true);
$this->assertEquals('ok', $content['status']);
$this->assertArrayHasKey('version', $content);
$this->assertIsArray($content['version']);
$this->assertArrayHasKey('environment', $content['version']);
}

public function testReadyReturnsOkWhenDatabaseIsConnected(): void
{
$connection = $this->createMock(Connection::class);
$connection->method('executeQuery')->willReturn($this->createMock(\Doctrine\DBAL\Result::class));

$controller = new HealthController();
/** @var HealthController $controller */
$controller = $this->container->get(HealthController::class);
$controller->setContainer($this->container);

$response = $controller->ready($connection);

$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());

$content = json_decode($response->getContent(), true);
$this->assertEquals('ok', $content['status']);
$this->assertArrayHasKey('version', $content);
$this->assertEquals('ok', $content['checks']['database']);
}

Expand All @@ -42,14 +61,17 @@ public function testReadyReturnsServiceUnavailableWhenDatabaseFails(): void
$connection = $this->createMock(Connection::class);
$connection->method('executeQuery')->willThrowException(new \Exception('Connection failed'));

$controller = new HealthController();
/** @var HealthController $controller */
$controller = $this->container->get(HealthController::class);
$controller->setContainer($this->container);

$response = $controller->ready($connection);

$this->assertEquals(Response::HTTP_SERVICE_UNAVAILABLE, $response->getStatusCode());

$content = json_decode($response->getContent(), true);
$this->assertEquals('degraded', $content['status']);
$this->assertArrayHasKey('version', $content);
$this->assertEquals('error', $content['checks']['database']);
}
}