Authentication Middleware Slim 3.0 - php

I am using a middleware to redirect to login page if session is not set.
$app->get('/dashboard', function (Request $request, Response $response, $args) {
include_once('employee-portal/dashboard.php');
return $response;})->add(new AuthMiddleware('counter', true, $app->getContainer()));
and my middleware:
class AuthMiddleware implements MiddlewareInterface{
private $min_role = 'counter';
private $redirect = true;
private $container = null;
public function __construct($role_required, $login_redirect, $container)
{
$this->min_role = $role_required;
$this->redirect = $login_redirect;
$this->container = $container;
}
public function __invoke($request, $response, $next)
{
if ($this->userIsAuthorised()) {
return $next($request, $response);
} else {
if ($this->redirect) {
/**
* #var \Slim\Router router`
*/
return $response->withRedirect(Details::getBaseUrl() . '/login' . '?ref=' . $request->getUri());
}
return $response->withStatus(401)->write("Sorry boss you are not authorised to see my secret");
}
}
private function userIsAuthorised()
{
if ($this->min_role == 'counter') {
return true;
} else if (SessionManager::isLoggedIn()) {
if ($_SESSION['user_type']=='counter') {
return true;
}
}
return false;
} }
but this doesn't works.
i can even see the dashboard page without login. and even after login i cannot access the $_SESSION['user_type'] session variable.
any help would be appriciated. thanks in advance.

You are passing 'counter' into your AuthMiddleware constructor, causing it to always return true in the first if() statement of userIsAuthorised() method.
if ($this->min_role == 'counter')
will always be true because you set $this->min = 'counter' in your constructor. Try rewriting the new AuthMiddleware() and constructor so that you just pass in the container. Before calling the new AuthMiddleware() you can do the following:
$container['min_role'] = 'counter' if you need it elsewhere in your app.

Related

How to redirect from a EventSubscriber in Symfony 5

I'm attempting to write a custom auth checker for Symfony5. This is to run on selected Controllers, all of which have an AuthenticationControllerInterface which includes numerous other bits of relevant code.
I am attempting to use an EventSubscriber bound to the ControllerEvent. This checks for this interface and identifies correctly the relevant controllers.
Please see the below for context:
class BearerTokenSubscriber implements EventSubscriberInterface
{
public function onKernelController(ControllerEvent $event)
{
$controller = $event->getController();
// load the controller
if (is_array($controller)) {
$controller = $controller[0];
if ($controller instanceof AuthenticationControllerInterface) {
if(
// my assorted auth functionality: works as expected
) {
//... where my question lies:
}
}
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => 'onKernelController',
];
}
}
At "where my question lies": is the point I want to do a redirect... in the following ways (in order of preference):
returning a specific controller method (I have a 403 pre-configured).
redirecting to another URL (with a 403)
Thanks in advance.
You can use ControllerEvent::setController($myController) to provide a controller of your choice once your conditions are met:
class TestControllerEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
$events = [];
$events[KernelEvents::CONTROLLER] = ['onKernelController', 0];
return $events;
}
public function onKernelController(ControllerEvent $event): void
{
$controller = $this->getControllerObjectFromEvent($event);
// Check if your conditions are met
if ($controller instanceof AuthenticationControllerInterface && $whateverElse) {
$event->stopPropagation();
// Set your custom controller that produces a 403 response
$event->setController(static function () {
return new Response(null, 403);
});
}
}
private function getControllerObjectFromEvent(ControllerEvent $event): ?object
{
$controller = $event->getController();
if (true === is_object($controller)) {
return (object) $controller;
}
if (false === is_array($controller)) {
return null;
}
foreach ($controller as $value) {
if (true === is_object($value)) {
return $value;
}
}
return null;
}
}
The specific answer in my case was:
if (/* some test logic */) {
$event->stopPropagation();
$event->setController(
static function () use ($controller) {
return $controller->some403ErrorResponse();
}
);
}
Many thanks to Jeroen who pointed me in the correct direction.
Have marked his as correct also.

SLIM FRAMEWORK 3: Can't pass variable from middleware to controller

What's wrong with my code? I can't pass variable from my Auth middleware to controller. In controller, the attribute "null".
Index:
$c = new \Slim\Container($configuration);
$api = new \Slim\App($c);
$api->group('/', function () use ($api) {
$this->post('login', '\AuthController:login');
...
$this->post('getIngredientsCategories', '\IngredientsController:getIngredientsCategories');
})->add(new ApiAuthenticateController());
$api->run();
Middleware (ApiAuthenticateController)
use \Firebase\JWT\JWT;
class ApiAuthenticateController
{
public function __invoke($request, $response, $next)
{
$jwtDecoded = null;
$req = $request->getUri()->getPath();
$_req = RequestValidatorController::isRequestEnabled($req);
if ($_req !== false) {
if ($_req['login_required']) {
$jwt = filter_var($request->getHeaderLine('AUTHORIZATION'), FILTER_SANITIZE_STRING);
$jwt = explode(" ", $jwt)[1];
try {
$jwtDecoded = JWT::decode($jwt, JWT_SECRET, array('HS256'));
} catch (\Exception $e) {
return $this->deny_access($response);
}
}
$request = $request->withAttribute('foo', $jwtDecoded);
//HERE - attribute "foo" in $request exists - checked by var_dump()..
$response = $next($request, $response);
$response = $response->withHeader('Content-type', 'application/json');
return $response;
} else {
return $this->deny_access($response);
}
}
IngredientsController
class IngredientsController extends Controller
{
private $_ingredients;
public function __construct(\Interop\Container\ContainerInterface $container)
{
parent::__construct($container);
}
}
Controller
class Controller
{
private $request;
private $response;
protected $data;
protected $method;
protected $user;
public function __construct(Interop\Container\ContainerInterface $container)
{
$this->request = $container->get('request');
$this->response = $container->get('response');
$this->data = (object)Tools::stripInput($this->request->getParsedBody());
$this->method = $this->request->getUri()->getPath();
$this->user = $this->request->getAttribute('foo');
var_dump($this->user);
// returns NULL. if $this->request->getAttributes()... returns empty
}
}
Please note, that code is "cleaned" for this post and for better readability, some functions and conditions where deleted. Code is working - sorry for possible typos.
the problem is coming from here
in the middleware ApiAuthenticateController, you should change the code like this
$request = $request->withAttribute('foo', $jwtDecoded);
// you should return the $next here not to do anything else
return $next($request, $response);
and in your IngredientsController:getIngredientsCategories part of code you should return the
function getIngredientsCategories ($request, $response, $args){
//do your job...
$response = $response->withHeader('Content-type', 'application/json');
return $response;
}
and finally, in the constructor, you have don't have the actual $request after the middleware I think so you would have the $this->request->getAttributes() inside your getIngredientsCategories function
Hope it helps.

Method Chaining in CodeIgniter Library

I've been working on a script for work. It's to use Slack with PHP. At work we use CodeIgniter (sad face) so I have to accommodate and I decided to write my script as a Library.
There isn't an issue here in that the code doesn't work, as it works fine, but I am just interested to know how I can apply method chaining when calling the library so myself and my colleagues can code cleaner when we use the library.
Here's the Library I wrote - I'm more of a programmer in-training so my OOP knowledge is limited.
<?php defined('BASEPATH') OR exit('No direct script access allowed');
include('../vendor/autoload.php');
use GuzzleHttp\Client;
class Slack {
protected $ci;
private $channel;
private $endpoint;
private $icon;
private $username;
public function __construct()
{
$this->ci =& get_instance();
$this->ci->config->load('slack');
$this->baseUri = $this->ci->config->item('base_uri');
$this->channel = $this->ci->config->item('channel');
$this->endpoint = $this->ci->config->item('endpoint');
$this->icon = $this->ci->config->item('icon');
$this->username = $this->ci->config->item('username');
$this->client = new Client([
'base_uri' => $this->baseUri
]);
}
public function getChannel()
{
return $this->channel;
}
public function setChannel($channel)
{
$this->channel = $channel;
return $this;
}
public function getEndpoint()
{
return $this->endpoint;
}
public function setEndpoint($endpoint)
{
$this->endpoint = $endpoint;
return $this;
}
public function getIcon()
{
return $this->icon;
}
public function setIcon($icon)
{
(mb_substr($icon, 0, 1) == ':')
? $this->iconType = 'icon_emoji'
: $this->iconType = 'icon_url';
$this->icon = $icon;
return $this;
}
public function getIconType()
{
return ($this->iconType) ? $this->iconType : 'icon_emoji';
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function to($channel)
{
$this->setChannel($channel);
return $channel;
}
public function from($username)
{
$this->setUsername($username);
return $username;
}
public function icon($icon)
{
$this->setIcon($icon);
return $icon;
}
public function payload($text)
{
$payload = [
'channel' => $this->getChannel(),
$this->getIconType() => $this->getIcon(),
'link_names' => 1,
'text' => $text,
'username' => $this->getUsername(),
];
return $payload;
}
public function send($text)
{
$payload = json_encode($this->payload($text));
$this->client->post($this->getEndpoint(), [
'body' => $payload
]);
return $this;
}
}
Now I'm using this in our API, which is coded in a Controller, and this is how I am calling the methods:
<?php
// ...
$this->load->library('slack');
$this->slack->icon(':hotdog:');
$this->slack->send('Hello...');
As I said, this works fine...
I wanted though, to be able to do method chaining, like so:
<?php
// ...
$this->slack->icon(':hotdog:')->send('Hello...');
Can you tell me if this is possible and how to achieve it?
Thank you.
As i can see to archive what you want you just need to change that
public function icon($icon)
{
$this->setIcon($icon);
return $icon;
}
to that
public function icon($icon)
{
$this->setIcon($icon);
return $this;
}
and then you will be able to do what you want
$this->slack->icon(':hotdog:')->send('Hello...');
Anyway your icon method no need to return $icon you already have getIcon method
also your send method calling payload method before make request so that should work

Symfony2 sessions in service

I'm getting a problem with Symfony2 sessions.
I'm trying to make a service which is executed at the top of every controller.
The aim of this service is to authenticate user.
I first check the session to see if there is an "auth" key.
If there's not, I check if there is a cookie ...
The problem is on accessing user's session in the service.
I've passed the Request object to my service parameters and I get the session using $request->getSession();.
Everything seems to work but no data is stored in the session ...
I'm pretty sure the problem comes from the way I get session in the service. But, I don't know how I can do it another way.
Can someone help me please ?
Here is my services.yml file :
services:
myBundle.authService:
class: MyCompany\MyBundle\Services\AuthService
scope: request
arguments:
request: #request
logger: #logger
em: #doctrine.orm.entity_manager
Here is my AuthService class :
namespace MyCompany\MyBundle\Services;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Doctrine\ORM\EntityManager;
use Monolog\Logger;
use MyCompany\MyBundle\Entity\User;
use MyCompany\MyBundle\Entity\Session;
class AuthService
{
private $_request;
private $_logger;
private $_em;
private $_session;
public function __construct(Request $request, Logger $logger, EntityManager $em)
{
$this->_request = $request;
$this->_logger = $logger;
$this->_em = $em;
$this->_session = $this->_request->getSession();
$this->_session->start();
$this->_fUser = null;
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$this->_serializer = new Serializer($normalizers, $encoders);
}
// THIS FUNCTION IS CALLED IN CONTROLLER
public function checkAuth()
{
$_return = false;
if($this->checkSession())
{
$_return = true;
}
else
{
$userCookie = $this->checkCookie();
if($userCookie)
{
if(is_array($userCookie) && array_key_exists('token', $userCookie) && array_key_exists('value', $userCookie))
{
$fUser = $this->findUserBySession($userCookie['token'], $userCookie['value']);
if($fUser)
{
$_return = $this->updateSession($fUser);
}
}
}
}
return $_return;
}
// CHECK IF SESSION HAS 'FUSER' KEY AND 'AUTH' KEY
private function checkSession()
{
$_return = false;
if($this->_session && $this->_session->has("FUSER") && $this->_session->has("AUTH") && preg_match("/[0-9a-zA-Z]{58}/", $this->_session->get("AUTH")) === true)
{
// I NEVER GO HERE
$this->_logger->info("OK \o/");
$_return = $this->_session->get('FUSER');
}
return $_return;
}
// CHECK IF AUTH COOKIE IS SET
private function checkCookie()
{
$_return = false;
$cookies = $this->_request->cookies;
if($cookies && count($cookies) > 0)
{
foreach($cookies as $key => $val)
{
if(preg_match("/[0-9a-zA-Z]{58}/", $key) && preg_match("/[0-9a-zA-Z]{58}/", $val))
{
$_return = array('token' => $key, 'value' => $val);
}
}
}
return $_return;
}
// FIND SESSION IN DB AND USER ASSOCIATED
private function findUserBySession($token, $value)
{
$_return = false;
$fSessionRepository = $this->_em->getRepository("MyCompanyBundle:FSession");
if($fSessionRepository)
{
$fSession = $fSessionRepository->findOneBy(array('token' => $value));
if($fSession)
{
$fUser = $fSession->getUser();
if($fUser)
{
if($fUser->getToken() === $token)
{
$_return = $fUser;
}
}
}
}
return $_return;
}
// UPDATE SESSION TO REGISTER 'USER' AND 'AUTH' KEYS
public function updateSession(User $fUser)
{
$_return = false;
if($this->_session)
{
$this->_session->set('FUSER', $this->serialize($fUser, 'json'));
$this->_session->set('AUTH', User::generateToken());
// THIS WORKS FINE
$this->_logger->info("OK !!! _o/");
$_return = $this->serialize($fUser, 'json');
}
return $_return;
}
// SERIALIZE OBJECT
public function serialize($JSONObject, $format)
{
return $this->_serializer->serialize($JSONObject, $format);
}
// DESERIALIZE OBJECT
public function deserialize($JSONObject, $entity, $format)
{
return $this->_serializer->deserialize($JSONObject, $entity, $format);
}
}
Thank you in advance for your time and help.

PHP MVC - Why is my request/router always going to home controller?

I've tried many fixes but for some reason my request and routing mechanisms in my framework aren't working properly. It's a multi-language site (en/fr) and I use mod_rewrite to get the language, class, method and an optional argument. My request.php sets the controller to languageController if the language isn't set, and it works (successfully echo "language" in controller). But, if the language is set, the controller that always ends up displaying is homeController despite that the uri is /en/about. I have no idea why this is happening and it's really frustrating me. Here's the problem code, help is appreciated.
Note: I haven't used namespaces as it is a small website that I just want to get up and running and there's no real need, although I plan to edit it to use namespaces in the future simply for the leaning.
relevant parts of index.php
//analyze request
$request = new Request($_GET); //breaks up the uri into an array
//routing
$router = new Router($request); //stores the requested uri in the router
$router->route(); //sets the proper route
//instancialize and execute the controller
$class = $router->getController();
$method = $router->getMethod();
$controller = new $class;
$controller->$method();
Request.php
class Request {
private $request;
public function __construct($uri) {
$this->request = $uri;
//if the language isn't selected
if($this->request['language']=="") {
$this->request['language'] = "language";
$this->request['class'] = "language";
$this->request['method'] = "index";
}
//else if the controller is not selected
else if($this->request['controller']=="") {
$this->request['class'] = "home";
$this->request['method'] = "index";
}
//stores the requested method
else if($this->request['method']=="") {
$this->request['method'] = "index";
}
//stores a value of false if there is no argument in the uri
if($this->request['arg']=="") {
$this->request['arg'] = false;
}
}
public function getLanguage() {
return $this->request['language'];
}
public function getClass() {
return $this->request['class'];
}
public function getMethod() {
return $this->request['method'];
}
public function getArgument() {
return $this->request['arg'];
}
}
Router.php
class Router {
private $language;
private $controller;
private $view;
private $method;
private $arg;
public function __construct(Request $request) { //given an instance of request, store it in local variables
$this->language = $request->getLanguage();
$this->controller = $request->getClass() . 'Controller';
$this->view = $request->getClass() . 'View';
$this->method = $request->getMethod();
$this->arg = $request->getArgument();
}
public function route() { //put the requested uri into forms we can deal with
if($this->language == "fr") { //compare it against url translation chart
//run all uri values through a chart that translates them from french to english if it finds a match
}
else { //it must be english
}
}
public function getLanguage() {
return $this->language;
}
public function getController() {
return $this->controller;
}
public function getView() {
return $this->view;
}
public function getMethod() {
return $this->method;
}
public function getArgument() {
return $this->arg;
}
}

Categories