I have a column in my User table named role with 2 possible values--"Admin" and
"Driver".
All my crud routes are protected with Auth middleware, but I'd like to further secure a few of those routes.
For example I'd like to have the "Create" routes only accessible by Users with the role column equalling "Admin". I wasn't sure how to go about this, so I can't provide examples of what I've tried.
web.php
...
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/users', App\Http\Livewire\User\Index::class)->name('users.index');
Route::get('/user/{user}/edit', App\Http\Livewire\User\Edit::class)->name('user.edit');
/* This is the route I want to protect to just "Admin" role */
Route::get('/user/create', App\Http\Livewire\User\Create::class)->name('user.create');
...
You can create a middleware with the artisan command
php artisan make:middleware IsAdminMiddleware
Then add something like this in the handle function of your middleware.
public function handle(Request $request, Closure $next)
{
// This validation assumes you can access role from User Model
if ($request->user()->role != "Admin") {
return response()->json(['error' => 'you are not an admin!'], 403);
}
return $next($request);
}
Finally add the middleware on your Routes
Route::get('/user/create', App\Http\Livewire\User\Create::class)
->middleware(IsAdminMiddleware::class) // <<----
->name('user.create');
For more info refer to middleware the docs at laravel.
You can use authorization in laravel for your case
in laravel you can use gate or policy for further feature
https://laravel.com/docs/9.x/authorization
Gate
define gate in App\Providers\AuthServiceProvider on method boot
use Illuminate\Support\Facades\Gate;
use Illuminate\Auth\Access\Response;
use App\Models\User;
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('create', function (User $user) {
return ($user->role == 'Admin')
? Response::allow()
: Response::deny('You must be an administrator.');
});
}
On your controller
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class CreateController extends Controller
{
public function create(Request $request)
{
$user = Auth::user();
if (Gate::allows('create', $user)) {
//create
}
}
}
Related
Trying to implement simple access authorization with Lumen. It works when doing the update (PUT) action.
But I would also like to handle accessing for example all articles.
I also tried the viewAny or view policy method but no success.
Router
$router->group(['prefix' => 'api/v1'], function () use ($router) {
$router->get('articles', ['uses' => 'ArticleController#showAllArticles']);
$router->get('articles/{id}', ['uses' => 'ArticleController#showOneArticle']);
$router->post('articles', ['uses' => 'ArticleController#create']);
$router->delete('articles/{id}', ['uses' => 'ArticleController#delete']);
$router->put('articles/{id}', ['uses' => 'ArticleController#update']);
});
AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
public function boot()
{
Gate::policy('App\Article', 'App\Policies\ArticlePolicy');
$this->app['auth']->viaRequest('api', function ($request) {
return app('auth')->setRequest($request)->user();
});
}
}
Policies
namespace App\Policies;
use App\User;
use App\Article;
class ArticlePolicy
{
public function showAllArticles(User $user, Article $post)
{
// not working
return true;
}
public function update(User $user, Article $post)
{
// this works
return true;
}
}
Controller
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function showAllArticles()
{
$this->authorize('showAllArticles');
return response()->json(Article::all());
}
public function showOneArticle($id)
{
return response()->json(Article::find($id));
}
public function update($id, Request $request)
{
$article = Article::findOrFail($id);
$this->authorize('update', $article);
$article->update($request->all());
return response()->json($article, 200);
}
}
As per the Laravel documentation on Authorization:
"When defining policy methods that will not receive a model instance, such as a create method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:"
public function create(User $user)
So:
public function showAllArticles(User $user)
"As previously discussed, some actions like create may not require a model instance. In these situations, you should pass a class name to the authorize method. The class name will be used to determine which policy to use when authorizing the action:"
$this->authorize('create', Post::class);
So:
$this->authorize('showAllArticles', Article::class);
Laravel 7.x Docs - Authorization - Writing Policies - Methods without Models
Laravel 7.x Docs - Authorization - Authorizing Actions Using Policies - via Controller Helper authorize
No explanation needed.
I'm making an application which requires a user and password for access. Every user should face the login view. Currently, writing the URL manually, I can access all the routes without login. What I want is to redirect every unauthenticated user to the login view, so that they can't see anything else until they log in.
LoginController
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
public function redirectTo() {
$isAuth = Auth::check();
if ($isAuth) {
return redirect('dashboard');
} else {
return redirect('login');
}
}
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function logout(Request $request)
{
Auth::logout();
return redirect('/login');
}
}
Routes
<?php
Route::get('/', function () {
return view('home');
});
Route::resource('project','ProjectController');
Route::resource('client','ClientController');
Route::resource('task','TaskController');
Route::resource('people','PeopleController');
Route::get('/login', function () {
return view('login');
});
Route::get('logout', '\App\Http\Controllers\Auth\LoginController#logout');
Auth::routes();
Route::get('/dashboard', 'DashboardController#index');
You should use a middleware for that. To get info on what a middleware is check here laravel.com/docs/master/middleware
Let's see how you can use the default Laravel's auth middleware for this purpose:
First of all get rid of your AdminBaseController and use only AdminController
Then you have to check that the auth middleware is enabled in the file app\Http\Kernel.php
You should have the line:
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
This means that the middleware is active and usable for your routes.
Now let's go inside the middleware class in app\Http\Middleware\Authenticate.php to specify the middleware's behaviour :
this method will be triggered before your controller constructor
public function handle($request, Closure $next)
{
//check here if the user is authenticated
if ( ! $this->auth->user() )
{
// here you should redirect to login
}
return $next($request);
}
Now the only thing left to do is to decide for what routes you should apply the middleware. Let's suppose you have two routes that you want to be only accessible from authenticated users, you should specify to use the middleware for these two routes in this way:
Route::group( ['middleware' => 'auth' ], function()
{
Route::get('admin/home', 'AdminController#index');
});
You should move your Auth::routes() before your first route
<?php
Auth::routes()
Route::get('/', function () {
return view('home');
});
And use auth middleware in your controllers where you want only authenticated user.
public function __construct()
{
$this->middleware('auth');
}
Move your Auth:Routes(); right after
Route::get('/', function () {
return view('home');
});
Then all your routes will be auth protected.
After I logout of my laravel application, in the browser I press the button to backward (go back) and then I see the dashboard.
I want to eliminate this "session" that laravel mantein if I go back.
can anyone help me?
EDIT: I have two login files, one is inside the Controllers/Auth and another is inside the Controller/. I'm sure this is not a good practice, but it's keeping my system up and running. how to solve this?
Controllers/Auth/LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Session;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* #return void
*/
private $user;
}
my Login Controllers/LoginController.php ->
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Session;
class LoginController extends Controller
{
private $user;
public function logout(){
Auth::logout();
\Session::flash('success',"logout");
return redirect()->route('login');
}
}
my DashboardController ->
use App\Authorization;
use App\BackLog;
use App\Key;
use App\isKeyInUse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
class DashboardController extends Controller
{
public function index() {
return view('dashboard');
}
}
my web.php ->
<?php
Route::get('/', 'LoginController#login')->name('login');
Route::get('auth/logout', 'Auth\LoginController#logout')->name('logout');
Route::get('/dashboard', 'DashboardController#index')->name('dashboard')->middleware('auth');
Route::post('/dashboard/getKey', 'DashboardController#getKey')->name('dashboard.key')->middleware('auth');
This is happening because caching. to prevent that we can create a middleware that intercepts every request and set the cache to expire in0 time and thus it will force the page to reload when the user press the back button here's the steps to create the middleware :
first
create a middleware i will call it MyAuth:
php artisan make:middleware MyAuth
second
register the middleware in app/Http/kernel.php
protected $routeMiddleware = [
...,
'my_auth' => \App\Http\Middleware\MyAuth::class,
];
third
in the newly created middleware app/Http/Middleware/MyAuth.php
public function handle($request, Closure $next, $guard = null)
{
$response = $next($request);
return $response
->withHeaders([
'Cache-Control' => 'no-store, no-cache, must-revalidate',
'Pragma'=> 'no-cache',
'Expires' => '0'
]);
}
}
Then
you can add your middleware like so:
Route::group(['middleware' => 'my_auth'], function() {
// All your routes you want to be secure
});
This code has been derived from this video
You are missing Request in logout function
public function logout(Request $request){
Auth::logout();
\Session::flash('success',"logout");
return redirect()->route('login');
}
And write in your dashboard controller
public function __construct()
{
$this->middleware('auth');
}
Insert these lines to your Dashboard controller and then check:
public function __contruct()
{
$this->middleware('auth');
}
This will check user is logged in or not? If user is loggedout, then it send to specific login page as you defined in auth middleware.
Pressing the Back button of your browser will load the previously loaded document. It is just visible but will not work for sure. For this you just have to override back press event from javascript.
See link How to Detect Browser Back Button event - Cross Browser
In Laravel 7.x, you can logout from the controller by using the following command:
Auth::logout()
I have a small dilema, i'm trying to make a login system that differentiates normal users from admin users using the laravel auth scaffolding.
The problem is it goes in a infinite redirect loop in the middleware.
After I press the login button it constantly redirects to a route and the question is, how can I solve this issue the "laravel way" or any other way for that matter.
Here are my controllers:
1. The basic home controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('home');
}
}
The main admin controller - entry controller:
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class Start extends Controller
{
public function index(){
return view('admin/index');
}
}
Login Controller(the default one from the auth scaffolding- modified by me, I removed the constructor):
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/home';
}
The Middleware(redirect if RedirectIfAuthenticated):
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if(Auth::user()->type == 2){//if user type is 1 then it's an admin.
return redirect()->route('web-admin');
}else{
return redirect()->route('home');
}
}
return $next($request);
}
}
The route file(web routes)
Route::get('/', function () {
return view('index');
});
Auth::routes();
Route::middleware(['auth','guest'])->group(function() {
Route::get('home',['as'=>'home', 'uses'=>'HomeController#index']);
Route::get('web-admin',['as'=>'web-admin', 'uses'=>'Admin\Start#index']);
});
The guest/RedirectIfAuthenticated redirects any request to corresponding home route for authenticated users. The problem is that admin home route is behind this middleware as well, that's why it keeps redirecting to the same page.
You need to remove the guest middleware from the route group - it should only be applied to routes that should be available to unauthenticated users only.
For sure this is an infinite loop because you applied both guest and auth middle ware to your routes, so also authenticated users will be redirected and this is an infinite loop.
Keep the RedirectIfAuthenticated.php as its original code and redirect authenticated users inside your main controller based on their type:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
// if type 1 is admin, why did you redirect type 2 to web-admin?!
if($request->user()->type == 2) { //if user type is 1 then it's an admin.
return redirect()->route('web-admin');
}
return view('home');
}
}
You may do same redirection in your admin controller for normal users to redirect them back in case they try to access admin page.
Additionally modify web.php routes as following:
Route::get('/', function () {
if(auth()->user()->type == 2) { //if user type is 1 then it's an admin.
return redirect()->route('web-admin');
} else {
return redirect()->route('home');
}
})->middleware('auth');
Auth::routes();
Route::middleware('auth')->group(function() {
Route::get('home',['as'=>'home', 'uses'=>'HomeController#index']);
Route::get('web-admin',['as'=>'web-admin', 'uses'=>'Admin\Start#index']);
});
In Laravel 4.2, I have the following route:
Route::group(array('before' => 'auth'), function() {
Route::post('/account/edit', array(
'as' => 'account-edit',
'uses' => 'UserController#accEdit'
));
});
I have a ClientController and an AdminController for common user and admin, respectively.
Assuming that I know the user type (Auth::getUser()->getType()), how can I replace the UserController with the correct controller without adding extra logic to routes class? Can this be done with filters?
I'm trying to avoid an extra controller between the routes and the final controller.
Actually, it is not necessary to create two user controller. Just use middleware to limit the access rights of clients. By this way, you can keep the original UserController.
You can add IsAdmin.php in the middleware.
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Contracts\Auth\Guard;
class IsAdmin {
public function handle($request, Closure $next)
{
if (Auth::getUser()->getType() === 'admin')
{
return $next($request);
}
return new RedirectResponse(url('/'));
}
}
In kernel.php, you need declare your middleware.
protected $routeMiddleware = [
// some other middlewares
'admin' => 'App\Http\Middleware\IsAdmin',
];
Then, add the following statements in public function __construct of the UserController.php
$this->middleware('admin', ['only' => ['OnlyForAdmin1','OnlyForAdmin2']]);
Thus, clients will have no access to the function OnlyForAdmin1 and function OnlyForAdmin2.