Middleware to the Rescue: A PHPMiNDS Story

Middleware to the Rescue: A PHPMiNDS Story

Introducing the PHPMinds User Group and how we used middleware for functionality ranging from basic authentication whitelisting to making CLI requests using actions.

8234e593787784e12761320a7dca363a?s=128

Antonis Pavlakis

October 01, 2016
Tweet

Transcript

  1. 2.

    A PHP UG in Nottingham Meeting every 2nd Thursday Founded

    by Adoni Pavlakis (@pavlakis) and Shaun Hare (@sdh100Shaun) In December 2015 Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  2. 3.

    The brief Use new technologies Event integration with meetup.com and

    joind.in Website Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  3. 4.

    Built using Centos 7 / Nginx / MariaDB / LetsEncrypt

    Automated Deployment PHP 7 Slim 3 framework Based on Rob Allen’s (@akrabat) Slim 3 Skeleton Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  4. 5.

    What is middleware? “A pipeline of small bits of code

    that operate on the response object or the request object.” Rob Allen Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  5. 7.

    Use case #1 Create an admin area for authenticated users

    (to manage events) Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  6. 8.

    Use case #1 - Steps * List links which require

    authentication (white list) * Redirect non-authenticated users to home page * Allow authenticated users to access the page Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  7. 9.

    ->add(function($request, $response, callable $next){
 $whiteList = [
 '/admin',
 '/create-event',
 '/create-speaker',


    '/event-details'
 ];
 
 if (!isset($_SESSION['auth'])) {
 if (in_array($request->getUri()->getPath(), $whiteList)) {
 return $response->withStatus(302)->withHeader('Location', '/');
 }
 }
 
 return $next($request, $response);
 }); Some basic logic to add to the route (or group of routes) Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  8. 10.

    class AuthCheck
 {
 private $isAuth = false;
 private $authRoutes =

    [];
 
 public function __construct(array $session, $authKey = 'auth', array $authRoutes = [])
 {
 if (array_key_exists($authKey, $session)) {
 $this->isAuth = true;
 }
 
 $this->authRoutes = $authRoutes;
 }
 
 
 public function __invoke(Request $request, Response $response, callable $next)
 {
 if (!$this->isAuth) {
 if (in_array($request->getUri()->getPath(), $this->authRoutes)) {
 return $response->withStatus(302)->withHeader('Location', '/');
 }
 }
 
 return $next($request, $response);
 }
 } Move it into its own class
  9. 13.

    Use case #2 - Steps * Create a joind.in event

    * Wait for the event to be approved * Create a joind.in talk Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  10. 14.

    Implementation * Create a cron * Make a call through

    the CLI * Use same entry point (?) Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  11. 15.

    Creating a Request object from parameters passed on the CLI

    Based on Bobby DeVeaux's (@bobbyjason) Gulp Skeleton Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  12. 16.

    Main objective Call the CLI script using: route / action

    / query string e.g. php public/index.php /status GET event=true Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  13. 17.

    global $argv;
 
 
 if (isset($argv)) {
 
 $path =

    $this->get($argv, 1);
 $method = $this->get($argv, 2);
 $params = $this->get($argv, 3);
 
 if (strtoupper($method) === 'GET') {
 $this->request = \Slim\Http\Request::createFromEnvironment(\Slim\Http\Environment::mock([
 'REQUEST_METHOD' => 'GET',
 'REQUEST_URI' => $this->getUri($path, $params),
 'QUERY_STRING' => $params
 ]));
 }
 
 unset($argv);
 }
 
 return $next($this->request, $response); A bit of logic… Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  14. 18.

    // ONLY WHEN CALLED THROUGH CLI
 if (PHP_SAPI !== 'cli')

    {
 return $response->withStatus(404)->withHeader('Location', '/404');
 }
 
 if (!$request->getParam('event')) {
 return $response->withStatus(404)->withHeader('Location', '/404');
 }
 
 
 // Create talks for approved events
 try {
 $result = $this->eventsService->manageApprovedEvents($userID);
 $this->logger->info(__CLASS__ . ' :: ' . $result);
 
 } catch (\Exception $e) {
 $this->logger->alert(__CLASS__ . ' :: ' . $e->getMessage());
 }
 The action Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  15. 20.

    Use case #3 Restrict CLI action only to CLI calls

    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  16. 23.

    final class Api
 {
 
 private $sapi;
 
 
 public

    function __construct($sapi)
 {
 $this->sapi = $sapi;
 }
 
 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
 {
 if (strtolower($this->sapi) !== PHP_SAPI) {
 $response->withStatus(403);
 }
 
 return $next($request, $response);
 }
 
 } Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  17. 25.

    // ONLY WHEN CALLED THROUGH CLI
 if (PHP_SAPI !== 'cli')

    {
 return $response->withStatus(404)->withHeader('Location', '/404');
 }
 
 if (!$request->getParam('event')) {
 return $response->withStatus(404)->withHeader('Location', '/404');
 }
 
 
 // Create talks for approved events
 try {
 $result = $this->eventsService->manageApprovedEvents($userID);
 $this->logger->info(__CLASS__ . ' :: ' . $result);
 
 } catch (\Exception $e) {
 $this->logger->alert(__CLASS__ . ' :: ' . $e->getMessage());
 }
 Now the action can be simplified from this Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  18. 26.

    
 if (!$request->getParam('event')) {
 return $response->withStatus(404)->withHeader('Location', '/404');
 }
 
 


    // Create talks for approved events
 try {
 $result = $this->eventsService->manageApprovedEvents($userID);
 $this->logger->info(__CLASS__ . ' :: ' . $result);
 
 } catch (\Exception $e) {
 $this->logger->alert(__CLASS__ . ' :: ' . $e->getMessage());
 }
 To this Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  19. 27.

    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon Resources Basic Authentication Check

    https://github.com/phpminds/website CLI Request https://github.com/pavlakis/slim-cli SAPI Check https://github.com/pavlakis/php-server-interface- middleware