I am using Entrust middleware from here. Everything goes fine except when I want to expose a certain page to admin when logged in and to any user who is NOT logged in .
With the help from here , I added the following middleware, but when I hit the url , it says, too many redirections.
namespace App\Http\Middleware;
use App\Models\User;
use App\Models\Role;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Eloquent\Collection;
class CheckPermission {
/**
* 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 )
{
if ( $this->auth->guest() )
{
$user = new User;
$user->id = 0;
$user->username = 'Guest';
$role = Role::find(9);// EXPLANATION OF 9 IS GIVEN BELOW UNDER CODE
$user->roles = new Collection;
$user->roles->add( $role );
}
else
{
$user = $this->auth->user();
}
// Automatically check permission based on route name
/*
if ( !$user->can( $request->route()->getName() ) )
{
// Handle denied permission, e.g. abort(401)
}
*/
return $next( $request );
}
}
Database change : in roles table I added a row with id 9 and name guest.
How can I add a guest support in Entrust so that any user who is not logged-in will be considered as a guest and he will be allowed to visit certain routes as well.
I'd personally avoid any global middleware dealing with authorization as to not block your application from having publicly accessible pages. Use route groups to assign middleware to protected routes.
While it may not fit into Entrust's design, you could also write a custom middleware to only allow guests and admins. Something like this:
class AdminOrGuestMiddleware {
/**
* 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 ($request->user() && !$request->user()->hasRole('admin')) {
return redirect('/home');
}
return $next($request);
}
Related
I want to implement Impersonate functionality into Laravel-8 without using any package.
Only super-admin can use this functionality.
I used laravel sanctum to authenticate.
to access impersonate functionality user should be super-admin. (is_admin(boolean) flag is set into users table).
Here is my middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ImpersonateUser
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$impersonateId = $request->cookie('x-impersonate-id');
if($request->user()->is_admin && $impersonateId) {
$user = User::findOrFail($impersonateId);
if($user->is_admin) {
return response()->json(["message" => trans("You cannot impersonate an admin account.")], 400);
}
Auth::setUser($user);
}
return $next($request);
}
}
My route file:
// Impersonate routes.
Route::middleware(['auth:sanctum', 'impersonate'])->group(function () {
// checklist routes
Route::get('checklists', [ChecklistController::class, "index"]);
});
Whether use Auth::setUser($user) is safe or I have to use Auth::onceUsingId($userId); ?
Auth::onceUsingId($userId); not working with auth::sanctum middleware. So Auth::setUser($user) is safe or not?
I used laravel to develop backend API only.(SPA)
They should be the same in terms of safety. OnceUsingId() calls setUser() in the background.
From the Illuminate\Auth\SessionGuard class
/**
* Log the given user ID into the application without sessions or cookies.
*
* #param mixed $id
* #return \Illuminate\Contracts\Auth\Authenticatable|false
*/
public function onceUsingId($id)
{
if (! is_null($user = $this->provider->retrieveById($id))) {
$this->setUser($user);
return $user;
}
return false;
}
/**
* Set the current user.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #return $this
*/
public function setUser(AuthenticatableContract $user)
{
$this->user = $user;
$this->loggedOut = false;
$this->fireAuthenticatedEvent($user);
return $this;
}
Both of these methods come from the SessionGuard though. I don't know if Sanctum implements its own version.
I am specifically trying to get Sanctum's Guard class to look for the API token in a JSON request body if it can't find it in the Authorization header. I simply need to add an elseif after it checks for the bearer token.
So question is: What is the best way to override this method (or class) with my own, without touching the original Sanctum files?
<?php
namespace Laravel\Sanctum;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Http\Request;
class Guard
{
/**
* The authentication factory implementation.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* The number of minutes tokens should be allowed to remain valid.
*
* #var int
*/
protected $expiration;
/**
* Create a new guard instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #param int $expiration
* #return void
*/
public function __construct(AuthFactory $auth, $expiration = null)
{
$this->auth = $auth;
$this->expiration = $expiration;
}
/**
* Retrieve the authenticated user for the incoming request.
*
* #param \Illuminate\Http\Request $request
* #return mixed
*/
public function __invoke(Request $request)
{
if ($user = $this->auth->guard('web')->user()) {
return $this->supportsTokens($user)
? $user->withAccessToken(new TransientToken)
: $user;
}
if ($token = $request->bearerToken()) {
$model = Sanctum::$personalAccessTokenModel;
$accessToken = $model::where('token', hash('sha256', $token))->first();
if (! $accessToken ||
($this->expiration &&
$accessToken->created_at->lte(now()->subMinutes($this->expiration)))) {
return;
}
return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken(
tap($accessToken->forceFill(['last_used_at' => now()]))->save()
) : null;
}
}
/**
* Determine if the tokenable model supports API tokens.
*
* #param mixed $tokenable
* #return bool
*/
protected function supportsTokens($tokenable = null)
{
return in_array(HasApiTokens::class, class_uses_recursive(
$tokenable ? get_class($tokenable) : null
));
}
}
I don't know if you've already figured out but I think you need to add an entry in your AppServiceProvider boot method and override configureGuard functionality placed in SanctumServiceProvider at line 94.
app/Providers/AppServiceProvider.php
Auth::resolved(function ($auth) {
$auth->extend('sanctum', function ($app, $name, array $config) use ($auth) {
return tap($this->createGuard($auth, $config), function ($guard) {
$this->app->refresh('request', $guard, 'setRequest');
});
});
});
You will also need to override createGuard function to specify your custom Guard class with the functionality you require.
I am using Laravel and sentinel to develop a permission system however it was designed so that the user can select and deselect which permissions the role has from a checkbox form. I have already coded the part where they can assign permissions however I need that the checkboxes that have already been assigned are marked when the user request the page. How do you recommend approaching this? I am using a middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Cartalyst\Sentinel\Laravel\Facades\Sentinel;
class PermissionsMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Sentinel::findById(1);
$permisos = array(array_keys($user['permissions']))
return $next($request);
}
}
However, I don't know how to pass data from the middleware to the view.
I don't think it's recommended using the middleware for this purpose, but if you still want to do it that way you can try using:
View::share ( 'permisos', $permisos );
To share the 'permisos' variable with the view that's coming after the middleware.
So your code is going to look like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Cartalyst\Sentinel\Laravel\Facades\Sentinel;
class PermissionsMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Sentinel::findById(1);
$permisos = array(array_keys($user['permissions']))
View::share ( 'permisos', $permisos );
return $next($request);
}
}
I'm new to laravel 5.1.
How can I use middleware parameter to protect my admin routes from users ?
something like this:
Route::group(['middleware' => 'auth:admin'], function()
/* Admin only Routes*/
{
//////
});
I have a field "role" in my "users" table that get two values:
1 for admin
2 for users
In my application, users, have their protected route.
I don't want to use packages.
You can do something like this. Inject the Guard class, then use it to check the user. You dont need to pass the parameter really. Just name your middleware 'admin' or something. The following middleware will check if the current user's role is admin, and if not, redirect to another route. You can do whatever you prefer on failure.
<?php
namespace Portal\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Admin
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
*/
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)
{
if($this->auth->user()->role != 'admin') {
return redirect()->route('not-an-admin');
}
return $next($request);
}
}
In case you do want to pass the parameter, you can do this:
public function handle($request, Closure $next, $role)
{
if($this->auth->user()->role != $role) {
return redirect()->route('roles-dont-match');
}
return $next($request);
}
I am wondering if there is a better way of handling how I have my authenticated users in regards of different types of users.
I have a basic user and an administrator user. The basic user obviously will only have access to basic pages and the administrator user needs to have access to other pages that the basic user cannot have access to.
What I have been doing is: I created a super_user column in my table and adding:
if(Auth::user()->super_user == 0) {
return Redirect::to('/')->with('error', 'You do not have permission to access this page');
}
to each page that I do not want a basic user to be able to access. Now, this worked but I am starting to transition my apps to Laravel 5 and I am thinking there is a different way I can handle this.
Best way to handle user roles in your case is to use Middleware.
Create middleware:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Admin
{
/**
* 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)
{
if (!$this->auth->getUser()->super_user) {
return redirect->to('/')
->with('error', 'You do not have permission to access this page');
}
}
return $next($request);
}
}
Add it to app\Http\Kernel.php:
protected $routeMiddleware = [
'admin' => 'App\Http\Middleware\Admin',
];
Use middleware in your routes:
Route::group(['middleware' => ['admin']], function() {
// your route
});