Test that middleware was used in Laravel - php

I have a post route inside a middleware that checks whether a user performing a request belongs to a specific role. I have 4 roles in total. And I have found it silly to write tests like :
testRole2CanNotDoAction
testRole3CanNotDoAction
testRole4CanNotDoAction
every time I want to test some action. So it would be really nice if I can just write 1 test
testMiddlewareWasCalled
to assure that route is placed in right place. How can I do it? To give you the context lets assume I have the following middleware:
class MustBeRole1
{
public function handle($request, Closure $next)
{
$user = $request->user();
if ($user && $user->isRole1()) {
return $next($request);
}
abort(403, 'You are not an role1!');
}
}
and following routes:
Route::group(['middleware' => ['isRole1']], function () {
Route::post('/testAction', ['as' => 'testAction', 'uses' => 'testActionController#test']);
}
How should my test look like?

Related

Laravel which route group does current route belong to

I am working with Laravel 5.5 and want to find out if the current route belongs to the auth:web middleware group. Is there a way to get this?
My routes:
/***********************************************************************************************************************
* Guest routes
**********************************************************************************************************************/
Route::group([
'middleware' => ['guest']
], function () {
Route::get("login", "Auth\LoginController#showLoginForm")->name("user.login");
Route::post("login", "Auth\LoginController#login")->name("user.do-login");
..................
});
/***********************************************************************************************************************
* All routes that require AUTH
**********************************************************************************************************************/
Route::group([
'middleware' => ['auth:user']
], function () {
..............
});
Something like below. Note I want the route group not the auth for the user:
$request()->currentRoute()->isGuest();
Is this possible? I cannot seem to find this in the Laravel docs.
I am trying to achieve executing a popup modal on all guest route pages without having to check and maintain a list of routes to test against. Thanks.
You can modify the handle method in the guest middleware class \App\Http\Middleware\RedirectIfAuthenticated, something like:
public function handle($request, Closure $next, $guard = null)
{
// share a token to all views for this middleware:
View::share('is_guest_route', true); // <-- this line is added
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
Make sure to also add:
use Illuminate\Support\Facades\View;
in the use-section of the file.
Now, in every view (blade file), you can do:
#if (isset($is_guest_route))
// do whatever is needed for guest routes
#endif
Fixed it with workaround. Added web. or app. prefix to all route names and now I search for this.
#if(substr(\Request::route()->getName(), 0, 4) !== 'web.')
#include('org.popupmsg.modal-pop')
#endif
with now the routes having changed to: web.user.login

Sentinel::check() always return FALSE in middleware

This is my route:
Route::group(['middleware' => ['web']], function () {
Route::get('/xx', 'xx\yy#zz');
This is my modification in Kernel.php:
protected $middlewareGroups = [
'web' => [
\Keevitaja\Linguist\LocalizeUrls::class,
LocalizeUrls.php:
public function handle($request, Closure $next)
{
if ($this->linguist->hasDefaultSlug() && $this->linguist->isDefaultDenied()) {
abort(404);
}
if (Sentinel::check())
{
dd("logged in");
}
dd("NOT logged in");
I am using Sentinel and Linguist for authentication and localisation.
I would like to get 'system_language' from the User model: check if the user is logged in and if he is, I would like to get his preferred language from the DB and then pass it to Linguist:
$this->linguist->localize('fr');
Unfortunately, Sentinel:check() always returns FALSE in this middleware. In my own middleware it is working well.
For sure, I am using the right Facade of Sentinel, because $sentinel = Sentinel::findById(1); returns a valid result.
The problem was caused because of the order in the Kernel.php $middlewareGroups where \Keevitaja\Linguist\LocalizeUrls::class was on first position.

Laravel, same URI, different route name, different middleware causes over loop

In laravel I've simply done this:
Route::group(["middleware" => "admin"], function() {
Route::get("/", "UserController#index")->name("user_index");
});
Route::group(["middleware" => "user", "as" => "User::"], function() {
Route::get("/", "DocumentController#index")->name("user_index");
});
The problem is when I am logged in as my Admin auth middleware, when going to "/" my browser returns too many redirects and stops. I'm guessing because the second route is removing this as when I print out php artisan route:list there is only one result for "/" and that's with the user middle's parameters so it's defo overriding the previous route.
What I don't understand is why would it do this is both have a separate middleware?
Both middlewares are extremely simple. Below is my admin
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->hasRole("customer_service") )
{
return $next($request);
}
return redirect("/");
}
And my user's middleware is exactly alike except the role is different
This is probably wrong but this is what I did to fix this particular issue with the above.
public function index() {
return \Auth::user()->hasRole("trainer") ? \App::call("App\Http\Controllers\Trainer\UserController#index")
: \App::call("App\Http\Controllers\User\UserController#index");
}

Multi-tenant sub-domains in Laravel 5

I am building a SaaS app in Laravel and want to give each person/company their own sub-domain. I have a users table with a company_id column. I have a companies table with a sub_domain column, which will be the sub-domain for that company. I don't want Company A to be able to visit Company B's sub-domain.
I have looked a quite a few articles and many forums on how to handle this and I am not finding any solutions that work. I am thinking that I need to use Middleware in combination with route grouping, but I just can't figure it out. Does anyone have experience with this?
Here is my routes.php:
Route::group(['domain' => '{sub_domain}.' . env('APP_DOMAIN_NAME'), 'middleware' => 'subdomain'], function() {
Route::auth();
Route::group(['middleware' => 'guest'], function () {
//Route::get('/', 'PublicController#index');
Route::get('/tickets/create', 'TicketsController#create');
Route::post('/tickets/create', 'TicketsController#store');
});
Route::group(['middleware' => 'auth'], function () {
Route::get('/tickets', 'TicketsController#index');
Route::get('/tickets/{id}', 'TicketsController#edit');
Route::patch('/tickets/{id}', 'TicketsController#update');
Route::delete('/tickets/{id}', 'TicketsController#destroy');
Route::get('/my-tickets', 'TicketsController#myTickets');
Route::get('/tickets/close/{id}', 'TicketsController#closeTicket');
});
});
The problem with this is that I can visit another sub-domain successfully. Now, I can still only view the tickets that are associated with the currently logged in user's company. I would like to throw a 403, or even just redirect back to their own sub-domain.
Here is the Subdomain.php middleware:
public function handle($request, Closure $next)
{
$request_uri = $request->server('HTTP_HOST');
$this->checkSubdomainExists($request_uri);
if(Auth::check()) {
$user = User::find(Auth::user()->id);
if($user->company->sub_domain !== Session::get('company_sub_domain')) {
Session::forget('company_sub_domain');
return 'not Authed';
}
}
return $next($request);
}
This middleware should work.
public function handle($request, Closure $next)
{
if(Auth::check()) {
$user = Auth::user();
$sub_domain = array_shift((explode(".",$_SERVER['HTTP_HOST'])));
if($user->company->sub_domain != $sub_domain) return abort(403);
}
return $next($request);
}
But pay attention beacuse if the company is not logged in, it can see the domain.
Sessions in Laravel can be specific to a domain, so you could use this feature with the current sub domain.
In the session configuration file:
'domain' => (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : null,

Laravel 5 : Restrict access to controllers by User group

I've started learning Laravel 5.1 and so far I'm liking it! But there is one thing I don't get yet..
In my previous project I had 2 specific controllers (eg: "normal", "extended") which , after a successfull login, were called based on the Users user_group from the database.
If "Foo.Bar" enters his valid credentials and has the group normal he is redirected to NormalControler. Since I wasn't using any framework I restricted access to the other group by setting a $_SESSION with the group and checking it. So if another group tried to access that controller he got redirected.
How would this be achievable in Laravel 5? So far I have a controller which is callable without an Authentication and one restricted by this code in routes.php :
// All routes in the group are protected, only authed user are allowed to access them
Route::group(array('before' => 'auth'), function() {
// TO-DO : Seperate Controller access
});
And the login looks like this :
public function performLogin()
{
$logindata = array(
'username' => Input::get('user_name'),
'password' => Input::get('user_pass')
);
if( Auth::attempt( $logindata ) ){
// return \Redirect::to( check group and access this controller based on it);
}
else {
// TO-DO : Redirect back and show error message
dd('Login failed!');
}
}
----- EDIT -----
I've run the artisan command and made this middleware as you suggested :
namespace App\Http\Middleware;
use Closure;
use Request;
class GroupPermissions
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $group)
{
// Check User Group Permissions
if( $request->user()->group === $group ){
// Continue the request
return $next($request);
}
// Redirect
return redirect('restricted');
}
}
and edited this line into Kernel.php into $routeMiddleware :
'group.perm' => \App\Http\Middleware\GroupPermissions::class
I think this is done right so far, correct me if I'm wrong! Could I then do something like this to restrict the controllers?
Route::group(array('before' => 'auth'), function() {
Route::group( ['middleware' => 'group.perm', 'group' => 'normal'], function(){
Route::get('/normal/index', 'DummyNormalController#index');
});
Route::group( ['middleware' => 'group.perm', 'group' => 'extended'], function(){
Route::get('/extended/index', 'DummyExtendedController#index');
});
});
Ok, here is what you might do. Once user is logged in, you would check his credentials, get his user_group and decide what controller he should be redirected to.
if( Auth::attempt( $logindata ) ){
$user = Auth::user();
if ($user->inGroup('normal')) {
return redirect()->route('normal_controllers_named_route');
}
return redirect()->route('extended_controllers_named_route');
}
return redirect()->back()->withFlashMessage('don\'t get me wrong');
This will handle right routing after logging in.
The next portion where you need to protect you routes from unwanted user groups may be achieved with middlewares.
do an artisan command php artisan make:middleware ShouldBeInGroup
go to app/http/Kernel.php and add your new middleware to the routeMiddleware array. Key of the item might be anything you like. Let's call in inGroup. So: 'inGroup' => 'App\Http\Middleware\ShouldBeInGroup'
Now, in your controller, in constructor, you are able to call this middleware
$this->middleware('inGroup:extended'); //we also passing the name of the group
at lastly, work on the our middleware. Open newly created ShouldBeInGroup class and edit the handle method.
public function handle($request, Closure $next, $groupName)
{
if (Auth::check() && Auth::user()->inGroup($groupName)) {
return $next($request);
}
return redirect('/');
}
And finally you should work on inGroup method, that should return true of false. I assume that you have user_group field your users table. Then in your User eloquent model add the method
public function inGroup($groupName) {
return $this->user_group == $groupName;
}
Edit
if you want to use this middleware in your routes, you can do the following
Route::group(array('before' => 'auth'), function() {
Route::get('/normal/index', ['middleware' => 'group.perm:normal', 'uses' =>'DummyNormalController#index']);
}
But generally it's better to put all your middlewares into your Controller's constructor
public function __construct(){
$this->middleware('group.perm:normal'); // you can also pass in second argument to limit the methods this middleware is applied to : ['only' => ['store', 'update']];
}
And also on this note, Laravel provides built in auth middleware that you can use
public function __construct(){
$this->middleware('auth');
$this->middleware('group.perm:normal');
}
so then your routes would become much cleaner, just:
Route::get('normal/index', 'DummyNormalController#index');
I think the best way to do that is using middlewares. See the doc here
You can easily create a middleware using the following artisan command:
php artisan make:middleware ExtendedMiddleware
If you can't or don't want to use artisan, you need to create a class in The App/Http/Middleware folder.
In this class you'll need the following method to handle the request. In the method you can check for the user group.
public function handle($request, Closure $next)
{
// check user group
if( user_group_ok )
return $next($request); // Continue the request
return redirect('restricted'); // Redidrect
}
You can then use this middleware in your route.php file:
Route::group(['middleware' => 'auth'], function()
{
// Logged-in user with the extended group
Route::group(['middleware' => 'extended'], function()
{
// Restricted routes here
});
// Normal routes here
});
You can create a Middleware called : PermissionFilter
In PermissionFilter, you check if requesting user is in the group or not.
I can't provide a demo for now, but if you want I can make a demo later.
L5 middleware: http://laravel.com/docs/5.1/middleware

Categories