How to implement a Slim v2 hook as middleware in Slim v3 - php

In Slim Framework v2 I use simple authentication function as a hook to check if a route needs login.
This is authentication code:
$authenticate = function ( $app ) {
return function () use ( $app ) {
if ( !isset( $_SESSION['userid'] ) ) {
$_SESSION['urlRedirect'] = $app->request()->getPathInfo();
$app->flash('danger', 'You need to login');
$app->redirect('/login');
}
};
};
this is how I use in Slim v2:
$app->get("/user/change-password", $authenticate($app), function () use ( $app ) {
$userStuff->changePassowrd($_SESSION['userid'], $newPassword, $oldPassword);
});
I can implement this to Slim v3 without a problem but I can't seem to understand how I supposed to do this with middleware(to learn and use the functionally)
I have tried this: this is my middleware class;
<?php
namespace entity;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class Auth
{
/**
* Example middleware invokable class
*
* #param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* #param \Psr\Http\Message\ResponseInterface $response PSR7 response
* #param callable $next Next middleware
*
* #return \Psr\Http\Message\ResponseInterface
*/
public function __invoke($request, $response, $next)
{
if ($this->authenticate()) {
$response = $next($request, $response);
} else {
echo 'You need to login';
}
return $response;
}
public function authenticate() {
if ( !isset( $_SESSION['userid'] ) ) {
return true;
}
return false;
}
}
?>
Registered it:
$app->add( new entity\Auth() );
and I don't know how to use this on a route as I did in Slim v2, where do I add this in the route to check if that route needs an authentication?
Thanks.

What you are looking for is actually excluding a route for the Auth middleware.
You do want that check for all other routes but not for changePassword.
In your __invoke you need to get the current route and only do authenticate if the route is not changePassword (if that's the name of your route).
public function __invoke($request, $response, $next)
{
$route = $request->getAttribute('route');
if (in_array($route->getName(), ['changePassword']) || $this->authenticate()) {
$response = $next($request, $response);
} else {
echo 'You need to login';
}
return $response;
}

Well the basic example is next
<?php
require 'vendor/autoload.php';
session_start();
class Auth {
public function __invoke($request, $response, $next) {
if ($this->authenticate()) {
$response = $next($request, $response);
} else {
echo 'You need to login';
}
return $response;
}
public function authenticate() {
if (isset($_SESSION['userid'])) {
return true;
}
return false;
}
}
$app = new \Slim\App();
$app->get('/open', function($request, $response, $args){
echo 'HAPPINESS FOR EVERYBODY, FREE, AND LET NO ONE BE LEFT BEHIND!';
});
$app->get('/closed', function($request, $response, $args){
echo 'You are authenticated!';
})->add(new Auth());
$app->get('/login', function($request, $response, $args){
$_SESSION['userid'] = 1;
});
$app->run();
I think there was a mistake in as authenticate function as !isset means there is no $_SESSION['userid'] and probably user is not logged in and your function tells that he is logged.
But anyway you can add middleware to single route or group of routes with add() method.

Related

How to restrict a user to only see their own profile

I have a view (resources/view/front/auth/profile.blade.php) and my route in file web.php is:
Route::get('/profile/{user}','UserController#edit')
->name('profile')
->middleware('profilecheck');
My problem is that when a user logs in and gets redirected to their own profile page (http://exmaple.com/profile/2), he/she can change the URL to http://exmaple.com/profile/3 and see other users' profile.
I want to use a middleware to check authenticated users id with URL parameter {user}. The $user->id will passed to the {user}, but I have no idea how.
Middleware UserProfile.php:
<?php
namespace App\Http\Middleware;
use App\User;
use Closure;
class UserProfile
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// $request->user()->id
// Auth::user()->id
return $next($request);
}
}
You can protect the route simply by removing the user id from the URL, but getting it through the authentication session instead.
So, your route signature should goes from:
Route::get('/profile/{user}', 'UserController#edit')->name('profile');
To this:
Route::get('/profile', 'UserController#edit')->name('profile');
So, in your controller, instead of getting the user id from the request:
public function edit(Request $request)
{
$user = User::findOrFail($request->id);
// ...
}
You could get the logged-in User through the Auth facade:
use Illuminate\Support\Facades\Auth;
public function edit(Request $request)
{
$user = Auth::user();
// ...
}
or just the auth() helper:
public function edit(Request $request)
{
$user = auth()->user();
// ...
}
This way, you are masking the URL to avoid a malicious user of doing things that he/she shouldn't.
You need to do something like this.
Your route
Route::get('/profile', [
'uses' => 'UserController#profile',
'middleware' => 'profilecheck'
]);
Your middleware
class CheckUserMiddleware
{
public function handle($request, Closure $next)
{
if(!auth()->user()) {
return redirect()->route('login');
}
return $next($request);
}
}
// Controller
public function index()
{
if (Auth::check() && Auth::user()->role->id == 2) {
return view('author.setting.settings');
} else {
Toastr::info('you are not authorized to access', 'Info');
return redirect()->route('login');
}
}
// Route
Route::group(['as'=>'user.','prefix'=>'user','namespace'=>'Author','middleware'=>['auth','user']], function (){
Route::get('/setting','SettingsController#index')->name('settings.settings');
});

Update/Put Route - Method not allowed

I'm using postman to test my API, but right now I have a problem with the put routes.
So this is a put route I wrote:
$app->put('/setting/{id}/settingvalue', function (ServerRequestInterface $request, Response
Interface $response, $args) {
try {
$user = new \Riecken\PBS\controller\SettingsValueController();
$result = $user->updateOneSettingsValueinDetail( $args['id'], $request->getParsedBody());
$response = $response->withJson($result);
$response = $response->withStatus(200);
return $response;
}catch(Exception $e) {
$response->getBody()->write($e->getMessage());
return $response->withStatus($e->getCode());
}
});
And this is the function which you see above (updateOneSettingsValueinDetail):
public function updateOneSettingsValueinDetail ($settingsvalueIdToUpdate, $body) {
try {
return $this->SettingsValueDao->update($settingsvalueIdToUpdate, $body);
}catch(DAOException $e) {
throw new \Exception($e->returnErrorMessage(), $e->returnHttpCode());
}catch(\Exception $e) {
throw new \Exception("System Error", 500);
}
}
The problem is that Postman tells me that the Method is not allowed, only POST and GET is allowed:
enter image description here
Does anybody know what type of problem that is and what the solution could be?
This response is come from Slim's NotAllowedHandler. And it is not only POST and GET as default. This response is not related to your mentioned above code.
Are you sure you don't customize "NotAllowedHandler" and you don't bind to app as middleware?
I wrote this code that containing that would create the same situation:
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
require __DIR__ . '/../vendor/autoload.php';
$app = new App([]);
$container = $app->getContainer();
$app->add(function ($request, $response, $next) {
$allowed = ['GET', 'POST'];
if (!in_array($request->getMethod(), $allowed)) {
$notAllowed = new \Slim\Handlers\NotAllowed();
return $notAllowed($request, $response, $allowed);
}
$next($request, $response);
});
$app->put('/setting/{id}/settingvalue', function (ServerRequestInterface $request, ResponseInterface $response, $args) {
die("Expected Context via PUT");
});
$app->get('/setting/{id}/settingvalue', function (ServerRequestInterface $request, ResponseInterface $response, $args) {
die("Expected Other Context via GET");
});
$app->run();
I hope to it help you.

Laravel 5.6 - Auth::check() Failing

I used the laravel spatie backup in my system, and all my functions such as creating a new backup, deleting, and downloading are working locally. I tried to deploy my website on a free hosting, everything seems to work except the delete and download function. Upon investigating, I have seen that it fails because of the Middleware I have created for the download/delete route. Here's my StaffMiddleware where only accounts with the staff role can access it.
Middleware
public function handle($request, Closure $next)
{
if(Auth::check())
{
if(Auth::user()->role == 'staff')
{
return $next($request);
}
else
{
return redirect('/');
}
}
else
{
return redirect('/');
}
}
Routes
Route::get('backup/create', 'Admin\BackupController#create');
Route::get('backup/download/{file_name}', 'Admin\BackupController#download');
Route::get('backup/delete/{file_name}', 'Admin\BackupController#delete');
When I try to access the download function, it redirects to the homepage since the Auth::check() line fails in my middleware. Note that I am logged in and authenticated while accessing the download function. This only happens in the live server, but all of the code works locally. Can you please help me on this one? Thanks!
can you try this
public function handle($request, Closure $next)
{
$user = Auth::user();
//dd($user); //debug if didn't work
if($user && $user->role == 'staff') // if your role is coming from relation then try `$user->role->name == 'staff'`
{
return $next($request);
}
return redirect('/');
}
I think you have to get the user from the request
public function handle($request, Closure $next)
{
if ($request->user() && $request->user()->role == 'staff')) {
return $next($request);
}
return redirect('/');
}
You can try this:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminMiddleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next,$guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect('admin/login');
}
}else{
if( \Auth::user()->role =="admin" ){
return $next($request);
}
}
return redirect("admin/login");
}
}

Display request method (GET,POST, ..) in Laravel middleware

I have a middleware class in Laravel and I wanted to get the action name like (GET, POST, DELETE, PUT,...) for logging the information. I have below code:
public function handle($request, Closure $next)
{
$api_key = $request->headers->get('x-api-key');
if($api_key!=$this->auth_key){
return $this->response->unauthorize(
"You're not authorize to access. Make sure that you're passing your api Key"
);
}
return $next($request);
}
I have this line $request->route(); that may help but I don't know about the method.
use Illuminate\Routing\Route;
private $route;
public __construct(Route $route) {
$this->route = $route;
}
public function handle($request, Closure $next)
{
$action = $this->route->getMethods(); // return array
$api_key = $request->headers->get('x-api-key');
if($api_key!=$this->auth_key){
return $this->response->unauthorize(
"You're not authorize to access. Make sure that you're passing your api Key"
);
}
return $next($request);
}

Slim 3 Implement Route Filtering

I am currently trying to implement route authentication filtering in Slim 3. What I would like to do is:
$app->get("/route", Filter::$guest(), function ($request, $response, $args) {
...
});
or maybe
$app->get("/route", function ($resquest, $response, $args) {
})->add(Filter::Admin);
and the Filter class would be:
class Filter
{
public static admin()
{
// Check if user is an admin.
// If not, throw an Error
}
...
In Slim 2, I could use someting like this
Filter.php
$authenticationCheck = function ($required) use ($app) {
return function () use ($required, $app) {
if ((!$app->auth && $required) || ($app->auth && !$required)) {
$app->redirect($app->urlFor("home"));
}
};
};
$authenticated = function () use ($authenticationCheck) {
return $authenticationCheck(true);
};
$guest = function () use ($authenticationCheck) {
return $authenticationCheck(false);
};
$admin = function () use ($app) {
return function () use ($app) {
if (!$app->auth || !$app->auth->isAdmin()) {
$app->notFound();
}
};
};
and in routes I could do:
$app->get("/route", $guest(), function () use ($app) {
//Route
});
I know that I can get the route through middleware, but I can't think of a good way to diffrenciate between a "admin" route and a normal route without having to build some sort of list.
You could create a basic middleware class Authorization:
<?php
class Authorization
{
/**
* Authorization middleware invokable class
*
* #param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* #param \Psr\Http\Message\ResponseInterface $response PSR7 response
* #param callable $next Next middleware
*
* #return \Psr\Http\Message\ResponseInterface
*/
public function __invoke($request, $response, $next)
{
$user = ""; //It should come from some place :)
if(!$this->isAuthorized($user)){
return $response->withRedirect('/notAuthorized');
}
return $next($request, $response);
}
/**
* Check if the given user is authorized.
*
* #param string $user The user to check.
*
* #return boolean True if the user is authorized, false otherwise.
*/
protected function isAuthorized($user){
return false;
}
}
Then you can extend it and create one middleware for guest authorization and another one for admin authorization:
<?php
class GuestAuthorization extends Authorization
{
protected function isAuthorized($user){
//Are you a guest?
$isGuest = true; //Your magic business here
return $isGuest;
}
}
class AdminAuthorization extends Authorization
{
protected function isAuthorized($user){
//Are you an admin?
$isAdmin = false; //Your magic business here
return $isAdmin;
}
}
Let's try with some routes and define the notAuthorized one:
<?php
$app->get("/guestRoute", function ($resquest, $response, $args) {
return $response->write("You're a guest");
})->add(new \GuestAuthorization());
$app->get("/adminRoute", function ($resquest, $response, $args) {
return $response->write("You're an admin");
})->add(new \AdminAuthorization());
$app->get("/notAuthorized", function ($resquest, $response, $args) {
return $response->write("You're not authorized for this, my son!");
});
PROs:
you can handle in different ways the authorization for every role;
you can add multiple middlewares for a single route.
CONs:
you can't handle in this way dynamic roles;
one middleware for each role.

Categories