Laravel enable route for only the current user - php

I have two user types admin and member. Now, members cannot view other profiles except their own. Only admins have access to all user profiles.
Now my route for the UsersController#show (only accessible by admins) is http://laravel.test/user/username
However if a user accesses that url with his own username, it should grant the request.
This applies to all other functions such as edit and update.
Now I could create another controller designated for the current user only, call it MyController. I could copy the code from the UsersController, just changing a few codes so that it gets the current user from auth(). But I would prefer not to.
Any help?

if you have your own conditions you should a little change this but logic is this
public function show($username)
{
$user = User::where('username',$username)->get();
if($user->id == Auth::id()){
// show profile
}
}

Related

Letting user access webpage if he has a certain role

I am making a small webpage with Laravel that has User and Admin roles.
My 'users' table consists of name, email, password and role value (0 for user, 1 for admin).
I dont need anything fancy so can I just make it so every time a normal user loads in a page that's meant for the administrator - he gets redirected?
To be more precise: How can I make it so whenever a new page loads, the users role gets checked and then my if or can statement checks if the users allowed to access the page?
For example I have a view:
results that displays all the match results(can be accessed by everyone) and I also have an admin/RESULTSadmin view, that should only be accessed by a user that has an admin role but the rest needs to get redirected. Thank you in advance!
You can make parent controller with method which should check what you need and extend your controller from that (kind of old way but most simplest)
Another option make some middleware to check access
Also please take a look on laravel auth mechanism : https://laravel.com/docs/9.x/authentication#password-confirmation-protecting-routes

How can we add multiple checks in a middleware to perform certain check in Laravel

I recently started learning Laravel and I was developing a CRUD blog where the users are divided into 3 roles. The Owner, The Admin and The Author. So I wanted Owner to be the top most auth that can make or remove users from the post of "Admin".
The second auth should be the Admin who can moderate, confirm or delete posts from the blog and can do almost everything an Owner can do but cannot ban or demote the Owner.
So i created a Users page where list of all registered users get displayed say: localhost:8000/users
Now I want only Owners and Admins to access this page and not the Authors. So to achieve this, I created a two functions in the User model just to check if user is an Owner (isOwner()) and if user is an admin (isAdmin()) and then made a middleware. Now in middleware, I want to add the rule that if user is Owner or Admin, let them access the users page, else return them back to home page and flash an error message.
The Code of my middleware is below
public function handle ($request, Closure $next) {
if (!auth()->user()->isOwner() || !auth()->user()->isAdmin()) {
session()->flash('error', 'You are not authenticated to perform this action');
return redirect('home');
}
return $next($request);
}
But this is not acting as I want it. This is just now allowing anyone to access the users page. When I remove the OR (||) from the if statement, then it works fine but just checks for either Admin or Owner. How can I make it work for both? Am I doing something wrong? I tried many different ways but none is working for me
Just change the condition:
public function handle ($request, Closure $next) {
if (auth()->user()->isOwner() || auth()->user()->isAdmin()) {
return $next($request);
} else {
session()->flash('error', 'You are not authenticated to perform this action');
return redirect('home');
}
}
But we use policies and gates for the same like a blog user can delete there blog and admin can delete that blog, we can use middleware as well, if you want to do for specific module or tables then use polices and gates, With the help for polices you can manage on all the CRUD operation with different different conditions.

How to route multi-level user listing and creation actions with three levels of user access.

Here is the requirements. I will have three levels of access to a dashboard that is restricted from the public (System Admin, Manager, Employee).
System admin, to simply put is essentially super admin. They have the ability to index,show,create,update,delete all companies and users.
Managers are created by system admin and assigned to a company, they have the ability to index,show,create,update,delete employees only for the company they belong to.
Employees have a read only access to their companies information and files.
Here is my current routes for the users. (using Slim Framework)
System Admin only access protected by middleware:
These actions are for the system admin to manage all users.
/*** USERS ***/
// View to List Users
$this->get('/users', 'App\Controller\User\UserController:index')->setName('user.index');
// View containing user registration form
$this->get('/users/new', 'App\Controller\User\UserController:create')->setName('user.create');
// Creates new user from registration form
$this->post('/users' , 'App\Controller\User\UserController:store');
// Show single user view
$this->get('/users/{id}', 'App\Controller\User\UserController:show')->setName('user.show');
// View containing edit user form
$this->get('/users/{id}/edit', 'App\Controller\User\UserController:edit')->setName('user.edit');
// Updates new user from edit user form
$this->put('/users/{id}', 'App\Controller\User\UserController:update');
// Deletes new user from edit user form
$this->delete('/users/{id}', 'App\Controller\User\UserController:delete');
System Admin & Manager only access protected by middleware:
These interactions are for the manager to manage employees
/*** COMPANIES Employees ***/
// View to List Companies employees
$this->get('/companies/{id}/users', 'App\Controller\Company\CompanyUsersController:index')->setName('company.user.index');
// View containing new employee registration form
$this->get('/companies/{id}/users/new', 'App\Controller\Company\CompanyUsersController:create')->setName('company.user.create');
// Creates new employee from registration form
$this->post('/companies/{id}/users' , 'App\Controller\Company\CompanyUsersController:store');
// Show single user
$this->get('/companies/{id}/users/{id}', 'App\Controller\Company\CompanyUsersController:show')->setName('company.user.show');
// View containing edit company employee form
$this->get('/companies/{id}/users/{id}/edit', 'App\Controller\Company\CompanyUsersController:edit')->setName('company.user.edit');
// Updates user from edit user form
$this->put('/companies/{id}/users/{id}', 'App\Controller\Company\CompanyUsersController:update');
// Deletes new user from edit user form
$this->delete('/companies/{id}/users/{id}', 'App\Controller\Company\CompanyUsersController:delete');
You will notice that for system admin, company id is not required. However, for store manager it is. So these interactions will have separate queries. My question is more about best practice. This solution seems a little redundent to me but it effectively separates concerns as well as simplifies the methods on each controller as well as the middlewares needed for access control. Am I going about this completely wrong?
I would have the same routes for all roles as the URL represents the canonical way to view that information.
e.g.
$this->get('/companies/{id}/users', 'App\Controller\Company\CompanyUsersController:index')->setName('company.user.index');
is the correct URL for the list of users for a given company.
I understand that the store manager can only access users for her own company, but that doesn't mean that the URL has to be different. It means that even though she can access /companies/123/users, she cannot access /companies/456/users. This should be done either in group middleware or in each controller. Personally, I suspect that I'd use group middleware like this:
$app->group(/companies/{id}, function () {
$this->get('/users', 'App\Controller\Company\CompanyUsersController:index')->setName('company.user.index');
})->add(CheckUserCompanyMiddlerware::class);
and either display an error page or redirect back to the correct URL for the store manager if they go to the "wrong" company.

How to authorise action against account that have multiple users in Laravel

The desired outcome would be that users can create account for company becoming first employee for that company having super-admin role. He then can create other employee accounts that have access to company account.
For that I have two models
Team (the company account)
User (the employee account)
and there is one-to-many relationship between team and user.
I have added global scope to User model in order to automatically load associated team so it would be easy to display necessary information in views.
The User model is authenticable.
Users can have different permissions. For example only super-admin is allowed to delete other user from team. I am following the Laravel way to authenticate action by using custom requests
public function destroy(DeleteTeamUserRequest $request, $id)
{
$this->users->deleteById($id);
return redirect()->route('dashboard')->withFlashSuccess('Deleted User');
}
class DeleteTeamUserRequest extends FormRequest
{
public function authorize()
{
return $this->user()->isSuperAdmin();
}
}
This way I can be sure that user without super-admin role wouldnt delete other user. However theoretically super-admin of company 1 is able to to delete user form company 2. Of course he would never have that url in frontend but if he tempers the code he has the permission to delete all users.
I could always check in controller that given user is within the team but thats just wrong.
I made both Users and Teams authenticable (create another web guard for team), manually log in the team once user logs in (the same on logout) but came upon the same problem - I have to check if user I try to delete is within the same team as user that performs the action.
{
public function authorize()
{
return $this->user()->isSuperAdmin()
&& $this->team()->has($otherUser); // I dont have other user here
}
}
The scenario should be quite common but I couldnt find a good explanation on how to properly handle these cases so I would highly appreciate any suggestions and help.
You can fetch the route param in the FormRequest like this:
$otherUserId = $this->route('id');
You can just fetch the other user and its team and compare that with the team of the authenticated user.
You can also write a little helper/service which can do the same; check if 2 users are part of the same team.
You're describing multi tenancy in your application. Many different users or groups of users having access to the same software but restricted to their data only.
Basically what you want is a team_id on the tables that have relationships to the team of a given user.
Typically, a middleware checks that a user belongs to a team and has the task of scoping queries for the duration of the request. For example, the middleware would add a where clause to every query made during the request lifecycle of the form ->where('team_id, auth()->user()->team->id).
This would prevent users with super admin roles from cross polluting or deleting other team's data because the where clause would not allow for it.
There a few really good packages that provide this functionality and then some. My personal favorite is Landlord.

CakePHP ACL Individual Object Access Control [duplicate]

Currently I am writing an application where I have multiple users. They have data that should only be visible to them and not the other authenticated users in the system. I also have administrators who manage the system and have access to all of the information. What is the best way to limit users to their data without limiting admin users?
Currently I am using a callback to limit the queries by user, but the admin will get the same limits. So I need to know a better way to do it. More importantly, the right way to do it.
For example, I want the standard user to be able to see their user information only and be limited to CRUD operations on their information only. The admin, however, should be able to see ALL users and CRUD ALL user data. Any ideas?
You need:
Information about the current user
Information about the item in question
You combine them with something like this (simple example):
$user = $this->Auth->user();
$book = $this->Book->find(…);
if ($user['type'] != 'admin' && $user['id'] != $book['Book']['creator_id']) {
$this->Session->setFlash("You're not allowed to view this item");
$this->redirect('somewhere');
}
You could make a method in your model like
function userCanAccessItem($item, $user)
to centralize the logic for the access check and call it from your controller.
Better yet, if you're using Cake's admin routing, you can omit all checking in the admin_ actions and only apply normal user access privilege checking in the user accessible actions.
You may also want to look at ACLs for more fine-grained access control.
You can get the current user info this way: $this->Auth->user(). You can use the user group id in your callback to limit the query. Also take a loot at WhoDidIt Behavior.
For any one else who comes here this is how I set it up.
First I set up a basic Role based ACL
Then I deny access to reports/all for normal users
$config['rules']['deny'][reports/all'] = 'Role/default' ;
Then in the model that I wanted to protect I added this:
public function beforeFind($queryData){
//TODO:make this cleaner
App::uses('ComponentCollection', 'Controller');
App::uses('AclComponent', 'Controller/Component');
$collection = new ComponentCollection();
$this->Acl = new AclComponent($collection);
if(!$this->Acl->check(array('User'=>AuthComponent::user()),'/reports/all/')){ // must be a user (not admin)
$this->bindModel(array('hasOne' => array('ReportsUser')));
$queryData['conditions']['ReportsUser.user_id'] = AuthComponent::user('id');
$queryData['recursive'] = 2;
}
return $queryData;
}
On the cases where ACL doesn't allow access to reports/all we add a condition to any find queries so it only shows reports with the correct user_id.

Categories