I have an application that has users and products. Only the product owners should be able to view the product.
If a user guesses a product id they might be able to view the product. For example
http://booker.app/admin/products/32
Following that link would allow any logged in user to view the product with id 32.
This is the route in question:
Route::middleware(['middleware' => 'auth'])->prefix('admin')->group(function(){
Route::resource('products', 'ProductController');
});
My products controller show method:
public function show(Product $product)
{
if($product->user_id !== Auth::user()->id){
return $this->forbidden('admin/products');
}
return $this->makeResponse('admin.products.product', compact('product'));
}
The forbidden and makeResponse functions simply check if the request was an ajax request and if so returns json.
As you can see I'm using route model binding and I'm checking if the authorised user is the same as product user_id. Basically is there a better way of checking if the user is the owner of the product.
In Laravel you can define Policies in order to specify ACL and access logic to your data layer.
For example create the class ProductPolicy:
class ProductPolicy
{
public function show(User $user, Product $product)
{
return $user->id === $product->user_id;
}
}
Then bind the policy to the Product model inserting the following line into $policies array in AuthServiceProvider:
protected $policies = [
Product::class => ProductPolicy::class,
];
Now in your controller method you can use the following syntax to authorize the user to do a certain action
public function show(Product $product)
{
$this->authorizeForUser(Auth::user(), 'show', [$product]);
return $this->makeResponse('admin.products.product', compact('product'));
}
The method authorizeForUser will call the show method of your Policy, which will return true only if the product belongs to the authenticated user.
Hope it helps.
Related
I have a nested route like this:
Route::get('/products/{product}/auctions/create', [AuctionController::class, 'create']);
All it does is it sets an auction on a given product. A Product - hasMany - Auctions.
AuctionPolicy to be invoked to authorize actions on an Auction instance:
// App\Providers\AuthServiceProvider
protected $policies = [
Auction::class => AuctionPolicy::class
]
AutionPolicy:
public function create(User $user, Product $product) // <-- where do I get this $product from?
{
if( $product->user_id === $user->id ) {
return $product->canCreateNewAuction();
}
return false;
}
AuctionController:
public function create(Request $request, Product $product)
{
$this->authorize('create', Auction::class);
}
It throws an Exception: AuctionPolicy::create expects two params, only one passed.
How do I pass $product to AuctionPolicy::create method.
Laravel allows to pass additional data to policies.
Supply Additional Context
In the ->authorize method, instead of passing Auction class string, we can also pass an array with additional data.
In this example, we want to be able to pass $product to the invoked Auction Policy:
$this->authorize('create', [Auction::class, $product]);
The first item of this array specifies which Policy (Auction::class => AuctionPolicy::class) to invoke and rest are additional data.
It doesn't have to be a class string, it could also be an instance.
$this->authorize('show', [$auction, $product]);
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.
}
So I have created a policy and registered it in the AuthServicePRovider, but it always returns false. It is my first time working with policies so I am sure I am doing it wrong, but following a few examples, nothing has worked for me.
I am logged in with user that has an id of 1. I try to edit a label that has a user_id of 1, returns false, and also when trying to edit a label that has a user_id of 2. This last one works as expected, but f the user_id and label->user_id match, I should ave a form displayed. Instead, I get this each time:
This action is unauthorized.
Any ideas?
AuthServiceProvider: (Tried both but both don't work):
protected $policies = [
'App\Label' => 'App\Policies\LabelPolicy'
];
And this one also did not do the trick:
protected $policies = [
Label::class => LabelPolicy::class
];
LabelsController#edit:
public function edit(Label $label)
{
// $this->authorize('edit', $label); // This also returns false
if (auth()->user()->cannot('edit', $label)) {
dd('NO'); // This is always shown
}
}
LabelPolicy:
public function edit(Label $label)
{
dd('test'); // This is never shown anywhere
return auth()->user()->id === $label->user_id;
}
The policies expects actually two inputs, the first input is always the User class, the second input is the Model and defaults to the Model class. So in your case:
LabelPolicy
public function edit(User $user, Label $label)
{
return $user->id === $label->user_id;
}
LabelsController#edit:
public function edit(Label $label)
{
$this->authorize($label);
}
if your $this->authorize inside Controllers always returns false, then double check if your model and model policy namespace was imported in AuthServiceProvider and also your model has been imported into your controller.
Let's assume I have different users that have the access to some pages. Pages are almost the same, but with minor changes, like delete button is not shown for user but for the admin.. and so.
If I have a controller called DashboardController which has a method index that shows some info related to the type of the user.
I have two approaches, one is to make different controllers like:
Admin\DashboardController
User\DashboardController
but my routes will be like : localhost/admin/dashboard and localhost/user/dashboard
and the other approach is to make one controller called DashboardController and check for the type of the user there.
So which approach is better, and is there a better approach to make one URL instead of prefixing them?
I suggest taking a look at view composers: http://laravel.com/docs/4.2/responses#view-composers
View composers make it possible to bind extra logic to a view. This could remove some double code or in your case could validate the authenticated user and bind a boolean to the view.
Quick example:
// DashboardComposer.php
class DashboardComposer {
public function compose($view)
{
$user = Auth::user();
$isAdmin = $user->admin; // The attribute 'admin' would be a boolean.
$view->with(
'showDelete',
$isAdmin
);
}
}
In your blade view you would check for this state:
// dashboard.blade.php
#if($isAdmin)
<button>Delete</button>
#endif
Please take note that this does NOT protect your 'delete' endpoint!
Adding a before filter on these routes should suffice.
// routes.php
Route::delete('resource', [
'as' => 'resource.delete',
'uses' => 'DashboardController#delete',
'before' => 'admin' // This would call a custom filter.
]);
And lastly the custom filter would almost look the same as the view composer.
// filters.php
Route::filter('admin', function () {
$user = Auth::user();
return $user->admin; // The attribute 'admin' would be a boolean.
});
One last note I've placed the view composer in a separate class, by doing so I can better organise my code. The filter is placed in the same file as all the other filters, however it is possible to do the same as with the composer: http://laravel.com/docs/4.2/routing#route-filters
Take a look at 'Filter Classes'.
I have solved this like the following way
routes.php
Route::get('dashboard', ['middleware' => 'auth', 'uses' => 'DashboardController#index']);
// Admin
Route::group([ 'middleware' => 'toegang:admin' ], function ()
{
Route::get('dashboard/projecten', 'ProjectController#index');
Route::get('dashboard/groepen', 'GroepController#index');
Route::get('dashboard/periode', 'PeriodeController#index');
Route::get('dashboard/producten', 'ProductController#index');
Route::get('dashboard/gebruikers', 'UserController#index');
Route::get('dashboard/scoring_rubrics', 'ScroingRubricsController#index');
Route::get('dashboard/pos', 'PosController#index');
Route::get('dashboard/project_status', 'ProjectStatusController#index');
Route::get('dashboard/beoordeling', 'BeoordelingController#index');
});
// Student
Route::group([ 'middleware' => 'toegang:student' ], function ()
{
Route::get('dashboard/project_status_student', 'ProjectStatusStudentController#index');
Route::get('dashboard/account', 'AccountStudentController#index');
});
I have multiple roles admin / student and they uses one controller DashBoardController.
SO to access dashboard both admin or student must be auth to access the dashboard homepage. For the specific pages for admin and student i use two route groups.
dashboardcontroller#index
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* #return View View
*/
public function index()
{
$autorisatie = Auth::user()->autorisatie();
return view('dashboard.home', compact('autorisatie'));
}
In the dashboard controller i will get role from the user by calling autorisatie()
autorisatie method in user model
public function autorisatie($rol = null)
{
$autorisatie = DB::table('autorisatie')
->select('rol')
->where('autorisatieID', Auth::user()->autorisatieID)
->first();
// Check als de opgegeven role in de routes hetzelfde is
// Als de ingelogde user
if($rol)
{
// Return true als $autorisatie rol hetzelfde is als de
// opgegeven route role
return $autorisatie->rol == $rol;
}
// return false
return $autorisatie->rol;
}
And in my view i will check for different displaying data or pages like so:
view dashboard
{{--Extends master page--}}#extends("master.master")
{{--Section for content area--}}
#section("content")
<h1>Dashboard</h1>
<p>
Dashboard - {{$autorisatie}} <br>
#if ($autorisatie == 'admin')
Show admin things.....
#elseif ($autorisatie == 'student')
Show student things...
#endif
</p>
What I basically want is user permissions.
I've got an table called 'accounts' in my database. There is a column called 'group_id'.
I want to set it when the 'group_id' = 3, then the user is admin. Then he can view special sites, buttons, and things like that. I've tried to implement something like that:
public function ($roleName) {
$role = $this->roles;
if ($role->name == $roleName) {
return true;
}
return false;
}
Also, I don't know what and how the model is needed, do I need an new one and things like that.
Old post, but maybe someone will find this useful
Add a method to your User model that returns true if the user is an admin. In our case here, it's simply "is our group_id equal to 3?"
// models/User.php
class User extends Eloquent
{
...
public function isAdmin()
{
return $this->group_id == 3;
}
}
Next add a filter that can be used to protect routes
// filters.php
Route::filter('admin', function($route, $request)
{
if ( ! Auth::user()->isAdmin())
{
return App::abort(401, 'You are not authorized.');
}
});
Finally use the filter to protect a group of routes. I this simplified case, only an admin user could access /admin
// routes.php
Route::group(array('before' => array('auth|admin')), function()
{
Route::get('/admin', function()
{
return Response::make("You're an admin!");
}
});
Based on this post:
http://laravelsnippets.com/snippets/admin-route-filter
I suggest Authority for Laravel 4
I personally use Verify package for user management.