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. 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. The brief Use new technologies Event integration with meetup.com and

    joind.in Website Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  3. 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. 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. Use case #1 Create an admin area for authenticated users

    (to manage events) Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  6. 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. ->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. 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. 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. Implementation * Create a cron * Make a call through

    the CLI * Use same entry point (?) Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  11. Creating a Request object from parameters passed on the CLI

    Based on Bobby DeVeaux's (@bobbyjason) Gulp Skeleton Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  12. 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. 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. // 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. Use case #3 Restrict CLI action only to CLI calls

    Adoni Pavlakis (@pavlakis) - PHPNW16 Uncon
  16. 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. // 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. 
 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. 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