Laravel 8 gates not working with Auth::guard - php

Hey I am developing a project where I have roles and permissions for users and I am trying to protect my routes with the help of middleware by defining Gates but it's showing me 403| Not Authorized. I can't understand what the actual problem is?
Into Category Controller
public function addcategory(AdminsRole $adminsroles){
return view('add-category');
}
Into Routes
Route::get('/add-category', [CategoryController::class, 'addcategory'])->middleware('can:add-category')->name('addcategory');
Into AuthServiceProvider.php
$admin = Auth::guard('admin');
Gate::define('add-category', function ($admin, AdminsRole $adminsroles) {
if($admin->user()->u_type != 'superadmin'){
$adminRolescount = $adminsroles::where([
['admin_id', '=', $admin->user()->id],
['module', '=', 'categories'],
['add_access', '=', '1'],
])->count();
return $adminRolescount;
}else{
return $adminRolescount = 1;
}
});

I think what you're looking for can be simplified. The code in your controller and routes file does not need to be adjusted. I would however change your gate definition to the following:
Gate::define('add-category', function ($user = null) {
// Fetch user from custom guard
$user = Auth::guard('admin')->user();
// Check if a valid user instance was passed
if (is_null($user)) {
return false;
}
// Allow super admins to add categories regardless of AdminsRole existence
if ($user->u_type === 'superadmin') {
return true;
}
// Check if current user has a matching AdminsRole row with add_access permission
return AdminsRole::where([
['admin_id', '=', $user->id],
['module', '=', 'categories'],
['add_access', '=', '1'],
])->exists();
});
Note that a gate always receives a user instance as their first parameter if there is a logged in user, you needn't supply this yourself. Additionally, you can query for the AdminsRole existence directly via the model, using the id of the user instance that is being checked and automatically supplied to the gate.

Related

actingAs doesn't work as expected for feature testing. laravel

I actually am not able to understand why I am getting the following error.
App\Models\User::team must return a relationship instance, but "null" was returned. Was the "return" keyword used?
I am basically creating test cases for simple orders for ecommerce.
User Modal
public function team(): BelongsTo|null
{
if (!empty($this->team_id)) {
return $this->belongsTo(Team::class);
}
return null;
}
Test case
public function test_order_status_update()
{
$order = $this->create_order($this->books->id, $this->appUser->id, $this->address->id);
$response = $this->actingAs($this->user)->put('orders/' . $order->json('order.id'), [
'order_status' => 'ordered',
]);
$response->assertRedirect('orders/' . $order->json('order.id'))->assertSessionHas('success');
}
In addition, I have another feature in my application called pages access control, which controls page access for multiple users (admin, developer, and users).
I have implemented this feature manually using middleware.
Middlware.php
public function handle(Request $request, Closure $next)
{
//teams 1-Developer 2-Admin 3-Management 4-Marketing 5-Audit 6-Sales 7-Bookstores 8-Delivery 9-User
$team = $request->user()->team;
if ($team->id == 1 || $team->id == 2) {
return $next($request);
}
$pages = auth()->user()->pages->merge(auth()->user()->team->pages);
$currentRouteName = $request->route()->getName();
$pages->contains('route_name', $currentRouteName) ?: abort(403);
return $next($request);
}
Based on the error above, I believe the actingAs function is unable to obtain authenticated user information, which is why my test failed.
How can I fix this?
Simply don't check your team_id:
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
Laravel tries to be smart. If team_id isn't set, it will just return null. However, if you don't return the BelongsTo, the magic code of Laravel will trip when you try to access user->team

Laravel 7.x Only the profile's Owner and some roles may enter or do action

I'm new at Laravel and I don't quite sure how to get User's session so he can see his own profile but can't be seen by other users except for the Admin role.
this is what I thought
profile/ProfileControllers.php
public function show(User $user)
{
if(Gate::allows('manageUsers')){
return view('Profile.Users.profile')->with([
'user' =>$user,
]);
}
if ($user->id === $user->id) {
return view('Profile.Users.profile')->with([
'user' =>$user,
]);
}
return redirect(route('home'));
}
I thought using $user->id == $user->id means the session user id = this id http://127.0.0.1:8000/profile/users/{id} but it is not and i can see the other user's profiles using a single user.
manageUsers inside the gate is the user admins like (cashier, sales, etc)
I reckon some other methods are using middleware in the web route. But I failed to understand what's to put on my middleware
routes/web.php
Route::namespace('Profile')->prefix('profile')->name('profile.')->middleware('?')->group(function(){
Route::resource('/users', 'ProfilesController', ['except' =>['store', 'create']]);
});
what I'm trying to approach is,
Only the owner of the profile and some roles can see the profile. and the guest will be redirected to home.
maybe you want something like this?
public function show(User $user)
{
if(Gate::allows('manageUsers')){
return view('Profile.Users.profile')->with([
'user' =>$user,
]);
}
if (auth()->user()->id === $user->id) {
return view('Profile.Users.profile')->with([
'user' =>$user,
]);
}
return redirect(route('home'));
}
Try $user->id == auth()->id()
You can create your own middleware and put your login theri.
php artisan make:middleware IsAdmin
Then register this to Kernel.php file
Inside middleware
If(auth()->user()->type == 'Admin'){
//Ruj your script inside if block , and instead of attribute TYPE
//you must put what you have used in databse to check if user
//is admin or not.
}

hide confidential data for multiple users

I have an user named eric which has like user role Former.
I would like the user Eric sees only his confidential information but not those of others.
Would you have any idea how I could make this?
Thank you for your help.
Edit Controller Former
public function index(Request $req)
{
if ($req->search == "") {
$formers = Former::paginate(5);
$formerIdsDown = Cours::where('date_seance', "<=" , Carbon::now())->pluck('fk_former')->toArray();
return view('admin.formers.index', compact('formers', 'formerIdsDown'));
} else {
$validated = $req->validate([
'search' => 'alpha',
]);
$formers = Former::where('nom', 'LIKE', '%' . $validated['search'] . '%')->paginate(5);
$formers->appends($req->only('search'));
$bikeIdsDown = Cours::where('date_seance', "<=" , Carbon::now())->pluck('fk_former')->toArray();
return view('admin.formers.index', compact('formers', 'formerIdsDown'));
}
$user = User::when($has_role, function ($query) {
return $query->where('id', auth()->id());
})
->get();
}
You could handle this in two ways.
Limiting the data from the server
This should be your approach. Here you could add a condition in your query to know if the user has the restricted role in order to limit the data:
# InSomeController.php
$has_role = auth()->user()->hasRole('former'); // checking if the user has the role
$user = User
::when($has_role, function ($query) {
return $query->where('a_column', 'a_value'); // The limitation you want to apply
})
->get(); // executing the query
Limiting in the view
In case you are using blade templating engine.. you can make use of some Blade directives:
# in_some_view.blade.php
#if (auth()->user()->hasRole('former'))
/** Your limited data goes here.. */
#else
/** The default data goes here.. */
#endif
Obs: In both cases I used the hasRole('role') method, replace this with the logic to validate if the user has a given role.

How to get computed variable from any model and controller in laravel?

I have 2 models: User and Account.
Account is an company account, it has a slug field. Slug is needed to determine which company the user is accessing. In example, route /account/*company_slug*/deals means that user trying to get an array of deals of company_slug company.
Each entity associated with a company has a field account_id. That's why I need to get an of the current account. Where should I do this and how?
In example, I get the middleware CheckIfAccountAcceptedForUser with the following code:
public function handle($request, Closure $next)
{
$account = Account::find($request->route()->parameter('account'));
abort_if(empty($account), 404) ;
abort_if(DB::table('account_user')
->where(function (Builder $query) use ($account) {
$query->where('account_slug', '=', $account->slug);
$query->where('user_id', '=', Auth::id());
})
->get()
->isEmpty(), 403);
return $next($request);
}
How to set the account_id globally for my application, if route is like /account/*account*/...?
Maybe this is not what you are looking for.
When you check account in middleware you make a query, after that you should make a new query in controller to take account data. In your situation you make 3 queries instead of 1.
You can check it in your account controller by one query.
Route:
Route::get('/account/{slug}/deals', 'AccountsController#deals');
Accounts controller:
public function deals($slug){
$account = Account::where('id', Auth::user()->account_id)
->where('slug', $slug)
->firstOrFail();
$deals = Deal::where('account_id', $account->id)->get();
return view('account.deals', compact('account', 'deals'));
}
It will always response 404 error if account with current slug doesn't exist or user can't access this account. Do you really need 403 response?
public function handle($request, Closure $next)
{
$account = Account::find($request->route()->parameter('account'));
abort_if(empty($account), 404) ;
abort_if(DB::table('account_user')
->where(function (Builder $query) use ($account) {
$query->where('account_slug', '=', $account->slug);
$query->where('user_id', '=', Auth::id());
})
->get()
->isEmpty(), 403);
//For global use in your all view file
View::share ( 'account_id', $account->id);
// to access account_id in controller
$request->request->add(['account_id' => $account->id]);
return $next($request);
}
In Controller
$account_id = request('account_id');

how to pass variable to routes in laravel?

So i have a variable in my routes.php and i want to pass it to the route::get, this is my code for more details :
$user = User::find(Auth::user()->username);
Route::get('/services/here i want to put my variable', 'ServiceController#show');
so please if someone has any idea i will be very appreciative
The problem with variating your routes with something like:
$user = User::find(Auth::user()->username);
Route::get("/services/$user->username", 'ServiceController#show');
Is that you may enconter some problems in cases where the user does't exists. If you do that and go to your command line and execute:
php artisan routes
There will be no logged user so that route will be pointed to /services/. What you have to do to prevent those cases is to create a filter and process your route on it:
The route:
Route::get("/services/{username}", array('before' => 'user-must-be-logged-in', 'uses' => 'ServiceController#show'));
A filter to do some checks on it:
Route::filter('user-must-be-logged-in', function()
{
if ( ! Auth::check())
{
App::abort(404); /// user is not logged in, this route does not exist
}
if ( ! User::where('username', $username)->first() )
{
App::abort(404); /// user does not exists, this route does not exist
}
if (User::where('username', $username)->first()->id !== Auth::user()->id)
{
App::abort(404); /// selected is not the current logged user, this route does not exist
}
});
A controller doing whatever you need with it:
class ServiceController extends Controller {
public function show()
{
$user = User::where('username', $username)->first();
// now do whatever you need with your user;
}
}
assuming its $user you want to put in there
$user = User::find(Auth::user()->username);
Route::get("/services/$user", 'ServiceController#show');
swap single quotes for doubles and pass in your var (either that or drop out of your single quotes concat in with . your var

Categories