I have multiple admin system: one is super admin and the other is normal admin, distinguished by is_admin attribute in users table.
And these two middlewares:
SuperAdminMiddleware.php
public function handle($request, Closure $next, $guard = null)
{
if(Auth::check())
{
if($request->user()->is_admin==1)
{
return $next($request);
}
return redirect('/login');
}
else
{
return redirect('/login');
}
}
and, NormalAdminMiddleware.php
public function handle($request, Closure $next, $guard = null)
{
if(Auth::check())
{
if($request->user()->is_admin==2)
{
return $next($request);
}
return redirect('/login');
}
else
{
return redirect('/login');
}
}
and in loginController:
protected function authenticated()
{
if (auth()->user()->is_admin==1) {
return redirect('/super-admin');
}
else if(auth()->user()->is_admin==2){
return redirect('/normal-admin');
}
else {
return redirect('/home');
}
}
Now, Delete and Read should be designed in such a way that super admin can delete and see all users details, while normal admin can only see their city's user.
id name city is_admin
1 Non Maety 1
3 Pom Lorey 2
4 Rom Lorey 0
2 Yer Easter 0
Non should be able to see all. while Pom should see only id 3 and 4.
If i put show and delete routes under SuperAdminMiddleware, Normal Admin couldnot see their city's records.
Route::group(['middleware' => ['App\Http\Middleware\SuperAdminMiddleware']], function () {
Route::get('/show/{id}', 'MyController#show');
Route::post('/delete', 'MyController#delete');
});
And if i put these routes under both SuperAdminMiddleware and NormalAddminMiddleware. NormalAdminMiddleware can also see other city's records.
Route::group(['middleware' => ['App\Http\Middleware\NormalAdminMiddleware']], function () {
Route::get('/show/{id}', 'MyController#show');
Route::post('/delete', 'MyController#delete');
});
How do i overcome this situation?
You can solve it with a policy:
class UserPolicy
{
/**
* Determine if the given user can be viewed by the user.
*
* #param \App\User $user
* #param \App\User $account
* #return bool
*/
public function view(User $user, User $account)
{
switch($user->is_admin) {
case 1:
return true;
case 2:
return $user->city == $account->city;
default:
return 0;
}
}
/**
* Determine if the given user can be updated by the user.
*
* #param \App\User $user
* #param \App\User $account
* #return bool
*/
public function update(User $user, User $account)
{
switch($user->is_admin) {
case 1:
return true;
case 2:
return $user->city == $account->city;
default:
return 0;
}
}
}
User would be the authenticated user model, account would be the user model that should be viewed.
After you registered your policy (https://laravel.com/docs/5.4/authorization#registering-policies) you can call it in the function of your controller like:
public function show(User $user) {
$this->can('view', $user);
}
I don't understand your purpose clearly by reading the question. If you put your methods under SuperAdminMiddleware, normal admin should be rejected and redirect to '/login'. If you want to control the operation of admin, I think middleware can't solve the problem. As the previous answer, when operate the data of database, judge whether he is superAdmin or not.
Related
I am working on a project in which I have three type of users Admin and user1 and user2. I want user1 and user2 to able to use certain features in application only if the admin has assigned an invoice to them. I have tried using helper function given below.
$invoice = Invoice::pluck('user_id')->toArray();
if (Auth::user()->admin == 1 || in_array(Auth::user()->id, $invoice)) {
return 1;
} else {
return 0;
}
but this does not work fine. I'll have to place it before every method of a controller in order to restrains users to use that feature. Is there any thing else I can do?
Any Better Approach for this?
You can use middlewares.
Create your middleware with
php artisan make:middleware UserWithInvoiceMiddleware
Then open your file in app/Http/Middleware/UserWithInvoiceMiddleware.php, and add this to the handle method:
public function handle($request, Closure $next, ...$guards)
{
$user = auth()->user();
$invoice = Invoice::pluck('user_id')->toArray();
if ($user->admin || in_array($user->id, $invoice)) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
Also, you can create a relation in your user model with the Invoice model:
public function invoice()
{
return $this->hasOne(Invoice::class);
}
Then, you can simplify your middleware using this relation:
public function handle($request, Closure $next, ...$guards)
{
if (auth()->user()->admin || auth()->user()->has('invoice')) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
You have to register your middleware in app/Http/Kernel.php, under the $routeMiddleware array:
protected $routeMiddleware = [
...
'user-with-invoice' => App\Http\Middleware\UserWithInvoiceMiddleware::class,
];
Then, you can protect your routes with this middleware, adding a ->middleware('user-with-invoice') to the routes where the user has to be an admin or have an invoice:
Route::get('/example', ExampleController::class)->middleware('user-with-invoice');
you can use make a middleware and pass requests throw it to check if the user is authorized to do that or not.
class SomeMidllewareName
{
/**
* Handle an incoming request.
*
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$invoice = Invoice::pluck('user_id')->toArray();
if (1 == Auth::user()->admin || in_array(Auth::user()->id, $invoice)) {
return $next($request);
}
return \response()->json(['message' => 'you are not authorized'], 401);
}
}
then, you can validate on the routes and you can use also policies and validate every feature alone
I want to check user type when they log in and apply function
In my middleware, I have this code
public function handle($request, Closure $next)
{
$user = Auth::user();
if ($user->isBasic()) {
$previous_session = $user->session_id;
if ($previous_session) {
\Session::getHandler()->destroy($previous_session);
}
Auth::user()->session_id = \Session::getId();
Auth::user()->save();
return redirect(route('home'));
}
return $next($request);
}
In my User model, I have this
public function isBasic()
{
return $this->role=='basic';
}
I have already registered the middleware in Kernel
'basic' => \App\Http\Middleware\CheckSession::class,
And I passed it to my LoginController like this
public function __construct()
{
$this->middleware('basic');
}
But when it tried to visit the login controller, it says
Call to a member function isBasic() on null
I am a beginner in Laravel and I don't know what to do
This seems like a good candidate for the authenticated method of the LoginController which gets called after a user has logged in.
use Illuminate\Http\Request;
/**
* The user has been authenticated.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function authenticated(Request $request, $user)
{
if ($user->isBasic()) {
$previous_session = $user->session_id;
if ($previous_session) {
$request->session()->getHandler()->destroy($previous_session);
}
$user->session_id = $requset->session()->getId();
$user->save();
// do these specific users need to be redirected to somewhere special?
return redirect()->route('home');
}
}
You could even listen for the Auth events if you wanted to, as long as its not a queued listener.
you should use Auth::check() to determine if user is authenticated or not
in your middleware, you are doing
$user = Auth::user();
if ($user->isBasic()) {...}
In this part, if the user is not authenticated then the $user will have null value
you will have to check if the user is authenticated or not.
Eg:
public function handle($request, Closure $next)
{
if(Auth::check()) { //check if the user is logged in or not
$user = Auth::user();
if ($user->isBasic()) {
$previous_session = $user->session_id;
if ($previous_session) {
\Session::getHandler()->destroy($previous_session);
}
Auth::user()->session_id = \Session::getId();
Auth::user()->save();
return redirect(route('home'));
}
}
return $next($request);
}
i have problem with laravel cus im begginer but i work with php languge very well
and my Question:
I created a table for users in my database and create column for type
There are 3 user types in my table:
customers - Workers - Factories
How can i use middlewarre or anything else Prevent access to other pages
public function Signupuser(Request $request){
$email=$request['email'];
$username=$request['username'];
$tell=$request['mobilenumber'];
$pass=bcrypt($request['password']);
$status_reg=$request['status_register'];
$usertable=new UserTable();
$usertable->username=$username;
$usertable->email=$email;
$usertable->Password=$pass;
$usertable->Tell=$tell;
$usertable->StatusReg=$status_reg;
$usertable->save();
Auth::login($usertable);
if($status_reg=='factory'){
return redirect()->route('FactoryDashboard');
}
if($status_reg=='worker'){
return redirect()->route('WorkerDashboard');
}
if($status_reg=='customer'){
return redirect()->route('CustomerDashboard');
}
}
public function signinuser(Request $request){
$email=$request['email'];
$pass=$request['pass'];
if (Auth::attempt(['email'=>$email,'password'=>$pass])){
$status = Auth::user()->StatusReg;
return $status;
}
else{
return "nokey";
}
}
i used with one middleware but this middleware dosent work
<?php
namespace App\Http\Middleware;
use App\UserTable;
use Closure;
class WorkerMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($request->user() && $request->user()->StatusReg !='worker'){
return redirect('homepage');
}
return $next($request);
}
}
please help guys
Your scenario is usually dealt with by using the Authorization service of Laravel.
For example, you could add the following to your app\Providers\AuthServiceProvider.php file:
Gate::define('factory', function ($user) {
return $user->StatusReg == 'factory';
});
Gate::define('worker', function ($user) {
return $user->StatusReg == 'worker';
});
Gate::define('customer', function ($user) {
return $user->StatusReg == 'customer';
});
And then you can use it in your application like the following:
if (Gate::allows('worker')) {
//...
}
if (Gate::denies('customer')) {
//...
}
There are plenty more usage examples in the docs:
https://laravel.com/docs/5.6/authorization
https://scotch.io/tutorials/user-authorization-in-laravel-54-with-spatie-laravel-permission.
I have followed the above link for my reference to set roles and permissions to users.
I am getting the error:
(1/1) FatalErrorException
Call to a member function hasPermissionTo() on null
in ClearanceMiddleware.php line 17
My db tables are
This is my db table
Cleareance middleware code is:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class ClearanceMiddleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
if (Auth::user()->hasPermissionTo('Administer roles & permissions')) //If user has this //permission
{
return $next($request);
}
if ($request->is('posts/create'))//If user is creating a post
{
if (!Auth::user()->hasPermissionTo('Create Post'))
{
abort('401');
}
else {
return $next($request);
}
}
if ($request->is('posts/*/edit')) //If user is editing a post
{
if (!Auth::user()->hasPermissionTo('Edit Post')) {
abort('401');
} else {
return $next($request);
}
}
if ($request->isMethod('Delete')) //If user is deleting a post
{
if (!Auth::user()->hasPermissionTo('Delete Post')) {
abort('401');
}
else
{
return $next($request);
}
}
return $next($request);
}
}
Please clear this.
It seems you are not logged in to your app. So user is null, and calling a member function on null gives the error.
You can use:
$user = Auth::user();
if(isset($user)){
// Check for permission
}
Beyond that you must define roles , and assign these roles to your user
before checking for permissions. ( I assume you have done these. )
Log in with a user with permissions, then try again.
I'm writing tests for a Laravel application. In my AuthServiceProvider->boot(), I define a number of user abilities with $gate->define() based on a permissions table in my database.
Basically this:
foreach ($this->getPermissions() as $permission) {
$gate->define($permission->name, function ($user) use ($permission) {
return $user->hasPermission($permission->name);
});
}
In my tests I'm creating permissions on the fly, but the AuthServiceProvider has already booted up, which means I can't verify user permissions with #can, Gate, etc.
Is there a proper way to deal with this issue?
I know I'm a bit late for the party on this one, but still - I just had the same problem myself and hence this question doesn't have a comprehensive answer, here is my solution for the same issue (in Laravel 5.3):
I've got this in my app\Providers\AuthServiceProvider:
/**
* Register any authentication / authorization services.
*
* #param Gate $gate
*/
public function boot(Gate $gate)
{
$this->registerPolicies();
if (!app()->runningInConsole()) {
$this->definePermissions($gate);
}
}
/**
* #param Gate $gate
*/
private function definePermissions(Gate $gate)
{
$permissions = Permission::with('roles')->get();
foreach($permissions as $permission) {
$gate->define($permission->key, function($user) use ($permission) {
return $user->hasRole($permission->roles);
});
}
}
This takes care of the normal application flow when not testing and disables the premature policy registration when testing.
In my tests/TestCase.php file I have the following methods defined (note that Gate points to Illuminate\Contracts\Auth\Access\Gate):
/**
* Logs a user in with specified permission(s).
*
* #param $permissions
* #return mixed|null
*/
public function loginWithPermission($permissions)
{
$user = $this->userWithPermissions($permissions);
$this->definePermissions();
$this->actingAs($user);
return $user;
}
/**
* Create user with permissions.
*
* #param $permissions
* #param null $user
* #return mixed|null
*/
private function userWithPermissions($permissions, $user = null)
{
if(is_string($permissions)) {
$permission = factory(Permission::class)->create(['key'=>$permissions, 'label'=>ucwords(str_replace('_', ' ', $permissions))]);
if (!$user) {
$role = factory(Role::class)->create(['key'=>'role', 'label'=>'Site Role']);
$user = factory(User::class)->create();
$user->assignRole($role);
} else {
$role = $user->roles->first();
}
$role->givePermissionTo($permission);
} else {
foreach($permissions as $permission) {
$user = $this->userWithPermissions($permission, $user);
}
}
return $user;
}
/**
* Registers defined permissions.
*/
private function definePermissions()
{
$gate = $this->app->make(Gate::class);
$permissions = Permission::with('roles')->get();
foreach($permissions as $permission) {
$gate->define($permission->key, function($user) use ($permission) {
return $user->hasRole($permission->roles);
});
}
}
This enables me to use this in tests in multiple ways. Consider the use cases in my tests/integration/PermissionsTest.php file:
/** #test */
public function resource_is_only_visible_for_those_with_view_permission()
{
$this->loginWithPermission('view_users');
$this->visit(route('dashboard'))->seeLink('Users', route('users.index'));
$this->visit(route('users.index'))->assertResponseOk();
$this->actingAs(factory(User::class)->create());
$this->visit(route('dashboard'))->dontSeeLink('Users', route('users.index'));
$this->get(route('users.index'))->assertResponseStatus(403);
}
/** #test */
public function resource_action_is_only_visible_for_those_with_relevant_permissions()
{
$this->loginWithPermission(['view_users', 'edit_users']);
$this->visit(route('users.index'))->seeLink('Edit', route('users.edit', User::first()->id));
$this->loginWithPermission('view_users');
$this->visit(route('users.index'))->dontSeeLink('Edit', route('users.edit', User::first()->id));
}
This works just fine in all my tests. I hope it helps.
public function boot(GateContract $gate)
{
parent::registerPolicies($gate);
$gate->before(function($user, $ability) use ($gate){
return $user->hasPermission($ability);
});
}
I haven't extensively tested this, but it seems to work from my quick tests.
I'm not sure what the "proper" way (if there is one) to define a gate for testing. I couldn't find an answer for this after looking at the documentation and searching, but this seems to work in a pinch in Laravel 5.7:
Defining a gate in a model factory state:
$factory->state(App\User::class, 'employee', function () {
Gate::define('employee', function ($user) {
return true;
});
return [];
});
This test function will have both the 'employee' and the 'admin' gate applied since we are using the 'employee' state when creating the user:
/** #test */
public function an_admin_user_can_view_the_admin_page()
{
$user = factory('App\User')->state('employee')->make();
$this->actingAs($user);
Gate::define('admin', function ($user) {
return true;
});
$this->get('/admin')
->assertOk();
}
I know this is a really old question, but it was the top result in a search and hopefully can help someone.
Don't forget to use the Gate facade:
use Illuminate\Support\Facades\Gate;
You could do something like this inside AuthServiceProvider
First import the necessary packages
use Illuminate\Auth\Access\Gate;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
and then add this boot() method
public function boot(GateContract $gate)
{
parent::registerPolicies($gate);
$gate->define('update-post', function ($user, $post, $isModerator) {
// check if user id equals post user id or whatever
if ($user->id === $post->user->id) {
return true;
}
// you can define multiple ifs
if ($user->id === $category->user_id) {
return true;
}
if ($isModerator) {
return true;
}
return false;
});
// you can also define multiple gates
$gate->define('update-sub', function($user, $subreddit) {
if($user->id === $subreddit->user->id) {
return true;
}
return false;
});
And then in your controller you could do something like this
if (Gate::denies('update-post', [$post, $isModerator])) {
// do something
}