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.
Related
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);
}
With the laravel 5.3 and above the concept of filters is gone and middleware is used instead. I have migrated my project with laravel 4 to 5.4.
I want to modify the DeviceLoginController that is when I am not logged in it must refresh to the login page. Other details can be seen in the controller page.
Problem: The controller page is useless as even when I am not logged in anyone can access this page and and anyone can fill anything. I have been trying to resolve this issue from 2 days still I am no where.
DeviceLoginController page looks like this:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\BaseController;
use Auth;
use Format;
use Input;
use DB;
use Session;
use Validator;
use Hash;
use Redirect;
use User;
use App\Models\License;
use App\Models\LicenseCount;
use App\Models\Manufacturer;
use App\Models\DeviceModel as Model;
use App\Models\Device;
use App\Models\Application;
class DeviceLoginController extends BaseController {
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
public function attempt()
{
$username = Format::ltr(Input::get("username"));
$device_key = Input::get("device_key");
$imei = Format::ltr(Input::get('imei'));
$model = Format::utr(Input::get('model'));
$manufacturer = Format::utr(Input::get('manufacturer'));
$app_code = Format::ltr(Input::get('app_code'));
$user = User::where('username', $username)->first();
if(!Hash::check($device_key, $user->device_key)) {
Event::fire('auth.login.fail', array($username, Request::getClientIp(), time()));
die("1");
}
Auth::loginUsingId($user->id);
// check if device is already registered under given user for given app
$license = License::where('device_imei', $imei)->where('app_code', $app_code)->where('user_username', $username);
// if device isn't registered, first check if device is registered by different user. If not, check if licenses are available or not with the user to register new device
if(!$license->count()) {
// checking if licenses are available or not
$license_count = LicenseCount::where('user_username', $username)->where('app_code', $app_code)->first();
// if licenses are left, register the device
if((int) $license_count['left']) {
$manufacturer = Manufacturer::firstOrCreate(array('name' => $manufacturer));
$model = Model::firstOrCreate(array('name' => $model, 'manufacturer_code' => $manufacturer->code));
$device = Device::where('imei', $imei)->first();
if(!$device) {
$device = Device::firstOrCreate(array('imei' => $imei, 'model_code' => $model->code));
}
License::create(array('device_imei' => $imei, 'app_code' => $app_code, "user_username" => $username, "expiry_date" => date("Y-m-d H:i:s", strtotime("+1 year"))));
$license_count->left = Format::itr($license_count->left) - 1;
$license_count->save();
} else {
// Prints 3, if the device is not registered and user has no more licenses left for the given app
die("3");
}
// Prints 2, if the device was not previously registered and it is now registered under given user for given app
Session::put('login_response', '2');
} else {
// Prints 0, if device is already registered under given user for given app
Session::put('login_response', '0');
}
}
}
My authenticate.php file looks like this
<?php
namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate
{
/**
* The authentication factory instance.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string[] ...$guards
* #return mixed
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, $guards=null)
{
if($this->auth ->guest())
{
if($request->ajax())
{
return response('unauthorized',401);
}
else
{
return redirect()->guest('login');
}
}
//$this->authenticate($guards);
return $next($request);
}
/**
* Determine if the user is logged in to any of the given guards.
*
* #param array $guards
* #return void
*
* #throws \Illuminate\Auth\AuthenticationException
*/
protected function authenticate(array $guards)
{
if (empty($guards)) {
return $this->auth->authenticate();
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
throw new AuthenticationException('Unauthenticated.', $guards);
}
}
I am new to Laravel please forgive me if I have asked some silly question. I am clueless what to do at this point. Please help and let me know if I need to add some other file.
It's great you have done the migration to Laravel 5.4. However, I suggest you go through the documentation first or watch the Laravel 5.4 from Scratch series.
For your question, you need the put the route that calls the controller function under the 'auth' middleware. Laravel provides this middleware out of the box. You can change the route to where the user will be redirected if he is not logged and calls the route.
Please go through the documentation for this.
Suppose your route is 'admin/profile' and you have defined this in the web.php routes file, you can add a middleware to it as shown (picked this example from the DOC.)
Route::get('admin/profile', function () {
//
})->middleware('auth');
To place multiple routes under the same middleware, you can use Route groups.
I'm configuring maintenance mode in Laravel. I'm trying to add in an IP whitelist.
When I run this code:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckForMaintenanceMode
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->app->isDownForMaintenance() &&
!in_array($request->getClientIP(), ['127.0.0.1']))
{
return response('Be right back!', 503);
}
return $next($request);
}
}
I get this error:
Undefined property: App\Http\Middleware\CheckForMaintenanceMode::$app
Can someone tell me what's the problem is?
Update
As of Laravel 5.6.21, this functionality is now built into Laravel. The php artisan down command now takes --allow parameters which lets you specify the IP addresses to allow to access the site.
So, instead of making any customizations, you'd just need to run php artisan down --allow=127.0.0.1.
Original
You're using $this->app, but your class doesn't have an $app property. You can either just use the app() helper method, you can inject the Application into your middleware, or you can extend Laravel's CheckForMaintenanceMode class, which will take care of all that for you.
Extend Laravel:
class CheckForMaintenanceMode extends \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode
Dependency Injection:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Foundation\Application;
class CheckForMaintenanceMode
{
/**
* The application implementation.
*
* #var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Foundation\Application $app
* #return void
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->app->isDownForMaintenance() &&
!in_array($request->getClientIP(), ['127.0.0.1']))
{
return response('Be right back!', 503);
}
return $next($request);
}
}
app() Helper
public function handle($request, Closure $next)
{
if (app()->isDownForMaintenance() &&
!in_array($request->getClientIP(), ['127.0.0.1']))
{
return response('Be right back!', 503);
}
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
});