I want to create two middleware to redirect the authenticated user, if it is an admin it will be redirected to the backoffice otherwise it will be redirected to a simple dashboard for simple users.
But I want to use only the users table without adding another table for the admins.
RedirectIfAuthenticated.php
<?php
class RedirectIfAuthenticated
{
/**
* 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)->check()) {
if (Auth::user()->role_id == 1)
{
return redirect('/admin/home');
}
return redirect('/dashboard');
}
return $next($request);
}
}
DashboardController.php
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('authFront.layoutAuthenticatedUser.dashboard');
}
}
web.php
Route::get('/us-admin', function () { return redirect('/admin/home'); })->name('admin.dashboard');
// Authentication Routes...
$this->get('login', 'Auth\LoginController#showLoginForm')->name('auth.login');
$this->post('login', 'Auth\LoginController#login')->name('auth.login');
$this->post('register', 'Auth\RegisterController#register')->name('auth.register');
$this->post('logout', 'Auth\LoginController#logout')->name('auth.logout');
// Change Password Routes...
$this->get('change_password', 'Auth\ChangePasswordController#showChangePasswordForm')->name('auth.change_password');
$this->patch('change_password', 'Auth\ChangePasswordController#changePassword')->name('auth.change_password');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('auth.password.reset');
$this->post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('auth.password.reset');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController#reset')->name('auth.password.reset');
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
Route::get('/home', 'HomeController#index');
});
Route::get('/dashboard', 'DashboardController#index');
You need to establish user roles in this case, or have an optional boolean field on the users table for is_admin. Then in middleware it would simply be something like:
class RedirectIfNotAdmin
{
/**
* 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::user()->admin()) {
return redirect()->to('user-dashboard');
}
return $next($request);
}
}
So in your case I would create a separate middleware for each role post-authorization. Then you would apply AdminMiddleware to all routes prefixed with 'admin', SimpleUserMiddleware to all other routes, for example.
You can simply check roles in RedirectIfAuthenticated middleware which do already exist by default and restrict the routes by creating an admin middleware
In app/Http/Middleware/RedirectIfAuthenticated.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* 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)->check()) {
if (Auth::user()->role_id == 1)
{
return redirect('/admin/home');
}
return redirect('/dashboard');
}
return $next($request);
}
}
In app/Http/Middleware/AuthenticateAdmin.php
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class AuthenticateAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!Auth::check() || Auth::user()->role_id != 1)
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('login');
}
}
return $next($request);
}
}
In app/Http/Kernal.php add this line in $routeMiddleware array
'admin' => \App\Http\Middleware\AuthenticateAdmin::class,
In routes/web.php
Route::middleware('admin')->prefix('admin')->group(function() {
Route::get('/home', 'HomeController#index')->name('admin.home');
});
In app/Http/Controllers/Auth/LoginController.php add this function
protected function authenticated(Request $request, $user)
{
if ($user->role_id == 1) {
return redirect()->intended(route('admin.home'));
}
return redirect()->intended(route('dashboard'));
}
Hope this helps you
Related
I'm using Laravel 9 with Breeze and below is all the modifications. What I did was set the session_driver to database instead of file. Right now I am using subdomains for users but later on ill be adding the ability to buy and set a domain for each tenant so that is why im using the subdomainOrDomain middleware instead of just the domain middleware by stancl. My problem is that the auth session wont be set and i dont see any entries in the tenants database within the table sessions. So no session gets set at all. The central domain also has auth and it works perfectly but just not the tenant.
App\Http\Kernel.php
protected $middlewareGroups = [
//...
'tenant' => [
\App\Http\Middleware\InitializeTenancyByDomainOrSubdomain::class,
\Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains::class,
],
'universal' => [],
];
App\Http\Middlware\InitializeTenancyByDomainOrSubdomain
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Str;
use App\Http\Middleware\InitializeTenancyByDomain;
use App\Http\Middleware\InitializeTenancyBySubdomain;
class InitializeTenancyByDomainOrSubdomain
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->isSubdomain($request->getHost()))
{
return app(InitializeTenancyBySubdomain::class)->handle($request, $next);
}
else
{
return app(InitializeTenancyByDomain::class)->handle($request, $next);
}
}
protected function isSubdomain(string $hostname): bool
{
return Str::endsWith($hostname, config('tenancy.central_domains'));
}
}
App\Http\Middlware\InitializeTenancyByDomain
namespace App\Http\Middleware;
use Closure;
use Stancl\Tenancy\Tenancy;
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
use Stancl\Tenancy\Middleware\IdentificationMiddleware;
class InitializeTenancyByDomain extends IdentificationMiddleware
{
/** #var callable|null */
public static $onFail;
/** #var Tenancy */
protected $tenancy;
/** #var DomainTenantResolver */
protected $resolver;
public function __construct(Tenancy $tenancy, DomainTenantResolver $resolver)
{
$this->tenancy = $tenancy;
$this->resolver = $resolver;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(in_array($request->getHost(), config('tenancy.central_domains'), true))
{
return $next($request);
}
return $this->initializeTenancy(
$request, $next, $request->getHost()
);
}
}
App\Http\Middlware\InitializeTenancyBySubdomain
namespace App\Http\Middleware;
use Closure;
use Exception;
use Illuminate\Support\Str;
use Illuminate\Http\Response;
use Stancl\Tenancy\Exceptions\NotASubdomainException;
class InitializeTenancyBySubdomain extends InitializeTenancyByDomain
{
/**
* The index of the subdomain fragment in the hostname
* split by `.`. 0 for first fragment, 1 if you prefix
* your subdomain fragments with `www`.
*
* #var int
*/
public static $subdomainIndex = 0;
/** #var callable|null */
public static $onFail;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(in_array($request->getHost(), config('tenancy.central_domains'), true))
{
return $next($request);
}
$subdomain = $this->makeSubdomain($request->getHost());
if (is_object($subdomain) && $subdomain instanceof Exception) {
$onFail = static::$onFail ?? function ($e) {
throw $e;
};
return $onFail($subdomain, $request, $next);
}
// If a Response instance was returned, we return it immediately.
if (is_object($subdomain) && $subdomain instanceof Response) {
return $subdomain;
}
return $this->initializeTenancy(
$request,
$next,
$request->getHost()
);
}
/** #return string|Response|Exception|mixed */
protected function makeSubdomain(string $hostname)
{
$parts = explode('.', $hostname);
$isLocalhost = count($parts) === 1;
$isIpAddress = count(array_filter($parts, 'is_numeric')) === count($parts);
// If we're on localhost or an IP address, then we're not visiting a subdomain.
$isACentralDomain = in_array($hostname, config('tenancy.central_domains'), true);
$notADomain = $isLocalhost || $isIpAddress;
$thirdPartyDomain = ! Str::endsWith($hostname, config('tenancy.central_domains'));
if ($isACentralDomain || $notADomain || $thirdPartyDomain) {
return new NotASubdomainException($hostname);
}
return $parts[static::$subdomainIndex];
}
}
Routes\Tenant.php
Route::middleware(['tenant', 'web'])->group(function()
{
Route::get('login', [AuthenticatedSessionController::class, 'create'])
->name('login');
Route::post('login', [AuthenticatedSessionController::class, 'store']);
});
in the tenant routes i have also tried switching the web middleware to guest as is by default with laravel breeze but it trows the $errors not found error (the validation handling variable).
i have two type of api authentication way and i want if my first way fails check santcum laravel auth .
i make midlleware class and but i dont know how to check santcum in that
<?php
namespace App\Http\Middleware;
class UserApiAuthenticated
{
/**
* User authenticator container
* #var UserAuthenticatorServiceInterface
*/
protected UserAuthenticatorServiceInterface $user_authenticator_service;
public function __construct(UserAuthenticatorServiceInterface$user_authenticator_service)
{
$this->user_authenticator_service = $user_authenticator_service;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request):
(\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if ($this->user_authenticator_service->isUserAuthenticated($request)) {
return $next($request);
} elseif () {
}
I would do something like that :
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #param string|null ...$guards
*
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard)
{
if (Auth::guard($guard)->check())
{
return $next($request);
}
}
abort(403, 'Unauthenticated')
}
it checks all the guards one by one,
when you use the middleware you set the guards :
->middleware('mymiddleware:web,sanctum')
or with a route group :
Route::group(['middleware' => ['mymiddleware:web,sanctum',
if you have routes in common, you could use both guards at the same time, and for routes that are specific to one authentication method, you can set the correct guard you need
but in the end i would check the auth middleware from laravel, i'm sure it does what you need (Illuminate\Auth\Middleware\Authenticate)
In my web.php I have a route
Route::get('summary_average_fee', 'Summary#AverageFee')->middleware('CheckParams#dateLimits');
Im trying to refrence the dateLimits function in the CheckParams class
My CheckParams class, saved as CheckParams.php in the Middleware folder
<?php
namespace App\Http\Middleware;
use Closure;
class CheckParams
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function dateLimits($request, Closure $next)
{
isEmpty($request->input('startDate'), 'NO_START_DATE');
isEmpty($request->input('endDate'), 'NO_END_DATE');
return $next($request);
}
private function isEmpty($value, $error, $status)
{
if(empty($value))
{
return response()->json($error, 422);
}
}
}
In the kernal.php file I add this to the routeMiddleware array
'CheckParams' => \App\Http\Middleware\CheckParams::class
When it runs, I get the error that Class CheckParams#dateLimits does not exist
Seems to me that your middleware should be rewritten and update the usage:
use App\Http\Middleware\CheckParams;
Route::get('summary_average_fee', 'Summary#AverageFee')->middleware(CheckParams::class);
Middleware:
namespace App\Http\Middleware;
use Closure;
class CheckParams
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$this->isEmpty($request->input('startDate'), 'NO_START_DATE');
$this->isEmpty($request->input('endDate'), 'NO_END_DATE');
return $next($request);
}
private function isEmpty($value, $error, $status)
{
if(empty($value))
{
return response()->json($error, 422);
}
}
}
I've got a middleware setup that's caching the HTML output of each public Controller request, in each of my controllers' __construct method.
public function __construct() {
$this->middleware('auth', ['except' => ['index', 'show']]);
$this->middleware('cache.get', ['only' => 'show']);
$this->middleware('cache.put', ['only' => 'show']);
}
The caching is working great, as expected, except for one thing: I've got Route-Model bindings setup in RouteServiceProvider.php for easy accessiblity of models in their respective controllers, like
public function boot(Router $router)
{
parent::boot($router);
$router->bind('posts', function($id) {
return \App\Article::findBySlugOrIdOrFail($id);
});
$router->bind('tags', function($name) {
return \App\Tag::where('name', $name)->firstOrFail();
});
$router->bind('artists', function($slug) {
return \App\Artist::findBySlugOrIdOrFail($slug);
});
Basically what's happening is even when pages are cached, I'm still getting a single query for each route where it's looking up that slug (SluggableInterface) or id. Wondering if there's a way to do this where the query doesn't occur when a route is cached? Or is that just not possible?
Thanks!
EDIT
Here's my caching middleware:
class GetCache
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$key = StringHelper::keygen($request->path());
if (Cache::has($key) && Auth::guest()) {
$content=Cache::get($key);
return response($content);
}
return $next($request);
}
}
class PutCache
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$key = StringHelper::keygen($request->path());
if (! Cache::has($key) && Auth::guest()) {
Cache::put($key, $response->getContent(), 600);
}
return $next($request);
}
}
I'm having a trouble with creating the "owner" middleware.
For example, I have a Articles and Usermodel associated with user_id key.
I want to add the "owner" middleware to the ArticlesController, so the only owner of that article can edit, update and delete it.
I've been searching for this issue for a while, but never found the code, which would work.
Some of them tried to make it work with Form Requests, but I'm interested in using Middleware.
Create middleware:
php artisan make:middleware OwnerMiddleware
namespace App\Http\Middleware;
use App\Article;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class OwnerMiddleware
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$articleId = $request->segments()[1];
$article = Article::findOrFail($articleId);
if ($article->user_id !== $this->auth->getUser()->id) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
Add it to app\Http\Kernel.php:
protected $routeMiddleware = [
'owner' => 'App\Http\Middleware\OwnerMiddleware',
];
Use middleware in your routes:
Route::group(['middleware' => ['owner']], function() {
// your route
});
Alternatively you could use route and middleware parameters, it has some advantages:
Even if the request structure changes your middleware would still work
The middleware is reusable for differents resources
You can use it inside controllers
Here’s the middleware (app/Http/Middleware/AbortIfNotOwner.php):
<?php
namespace App\Http\Middleware;
use Closure;
class AbortIfNotOwner
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string $resourceName
* #return mixed
*/
public function handle($request, Closure $next, $resourceName)
{
$resourceId = $request->route()->parameter($resourceName);
$user_id = \DB::table($resourceName)->find($resourceId)->user_id;
if ($request->user()->id != $user_id) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
Inside app\Http\Kernel.php:
protected $routeMiddleware = [
'owner' => 'App\Http\Middleware\AbortIfNotOwner',
];
Inside your route file (app/Http/routes.php):
Route::group(['middleware' => ['owner:articles']], function() {
// your route
});
And optionally call it in the controller:
public function __construct()
{
$this->middleware('owner:articles', ['only' => ['edit', 'update']]);
}