Hi All
I hope someone can help, I am struggling to add some middleware (I think)
I have created the below api via the docs and various tutorials that talks to a mySQL & SQL server database.
Problems: I am currently repeating a lot of code and wondering if anyone knows how I can prevent it.
Problem 1 (Auth): See CompanyController.php & Validator.php
For every function in a controller I am writing the same auth logic and hope someone could help prevent that.
I have seen and attempted to add middleware to a group but cannot get anything to work.
I would like to have on a group or single route an authorization check: See Validator.php below.
Problem 2 (Preflight): See Routes.php & PreFlightController.php
For every route I am having to to add an options route so it works through a browser, Is there a way of adding this PreFlightController to all routes in the $app?
I hope I have made sense, any help would be very much appreciated.
(I have added my questions in the code comments)
Sorry its a lot.
Thanks
**MAIN API CODE BELOW ** (Questions prefixed with //???..)
bootstrap.php
<?php
date_default_timezone_set('Europe/London');
use DI\Container;
use DI\Bridge\Slim\Bridge as SlimAppFactory;
require_once __DIR__ .'/../vendor/autoload.php';
require_once __DIR__ .'/../vendor/paragonie/sodium_compat/autoload.php';
$container = new Container();
$settings = require_once __DIR__.'/settings.php';
$settings($container);
$app = SlimAppFactory::create($container);
$app->setBasePath('/api');
$middleware = require_once __DIR__ . '/middleware.php';
$middleware($app);
$routes = require_once __DIR__ .'/routes.php';
$routes($app);
$app->run();
settings.php
<?php
use Psr\Container\ContainerInterface;
return function (ContainerInterface $container)
{
$container->set('settings',function()
{
$db = require __DIR__ . '/database.php';
return [
"db"=>$db
];
});
};
middleware.php
<?php
use Slim\App;
use App\Response\CustomResponse;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteContext;
return function (App $app) {
$app->getContainer()->get('settings');
$app->addBodyParsingMiddleware();
// This middleware will append the response header Access-Control-Allow-Methods with all allowed methods
$app->add(function (Request $request, RequestHandlerInterface $handler): Response {
$routeContext = RouteContext::fromRequest($request);
$routingResults = $routeContext->getRoutingResults();
$methods = $routingResults->getAllowedMethods();
$requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');
$response = $handler->handle($request);
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
$response = $response->withHeader('Access-Control-Allow-Methods', implode(',', $methods));
$response = $response->withHeader('Access-Control-Allow-Headers', $requestHeaders);
return $response;
});
$app->addRoutingMiddleware();
// Define Custom Error Handler
$customErrorHandler = function (
Request $request,
Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
// ?LoggerInterface $logger = null
) use ($app) {
// $logger->error($exception->getMessage());
$payload = ['error' => $exception->getMessage()];
$response = $app->getResponseFactory()->createResponse();
$customResponse = new CustomResponse();
return $customResponse->is400Response($response, $payload);
};
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);
};
database.php
<?php
//MY SQL
$database_config_mysql = [
'driver'=>'mysql',
'host'=>'localhost',
'database'=>'mysql_dbname',
'username'=>'username',
'password'=>'password',
'charset'=>'utf8',
'collation'=>'utf8_unicode_ci',
'prefix'=>''
];
//SQL SERVER
$database_config_sqlsrv = [
'driver' => 'sqlsrv',
'host' => 'sqlServerHost',
'database' => 'sqlsrvl_dbname',
'username' => 'username',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
];
$capsule = new Illuminate\Database\Capsule\Manager;
$capsule->addConnection($database_config_mysql);
//???How do I get access to this connection from one of my controllers???
$capsule->addConnection($database_config_sqlsrv , "SQLSRV");
$capsule->setAsGlobal();
$capsule->bootEloquent();
return $capsule;
routes.php
<?php
use Slim\App;
use Slim\Exception\HttpNotFoundException;
use App\Controllers\PreFlightController;
use App\Controllers\SQLSRV\CompanyController;
return function (App $app) {
//???How do I have all of these routes go through my validation check before accessing the route.
$app->group("/company", function ($app) {
$app->get('/company', [CompanyController::class, 'getAllRows']);
$app->get('/compay/{id}', [CompanyController::class, 'getSingleCompany']);
$app->get('/company/search/{id}', [CompanyController::class, 'getAllRows']);
//???Can I prevent writing an option route for each above route.
$app->options('/company', [PreFlightController::class, 'preflight']);
$app->options('/compay/{id}'', [PreFlightController::class, 'preflight']);
$app->options('/company/search/{id}'', [PreFlightController::class, 'preflight']);
});
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($request, $response) {
throw new HttpNotFoundException($request);
});
};
CompanyController.php (sqlsrv)
<?php
namespace App\Controllers\SQLSRV;
use App\Response\CustomResponse;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Models\SQLSRV\Company;
use App\Validation\Validator;
class CompanyController
{
protected $company;
protected $customResponse;
protected $validator;
public function __construct()
{
$this->company= new Company();
$this->customResponse = new CustomResponse();
$this->validator = new Validator();
}
public function getCompanies(Request $request, Response $response)
{
//???Can I prevent writing this validation logic in every function, Would like to add it to all routes in a group.
$user= $this->validator->validateFirebaseJWT($request);
if ($user== null) {
return $this->customResponse->is401Response($response, "Unauthorised Request");
}
$users = $this->user->get();
return $this->customResponse->is200Response($response, $users);
}
//???How can I run a stored procedure on a SQL server?
public function runStoredProcedure(Request $request, Response $response)
{
//???I need a connection to the SQLSRV database, something like below...
$result = $db->select($db->raw("Stored Procedure Code'")
return $this->customResponse->is200Response($response, $result );
}
}
Company.php (sqlsrv)
<?php
namespace App\Models\Eclipse;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
protected $table = 'SQLSRV.Commercial.Company';
protected $fillable = [
"CompanyID",
"CompanyName",
];
}
validator.php
<?php
namespace App\Controllers\Base;
use Firebase\JWT\JWT;
use Psr\Http\Message\RequestInterface as Request;
use App\Models\User;
use App\Models\GooglePublicKey;
//Gets the GooglePublicKey stored in the database
//Firebase\JWT\JWT Decodes the JWT
//Use Decoded data to check user is in the data base
//If user then authorize
//NOTE:this has some functions removed for space
class FirebaseAuth
{
protected $user;
protected $googlePublicKey;
public function __construct()
{
$this->user = new User();
$this->googlePublicKey = new GooglePublicKey();
}
public function validateFirebaseJWT(Request $request)
{
//Get JWT from auth header
$jwt = $this->getJWT($request);
if (!$jwt) {
return null;
}
//Check if use is found in database
$userData = $this->getUserData($jwt);
if (!$userData) {
return null;
} else {
return $userData;
}
}
}
PreFlightController.php
<?php
namespace App\Controllers;
use Psr\Http\Message\ResponseInterface as Response;
class PreFlightController
{
public function preflight(Response $response)
{
return $response;
}
}
CustomResponse.php
<?php
namespace App\Response;
class CustomResponse
{
public function is200Response($response, $responseMessage)
{
$responseMessage = json_encode(["success" => true, "response" => $responseMessage, "status" => 200]);
$response->getBody()->write($responseMessage);
return $response->withHeader("Content-Type", "application/json")
->withStatus(200);
}
public function is400Response($response, $responseMessage)
{
$responseMessage = json_encode(["success" => false, "response" => $responseMessage, "status" => 400]);
$response->getBody()->write($responseMessage);
return $response->withHeader("Content-Type", "application/json")
->withStatus(400);
}
public function is401Response($response, $responseMessage)
{
$responseMessage = json_encode(["success" => false, "response" => $responseMessage, "status" => 401]);
$response->getBody()->write($responseMessage);
return $response->withHeader("Content-Type", "application/json")
->withStatus(401);
}
}