|
@@ -0,0 +1,235 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+declare(strict_types=1);
|
|
|
|
|
+
|
|
|
|
|
+namespace App\App;
|
|
|
|
|
+
|
|
|
|
|
+use App\ApiClient\AdminClient;
|
|
|
|
|
+use App\ApiClient\ApiClient;
|
|
|
|
|
+use App\ApiClient\ApiHealth;
|
|
|
|
|
+use App\ApiClient\AuthClient;
|
|
|
|
|
+use App\Auth\JumbojettOidcAuthenticator;
|
|
|
|
|
+use App\Auth\LocalLoginController;
|
|
|
|
|
+use App\Auth\LogoutController;
|
|
|
|
|
+use App\Auth\OidcAuthenticator;
|
|
|
|
|
+use App\Auth\OidcController;
|
|
|
|
|
+use App\Auth\SessionManager;
|
|
|
|
|
+use App\Controllers\HealthzController;
|
|
|
|
|
+use App\Controllers\HomeController;
|
|
|
|
|
+use App\Controllers\MeController;
|
|
|
|
|
+use App\Controllers\NoAccessController;
|
|
|
|
|
+use App\Http\AuthRequiredMiddleware;
|
|
|
|
|
+use App\Http\CsrfMiddleware;
|
|
|
|
|
+use App\Http\JsonExceptionHandler;
|
|
|
|
|
+use App\Http\SessionMiddleware;
|
|
|
|
|
+use App\Http\TwigGlobalsMiddleware;
|
|
|
|
|
+
|
|
|
|
|
+use function DI\autowire;
|
|
|
|
|
+
|
|
|
|
|
+use DI\ContainerBuilder;
|
|
|
|
|
+
|
|
|
|
|
+use function DI\factory;
|
|
|
|
|
+
|
|
|
|
|
+use GuzzleHttp\Client as GuzzleClient;
|
|
|
|
|
+use GuzzleHttp\ClientInterface as GuzzleClientInterface;
|
|
|
|
|
+use Monolog\Formatter\JsonFormatter;
|
|
|
|
|
+use Monolog\Handler\StreamHandler;
|
|
|
|
|
+use Monolog\Logger;
|
|
|
|
|
+use Psr\Container\ContainerInterface;
|
|
|
|
|
+use Psr\Http\Message\ResponseFactoryInterface;
|
|
|
|
|
+use Psr\Log\LoggerInterface;
|
|
|
|
|
+use Slim\Psr7\Factory\ResponseFactory;
|
|
|
|
|
+use Slim\Views\Twig;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Builds the UI's DI container. UI-side; no DB. Everything that talks
|
|
|
|
|
+ * to the api goes through `ApiClient` / its subclients.
|
|
|
|
|
+ */
|
|
|
|
|
+final class Container
|
|
|
|
|
+{
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @param array<string, mixed> $settings
|
|
|
|
|
+ */
|
|
|
|
|
+ public static function build(array $settings): ContainerInterface
|
|
|
|
|
+ {
|
|
|
|
|
+ $builder = new ContainerBuilder();
|
|
|
|
|
+ $builder->useAutowiring(true);
|
|
|
|
|
+ $builder->addDefinitions([
|
|
|
|
|
+ 'settings' => $settings,
|
|
|
|
|
+ 'settings.app_env' => $settings['app_env'] ?? 'production',
|
|
|
|
|
+ 'settings.is_dev' => ($settings['app_env'] ?? 'production') === 'development',
|
|
|
|
|
+ 'settings.log_level' => $settings['log_level'] ?? \Monolog\Level::Info,
|
|
|
|
|
+ 'settings.api_base_url' => (string) ($settings['api_base_url'] ?? ''),
|
|
|
|
|
+ 'settings.api_timeout' => (float) ($settings['api_timeout_seconds'] ?? 5.0),
|
|
|
|
|
+ 'settings.ui_service_token' => (string) ($settings['ui_service_token'] ?? ''),
|
|
|
|
|
+ 'settings.oidc_enabled' => (bool) ($settings['oidc_enabled'] ?? false),
|
|
|
|
|
+ 'settings.oidc_issuer' => (string) ($settings['oidc_issuer'] ?? ''),
|
|
|
|
|
+ 'settings.oidc_client_id' => (string) ($settings['oidc_client_id'] ?? ''),
|
|
|
|
|
+ 'settings.oidc_client_secret' => (string) ($settings['oidc_client_secret'] ?? ''),
|
|
|
|
|
+ 'settings.oidc_redirect_uri' => (string) ($settings['oidc_redirect_uri'] ?? ''),
|
|
|
|
|
+ 'settings.local_admin_enabled' => (bool) ($settings['local_admin_enabled'] ?? false),
|
|
|
|
|
+ 'settings.local_admin_username' => (string) ($settings['local_admin_username'] ?? ''),
|
|
|
|
|
+ 'settings.local_admin_password_hash' => (string) ($settings['local_admin_password_hash'] ?? ''),
|
|
|
|
|
+ 'settings.session_idle' => (int) ($settings['session_idle_seconds'] ?? 28800),
|
|
|
|
|
+ 'settings.session_absolute' => (int) ($settings['session_absolute_seconds'] ?? 86400),
|
|
|
|
|
+
|
|
|
|
|
+ LoggerInterface::class => factory(static function (ContainerInterface $c): LoggerInterface {
|
|
|
|
|
+ $logger = new Logger('ui');
|
|
|
|
|
+ /** @var \Monolog\Level $level */
|
|
|
|
|
+ $level = $c->get('settings.log_level');
|
|
|
|
|
+ $handler = new StreamHandler('php://stdout', $level);
|
|
|
|
|
+ $handler->setFormatter(new JsonFormatter());
|
|
|
|
|
+ $logger->pushHandler($handler);
|
|
|
|
|
+
|
|
|
|
|
+ return $logger;
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ ResponseFactoryInterface::class => autowire(ResponseFactory::class),
|
|
|
|
|
+
|
|
|
|
|
+ Twig::class => factory(static function (ContainerInterface $c): Twig {
|
|
|
|
|
+ /** @var bool $isDev */
|
|
|
|
|
+ $isDev = $c->get('settings.is_dev');
|
|
|
|
|
+
|
|
|
|
|
+ return Twig::create(__DIR__ . '/../../resources/views', [
|
|
|
|
|
+ 'cache' => false,
|
|
|
|
|
+ 'auto_reload' => $isDev,
|
|
|
|
|
+ 'strict_variables' => false,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ SessionManager::class => factory(static function (ContainerInterface $c): SessionManager {
|
|
|
|
|
+ /** @var bool $isDev */
|
|
|
|
|
+ $isDev = $c->get('settings.is_dev');
|
|
|
|
|
+ /** @var int $idle */
|
|
|
|
|
+ $idle = $c->get('settings.session_idle');
|
|
|
|
|
+ /** @var int $absolute */
|
|
|
|
|
+ $absolute = $c->get('settings.session_absolute');
|
|
|
|
|
+
|
|
|
|
|
+ return new SessionManager(
|
|
|
|
|
+ secureCookie: !$isDev,
|
|
|
|
|
+ idleSeconds: $idle,
|
|
|
|
|
+ absoluteSeconds: $absolute,
|
|
|
|
|
+ );
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ ApiHealth::class => factory(static fn (): ApiHealth => new ApiHealth()),
|
|
|
|
|
+
|
|
|
|
|
+ GuzzleClientInterface::class => factory(static function (ContainerInterface $c): GuzzleClientInterface {
|
|
|
|
|
+ /** @var string $base */
|
|
|
|
|
+ $base = $c->get('settings.api_base_url');
|
|
|
|
|
+ /** @var float $timeout */
|
|
|
|
|
+ $timeout = $c->get('settings.api_timeout');
|
|
|
|
|
+
|
|
|
|
|
+ return new GuzzleClient([
|
|
|
|
|
+ 'base_uri' => $base,
|
|
|
|
|
+ 'timeout' => $timeout,
|
|
|
|
|
+ 'connect_timeout' => $timeout,
|
|
|
|
|
+ 'http_errors' => false,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ ApiClient::class => factory(static function (ContainerInterface $c): ApiClient {
|
|
|
|
|
+ /** @var GuzzleClientInterface $http */
|
|
|
|
|
+ $http = $c->get(GuzzleClientInterface::class);
|
|
|
|
|
+ /** @var string $token */
|
|
|
|
|
+ $token = $c->get('settings.ui_service_token');
|
|
|
|
|
+ /** @var ApiHealth $health */
|
|
|
|
|
+ $health = $c->get(ApiHealth::class);
|
|
|
|
|
+ /** @var LoggerInterface $logger */
|
|
|
|
|
+ $logger = $c->get(LoggerInterface::class);
|
|
|
|
|
+
|
|
|
|
|
+ return new ApiClient($http, $token, $health, $logger);
|
|
|
|
|
+ }),
|
|
|
|
|
+ AuthClient::class => autowire(),
|
|
|
|
|
+ AdminClient::class => autowire(),
|
|
|
|
|
+
|
|
|
|
|
+ OidcAuthenticator::class => factory(static function (ContainerInterface $c): OidcAuthenticator {
|
|
|
|
|
+ /** @var string $issuer */
|
|
|
|
|
+ $issuer = $c->get('settings.oidc_issuer');
|
|
|
|
|
+ /** @var string $clientId */
|
|
|
|
|
+ $clientId = $c->get('settings.oidc_client_id');
|
|
|
|
|
+ /** @var string $clientSecret */
|
|
|
|
|
+ $clientSecret = $c->get('settings.oidc_client_secret');
|
|
|
|
|
+ /** @var string $redirectUri */
|
|
|
|
|
+ $redirectUri = $c->get('settings.oidc_redirect_uri');
|
|
|
|
|
+
|
|
|
|
|
+ return new JumbojettOidcAuthenticator($issuer, $clientId, $clientSecret, $redirectUri);
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ // Middlewares — autowire works directly.
|
|
|
|
|
+ SessionMiddleware::class => autowire(),
|
|
|
|
|
+ CsrfMiddleware::class => autowire(),
|
|
|
|
|
+ AuthRequiredMiddleware::class => autowire(),
|
|
|
|
|
+ TwigGlobalsMiddleware::class => factory(static function (ContainerInterface $c): TwigGlobalsMiddleware {
|
|
|
|
|
+ /** @var Twig $twig */
|
|
|
|
|
+ $twig = $c->get(Twig::class);
|
|
|
|
|
+ /** @var SessionManager $sessions */
|
|
|
|
|
+ $sessions = $c->get(SessionManager::class);
|
|
|
|
|
+
|
|
|
|
|
+ return new TwigGlobalsMiddleware($twig, $sessions, [
|
|
|
|
|
+ 'oidc_enabled' => (bool) $c->get('settings.oidc_enabled'),
|
|
|
|
|
+ 'local_admin_enabled' => (bool) $c->get('settings.local_admin_enabled'),
|
|
|
|
|
+ 'app_env' => (string) $c->get('settings.app_env'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ JsonExceptionHandler::class => factory(static function (ContainerInterface $c): JsonExceptionHandler {
|
|
|
|
|
+ /** @var Twig $twig */
|
|
|
|
|
+ $twig = $c->get(Twig::class);
|
|
|
|
|
+ /** @var ResponseFactoryInterface $rf */
|
|
|
|
|
+ $rf = $c->get(ResponseFactoryInterface::class);
|
|
|
|
|
+ /** @var LoggerInterface $logger */
|
|
|
|
|
+ $logger = $c->get(LoggerInterface::class);
|
|
|
|
|
+
|
|
|
|
|
+ return new JsonExceptionHandler($twig, $rf, $logger, (bool) $c->get('settings.is_dev'));
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ // Controllers
|
|
|
|
|
+ HomeController::class => autowire(),
|
|
|
|
|
+ HealthzController::class => autowire(),
|
|
|
|
|
+ MeController::class => autowire(),
|
|
|
|
|
+ NoAccessController::class => autowire(),
|
|
|
|
|
+ LogoutController::class => autowire(),
|
|
|
|
|
+
|
|
|
|
|
+ LocalLoginController::class => factory(static function (ContainerInterface $c): LocalLoginController {
|
|
|
|
|
+ /** @var Twig $twig */
|
|
|
|
|
+ $twig = $c->get(Twig::class);
|
|
|
|
|
+ /** @var SessionManager $sessions */
|
|
|
|
|
+ $sessions = $c->get(SessionManager::class);
|
|
|
|
|
+ /** @var AuthClient $auth */
|
|
|
|
|
+ $auth = $c->get(AuthClient::class);
|
|
|
|
|
+ /** @var ResponseFactoryInterface $rf */
|
|
|
|
|
+ $rf = $c->get(ResponseFactoryInterface::class);
|
|
|
|
|
+ /** @var LoggerInterface $logger */
|
|
|
|
|
+ $logger = $c->get(LoggerInterface::class);
|
|
|
|
|
+
|
|
|
|
|
+ return new LocalLoginController(
|
|
|
|
|
+ $twig,
|
|
|
|
|
+ $sessions,
|
|
|
|
|
+ $auth,
|
|
|
|
|
+ $rf,
|
|
|
|
|
+ $logger,
|
|
|
|
|
+ (bool) $c->get('settings.local_admin_enabled'),
|
|
|
|
|
+ (bool) $c->get('settings.oidc_enabled'),
|
|
|
|
|
+ (string) $c->get('settings.local_admin_username'),
|
|
|
|
|
+ (string) $c->get('settings.local_admin_password_hash'),
|
|
|
|
|
+ );
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ OidcController::class => factory(static function (ContainerInterface $c): OidcController {
|
|
|
|
|
+ /** @var OidcAuthenticator $authenticator */
|
|
|
|
|
+ $authenticator = $c->get(OidcAuthenticator::class);
|
|
|
|
|
+ /** @var AuthClient $auth */
|
|
|
|
|
+ $auth = $c->get(AuthClient::class);
|
|
|
|
|
+ /** @var SessionManager $sessions */
|
|
|
|
|
+ $sessions = $c->get(SessionManager::class);
|
|
|
|
|
+ /** @var LoggerInterface $logger */
|
|
|
|
|
+ $logger = $c->get(LoggerInterface::class);
|
|
|
|
|
+
|
|
|
|
|
+ return new OidcController($authenticator, $auth, $sessions, $logger, (bool) $c->get('settings.oidc_enabled'));
|
|
|
|
|
+ }),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return $builder->build();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|