Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Antonis Pavlakis

October 01, 2016
Tweet

More Decks by Antonis Pavlakis

Other Decks in Programming

Transcript

  1. Middleware to the
    rescue
    A PHPMiNDS Story…
    Adoni Pavlakis (@pavlakis)
    PHPNW16 UNCON

    View Slide

  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

    View Slide

  3. The brief
    Use new technologies
    Event integration with
    meetup.com and joind.in
    Website
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide

  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

    View Slide

  6. Taken from: Slim 3 website
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  7. Use case #1
    Create an admin area for authenticated
    users (to manage events)
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  11. $app->add($container->get('PHPMinds\Middleware\AuthCheck'));
    Call it before loading the routes
    (in middleware.php)
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  12. Use case #2
    Create a joind.in talk
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide

  14. Implementation
    * Create a cron
    * Make a call through the CLI
    * Use same entry point (?)
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  15. Creating a Request object from parameters
    passed on the CLI
    Based on Bobby DeVeaux's (@bobbyjason) Gulp Skeleton
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  19. Available on packagist
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  20. Use case #3
    Restrict CLI action only to CLI calls
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  21. Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  22. Just create a middleware!
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide

  24. $app->get('/status', 'PHPMinds\Action\EventStatusAction:dispatch')

    ->add(new Pavlakis\Middleware\Server\Sapi(["cli"]));
    Add it to the route…
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  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

    View Slide


  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

    View Slide

  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

    View Slide

  28. Questions ?
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide

  29. Thank you!
    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon

    View Slide