refactor
- move elixForms API logic to specific folder - move everything else under Api folder
This commit is contained in:
+6
-4
@@ -11,7 +11,7 @@ use Core\RateLimiter\FileRateLimiter;
|
|||||||
use Core\RateLimiter\InMemoryRateLimiter;
|
use Core\RateLimiter\InMemoryRateLimiter;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Core\LoggerFactory;
|
use Core\LoggerFactory;
|
||||||
use Services\ExternalApiService;
|
use ElixForms\ElixFormsClient;
|
||||||
|
|
||||||
$container = new Container();
|
$container = new Container();
|
||||||
|
|
||||||
@@ -51,9 +51,11 @@ $container->singleton(LoggerInterface::class, function($c) {
|
|||||||
return LoggerFactory::create($config->get('log_ident', 'api'));
|
return LoggerFactory::create($config->get('log_ident', 'api'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// External API service binding
|
// ElixForms client binding
|
||||||
$container->singleton(ExternalApiService::class, function($c) {
|
$container->singleton(ElixFormsClient::class, function($c) {
|
||||||
return new ExternalApiService();
|
$config = $c->make(Config::class);
|
||||||
|
$baseUrl = $config->get('elixforms_api_base_url');
|
||||||
|
return new ElixFormsClient($baseUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
// If you have other services, bind them here, for example:
|
// If you have other services, bind them here, for example:
|
||||||
|
|||||||
+6
-4
@@ -10,10 +10,12 @@
|
|||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Core\\": "src/Core/",
|
"Core\\": "src/Api/Core/",
|
||||||
"Controllers\\": "src/Controllers/",
|
"Controllers\\": "src/Api/Controllers/",
|
||||||
"Services\\": "src/Services/",
|
"Services\\": "src/Api/Services/",
|
||||||
"Helpers\\": "src/Helpers/"
|
"Helpers\\": "src/Api/Helpers/",
|
||||||
|
"Api\\": "src/Api/",
|
||||||
|
"ElixForms\\": "src/ElixForms/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'external_api_base_url' => 'https://api.example.com',
|
'elixforms_api_base_url' => 'https://api.example.com',
|
||||||
'rate_limiter_driver' => 'file', // o 'memory'
|
'rate_limiter_driver' => 'file', // o 'memory'
|
||||||
// configuration for file-based rate limiter
|
// configuration for file-based rate limiter
|
||||||
'rate_limit_storage_dir' => sys_get_temp_dir() . '/api_rate_limit',
|
'rate_limit_storage_dir' => sys_get_temp_dir() . '/api_rate_limit',
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'external_api_username' => 'myUser',
|
// elixforms API credentials
|
||||||
'external_api_password' => 'myPass',
|
'elixforms_api_username' => 'myUser',
|
||||||
'external_api_token' => 'mySecretToken',
|
'elixforms_api_password' => 'myPass',
|
||||||
|
'elixforms_api_token' => 'mySecretToken',
|
||||||
|
'api_access_token' => 'myApiAccessToken',
|
||||||
];
|
];
|
||||||
|
|||||||
+18
-2
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
$container = require __DIR__ . '/../bootstrap.php';
|
$container = require __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
|
use Api\Auth\ApiTokenAuthenticator;
|
||||||
use Core\Router;
|
use Core\Router;
|
||||||
use Core\Request;
|
use Core\Request;
|
||||||
use Core\Response;
|
use Core\Response;
|
||||||
@@ -28,13 +29,28 @@ $limiter = $container->make(RateLimiterInterface::class);
|
|||||||
|
|
||||||
$router = new Router($container); // vedi nota: router può ricevere container
|
$router = new Router($container); // vedi nota: router può ricevere container
|
||||||
|
|
||||||
|
$request = new Request();
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
// Autenticazione separata per le API esterne
|
||||||
|
if (strpos($request->path(), '/api/') === 0) {
|
||||||
|
try {
|
||||||
|
$authenticator = $container->make(ApiTokenAuthenticator::class);
|
||||||
|
$authenticator->authenticate($request);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$logger->warning('External API auth failed', ['path' => $request->path(), 'error' => $e->getMessage()]);
|
||||||
|
$response->json(['error' => 'Unauthorized'], 401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// register routes (path without version prefix)
|
// register routes (path without version prefix)
|
||||||
$router->get('/users', 'UsersController@index');
|
$router->get('/users', 'UsersController@index');
|
||||||
$router->post('/users', 'UsersController@create');
|
$router->post('/users', 'UsersController@create');
|
||||||
$router->get('/example', 'ExampleController@test');
|
$router->get('/example', 'ExampleController@test');
|
||||||
|
|
||||||
$request = new Request();
|
// external API routes
|
||||||
$response = new Response();
|
$router->get('/api/elixforms/status', 'Api\\Controllers\\ExternalElixFormsController@status');
|
||||||
|
$router->post('/api/elixforms/login', 'Api\\Controllers\\ExternalElixFormsController@login');
|
||||||
|
|
||||||
// Rate limiting by IP address
|
// Rate limiting by IP address
|
||||||
$key = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
$key = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
namespace Api\Auth;
|
||||||
|
|
||||||
|
use Core\Config;
|
||||||
|
use Core\Request;
|
||||||
|
|
||||||
|
class ApiTokenAuthenticator
|
||||||
|
{
|
||||||
|
private Config $config;
|
||||||
|
|
||||||
|
public function __construct(Config $config)
|
||||||
|
{
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authenticate(Request $request): void
|
||||||
|
{
|
||||||
|
$authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
|
||||||
|
if (!$authorization) {
|
||||||
|
throw new \Exception('Missing Authorization header');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^Bearer\s+(.*)$/i', trim($authorization), $matches)) {
|
||||||
|
throw new \Exception('Invalid Authorization header format');
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $matches[1];
|
||||||
|
$expected = $this->config->secret('api_access_token');
|
||||||
|
|
||||||
|
if (empty($expected) || !hash_equals((string) $expected, (string) $token)) {
|
||||||
|
throw new \Exception('Invalid API access token');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Controllers;
|
||||||
|
|
||||||
|
use Core\Request;
|
||||||
|
use Core\Response;
|
||||||
|
use ElixForms\ElixFormsClient;
|
||||||
|
|
||||||
|
class ExampleController
|
||||||
|
{
|
||||||
|
private ElixFormsClient $elixFormsClient;
|
||||||
|
|
||||||
|
public function __construct(ElixFormsClient $elixFormsClient)
|
||||||
|
{
|
||||||
|
$this->elixFormsClient = $elixFormsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test(Request $req, Response $res)
|
||||||
|
{
|
||||||
|
$response = $this->elixFormsClient->request('GET', '/status');
|
||||||
|
|
||||||
|
return $res->json([
|
||||||
|
'external_api_response' => $response['json'] ?? $response['body'],
|
||||||
|
'status' => $response['status']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
namespace Api\Controllers;
|
||||||
|
|
||||||
|
use Core\Request;
|
||||||
|
use Core\Response;
|
||||||
|
use ElixForms\ElixFormsClient;
|
||||||
|
use ElixForms\Exceptions\ElixFormsException;
|
||||||
|
|
||||||
|
class ExternalElixFormsController
|
||||||
|
{
|
||||||
|
private ElixFormsClient $elixFormsClient;
|
||||||
|
|
||||||
|
public function __construct(ElixFormsClient $elixFormsClient)
|
||||||
|
{
|
||||||
|
$this->elixFormsClient = $elixFormsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status(Request $req, Response $res)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = $this->elixFormsClient->request('GET', '/status');
|
||||||
|
return $res->json([
|
||||||
|
'status' => $response['status'],
|
||||||
|
'body' => $response['json'] ?? $response['body']
|
||||||
|
]);
|
||||||
|
} catch (ElixFormsException $e) {
|
||||||
|
return $res->json(['error' => $e->getMessage()], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(Request $req, Response $res)
|
||||||
|
{
|
||||||
|
$data = $req->body();
|
||||||
|
$username = $data['username'] ?? '';
|
||||||
|
$password = $data['password'] ?? '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$token = $this->elixFormsClient->auth()->login($username, $password);
|
||||||
|
return $res->json(['authToken' => $token]);
|
||||||
|
} catch (ElixFormsException $e) {
|
||||||
|
return $res->json(['error' => $e->getMessage()], 401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,4 +17,10 @@ class Request {
|
|||||||
public function query() {
|
public function query() {
|
||||||
return $_GET;
|
return $_GET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function header(string $name): ?string
|
||||||
|
{
|
||||||
|
$key = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
|
||||||
|
return $_SERVER[$key] ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@ class ExternalApiService
|
|||||||
|
|
||||||
public function getStatus()
|
public function getStatus()
|
||||||
{
|
{
|
||||||
$url = rtrim($this->config->get('external_api_base_url'), '/') . '/status';
|
$url = rtrim($this->config->get('elixforms_api_base_url'), '/') . '/status';
|
||||||
$token = $this->config->secret('external_api_token');
|
$token = $this->config->secret('elixforms_api_token');
|
||||||
$headers = ['Authorization' => 'Bearer ' . $token];
|
$headers = ['Authorization' => 'Bearer ' . $token];
|
||||||
return $this->httpClient->get($url, $headers);
|
return $this->httpClient->get($url, $headers);
|
||||||
}
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Controllers;
|
|
||||||
|
|
||||||
use Core\Request;
|
|
||||||
use Core\Response;
|
|
||||||
use Core\Config;
|
|
||||||
use Core\HttpClient;
|
|
||||||
|
|
||||||
class ExampleController
|
|
||||||
{
|
|
||||||
private $config;
|
|
||||||
private $httpClient;
|
|
||||||
|
|
||||||
public function __construct(Config $config, HttpClient $httpClient)
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
$this->httpClient = $httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test(Request $req, Response $res)
|
|
||||||
{
|
|
||||||
|
|
||||||
$url = $this->config->get('external_api_base_url') . '/status';
|
|
||||||
|
|
||||||
$response = $this->httpClient->get($url, [
|
|
||||||
'Authorization' => 'Bearer ' . $this->config->secret('external_api_token')
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $res->json([
|
|
||||||
'external_api_response' => $response
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
namespace ElixForms\Auth;
|
||||||
|
|
||||||
|
use ElixForms\Exceptions\ElixFormsException;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
|
||||||
|
class ElixFormsAuthenticationClient {
|
||||||
|
private $baseUrl;
|
||||||
|
private $httpClient;
|
||||||
|
|
||||||
|
public function __construct(string $baseUrl, Client $httpClient = null) {
|
||||||
|
$this->baseUrl = rtrim($baseUrl, '/');
|
||||||
|
$this->httpClient = $httpClient ?? new Client(['timeout' => 10.0, 'http_errors' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Esegue il login verso l'API di elixForms.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @return string Il token di autenticazione (authToken)
|
||||||
|
* @throws ElixFormsException Se le credenziali sono errate o c'è un errore server
|
||||||
|
*/
|
||||||
|
public function login(string $username, string $password): string {
|
||||||
|
$url = $this->baseUrl . '/eF/services/api/authentication/login/v1';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->post($url, [
|
||||||
|
'form_params' => [
|
||||||
|
'username' => $username,
|
||||||
|
'password' => $password
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$status = $response->getStatusCode();
|
||||||
|
if ($status !== 200) {
|
||||||
|
throw new ElixFormsException("Errore durante il login elixForms. HTTP Status: " . $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
$json = json_decode($body, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE || empty($json)) {
|
||||||
|
throw new ElixFormsException("Risposta non valida dal server elixForms: atteso JSON.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($json['value']['authToken'])) {
|
||||||
|
return $json['value']['authToken'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ElixFormsException("authToken non trovato nella risposta del login elixForms.");
|
||||||
|
} catch (GuzzleException $e) {
|
||||||
|
throw new ElixFormsException("Errore di connessione al server elixForms: " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effettua il logout invalidando il token sul server elixForms.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $token
|
||||||
|
* @return bool True se il logout ha successo
|
||||||
|
* @throws ElixFormsException Se c'è un errore durante il logout
|
||||||
|
*/
|
||||||
|
public function logout(string $username, string $token): bool {
|
||||||
|
$url = $this->baseUrl . '/eF/services/api/authentication/' . urlencode($username) . '/logout/v1';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->post($url, [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'Bearer ' . $token,
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$status = $response->getStatusCode();
|
||||||
|
if ($status !== 200) {
|
||||||
|
throw new ElixFormsException("Errore durante il logout elixForms. HTTP Status: " . $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (GuzzleException $e) {
|
||||||
|
throw new ElixFormsException("Errore di connessione al server elixForms: " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
namespace ElixForms;
|
||||||
|
|
||||||
|
use ElixForms<Auth\ElixFormsAuthenticationClient as AuthClient;
|
||||||
|
use ElixForms\Exceptions\ElixFormsException;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
|
||||||
|
class ElixFormsClient
|
||||||
|
{
|
||||||
|
private string $baseUrl;
|
||||||
|
private Client $httpClient;
|
||||||
|
|
||||||
|
public function __construct(string $baseUrl, Client $httpClient = null)
|
||||||
|
{
|
||||||
|
$this->baseUrl = rtrim($baseUrl, '/');
|
||||||
|
$this->httpClient = $httpClient ?? new Client(['timeout' => 10.0, 'http_errors' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function auth(): AuthClient
|
||||||
|
{
|
||||||
|
return new AuthClient($this->baseUrl, $this->httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request(string $method, string $path, ?array $body = null, array $headers = []): array
|
||||||
|
{
|
||||||
|
$url = $this->baseUrl . '/' . ltrim($path, '/');
|
||||||
|
$options = ['headers' => $headers];
|
||||||
|
|
||||||
|
if ($body !== null) {
|
||||||
|
$options['json'] = $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->request($method, $url, $options);
|
||||||
|
$status = $response->getStatusCode();
|
||||||
|
$bodyRaw = $response->getBody()->getContents();
|
||||||
|
$contentType = $response->getHeaderLine('Content-Type');
|
||||||
|
$isJson = stripos($contentType, 'application/json') !== false;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => $status,
|
||||||
|
'headers' => $response->getHeaders(),
|
||||||
|
'body' => $bodyRaw,
|
||||||
|
'is_json' => $isJson,
|
||||||
|
'json' => $isJson ? json_decode($bodyRaw, true) : null,
|
||||||
|
];
|
||||||
|
} catch (GuzzleException $e) {
|
||||||
|
throw new ElixFormsException('Errore di connessione al server elixForms: ' . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace ElixForms\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ElixFormsException extends Exception {
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Services;
|
|
||||||
|
|
||||||
use Core\Config;
|
|
||||||
use Core\HttpClient;
|
|
||||||
use Core\ElixFormsException;
|
|
||||||
|
|
||||||
class ElixFormsAuthenticationClient {
|
|
||||||
private $config;
|
|
||||||
private $httpClient;
|
|
||||||
|
|
||||||
public function __construct(Config $config, HttpClient $httpClient) {
|
|
||||||
$this->config = $config;
|
|
||||||
$this->httpClient = $httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Esegue il login verso l'API di elixForms.
|
|
||||||
*
|
|
||||||
* @param string $username
|
|
||||||
* @param string $password
|
|
||||||
* @return string Il token di autenticazione (authToken)
|
|
||||||
* @throws ElixFormsException Se le credenziali sono errate o c'è un errore server
|
|
||||||
*/
|
|
||||||
public function login(string $username, string $password): string {
|
|
||||||
$baseUrl = rtrim($this->config->get('elixforms_api_base_url'), '/');
|
|
||||||
$url = $baseUrl . '/eF/services/api/authentication/login/v1';
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'username' => $username,
|
|
||||||
'password' => $password
|
|
||||||
];
|
|
||||||
|
|
||||||
// L'API supporta application/json
|
|
||||||
$response = $this->httpClient->post($url, $data);
|
|
||||||
|
|
||||||
if ($response['status'] !== 200) {
|
|
||||||
throw new ElixFormsException("Errore durante il login elixForms. HTTP Status: " . $response['status']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$response['is_json'] || empty($response['json'])) {
|
|
||||||
throw new ElixFormsException("Risposta non valida dal server elixForms: atteso JSON.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$json = $response['json'];
|
|
||||||
|
|
||||||
// Estraiamo il token dalla struttura complessa
|
|
||||||
if (isset($json['value']['authToken'])) {
|
|
||||||
return $json['value']['authToken'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ElixFormsException("authToken non trovato nella risposta del login elixForms.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effettua il logout invalidando il token sul server elixForms.
|
|
||||||
*
|
|
||||||
* @param string $username
|
|
||||||
* @param string $token
|
|
||||||
* @return bool True se il logout ha successo
|
|
||||||
* @throws ElixFormsException Se c'è un errore durante il logout
|
|
||||||
*/
|
|
||||||
public function logout(string $username, string $token): bool {
|
|
||||||
$baseUrl = rtrim($this->config->get('elixforms_api_base_url'), '/');
|
|
||||||
$url = $baseUrl . '/eF/services/api/authentication/' . urlencode($username) . '/logout/v1';
|
|
||||||
|
|
||||||
$headers = [
|
|
||||||
'Authorization' => 'Bearer ' . $token,
|
|
||||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
|
||||||
];
|
|
||||||
|
|
||||||
$response = $this->httpClient->post($url, null, $headers);
|
|
||||||
|
|
||||||
if ($response['status'] !== 200) {
|
|
||||||
throw new ElixFormsException("Errore durante il logout elixForms. HTTP Status: " . $response['status']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user