show/hide (laravel) menus depending on roles - php

I want to have certain menu options visible only to certain users.
I've modified my user table in the standard auth framework by adding two roles - boolean columns: isTeacher and isOnCommittee.
I thought I'd try
create a method in my controller to check if the logged in user has the specific role, and then
in the view, call the method, and show the menu option, (or not).
It's all well and fine to put a #if (Auth::iSTeacher()) into my view, but where do I put my function?
I did a search for guest() across all files and found this
...\vendor\laravel\framework\src\Illuminate\Contracts\Auth\Guard.php:
public function guest();
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
I understand the purpose of a guard is protect a route, so this is not the place for it.
Where am I supposed to be creating my function?
(if you didn't guess - I'm very new to laravel.

My preferred way to do this would be with Authorization via the Gate Facade. You can define so-called "abilities" in AuthServiceProvider like this:
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
}
Than inside views you can check:
#can('update-post', $post)
Edit Post
#endcan
Source:
https://laravel.com/docs/5.2/authorization#within-blade-templates

You can use Query Scopes functions in your User model. e.g. write your isTeacher() in User model and in view check
#if(Auth::user()->isTeacher)
YOUR CODE HERE
#end if

Related

How to name a policy for multimodel route in Laravel?

I'm trying to write a policy for this route:
Route::delete(
'users/{user}/locations/{location}',
[UserLocationController::class, 'destroy'],
)->middleware('can:delete,user,location');
but I don't know how to name the policy to allow Laravel to find it automatically. I'm deleting records from location_user table, but I don't have model for this table. My policy function looks like this:
public function delete(User $user, User $userModel, Location $location)
{
return $user->id === $userModel->id
&& $user->locations->contains($location->id);
}
I've tried names like LocationUserPolicy, UserLocationPolicy, LocationPolicy, but none of them works. Do you have any suggestions how to name the policy or how to write custom logic to allow Laravel to find it?
Okay, I found the answer right in the Laravel documentation :D The trick is in the changing the order of the can middleware attributes.
The first element in the array will be used to determine which policy should be invoked, while the rest of the array elements are passed as parameters to the policy method and can be used for additional context when making authorization decisions.
So I changed my route to this:
Route::delete(
'users/{user}/locations/{location}',
[UserLocationController::class, 'destroy']
)->middleware('can:delete,location,user');
named the policy LocationPolicy (by first attribute) and changed my policy function to this:
public function delete(User $user, Location $location, User $model)
{
return $user->id === $model->id
&& $user->locations->contains($location->id);
}
Now it works like a charm.

How to have Acces level Control the easiest way?

I am designing a CMS and i have setup users based on the role.How do i limit the users of their permissions based on their access level?
The easiest way is to get users by their role. Have a column for your users table called role or whatever you name it.
You can do Access Level Control easily with Gates
In your app\Providers\AuthServiceProvider register your policy. Example:
use Illuminate\Support\Facades\Gate;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('isUser', function($user){
return $user->role == 'user';
});
$gate->define('isDealer', function($user){
return $user->role == 'dealer';
});
}
isUser , isDealer are the user Types we are defining to Use in the project blade,controllers.You can change it as you like.Role is the column that you created in the table and we are comparing with the table values which are the user types user and dealer.
you can limit values in blade with laravel method
#can('isUser')
<only visible to users based on role user>
#endcan
It will be still accessible via routes so you can limit via controller functions or routes.
//controller
public function create()
{
if(!Gate::allows('isUser')){ // || for multiple parameters can('isAdmin' || 'isUser)
abort(404,"Abort");
}
return view('yourView');
}
This way the controller function will be not accessible for the roles defined.
Check the official documentation for in detail methods and information.

How to Dynamically add gates in Laravel?

How to Dynamically add gates in Laravel?
I built my CMS on laravel, and it support plugins. Plugin can add menu item in dashboard, some page , routes etc. I need use gate for protect edit / delete / add / change page for example, that an authorized user did not create.
So I need that each plugin can Dynamically add this permissions (view /add/edit/update/delete/)
How I can do this?
And btw plugin locate in APP/Plugins folder.
And Admin can attach this permission to some roles http://prntscr.com/kidnxz
https://pastecode.xyz/view/bcb9d00f here example table that I have.
and problem that I need also add in permissions table permission
I use laravel 5.6
Here example of simple plugin pastecode.xyz/view/75d2fa28 Calls boot when plugin loaded And onActivate when user activate this plugin
I'm looking for something like dynamic Policies
or like https://octobercms.com/docs/plugin/registration
'permissions' => ['acme.blog.*'],
You can use Gate::policy(PolicyTarget::class, PolicyImplementation::class) to register a new gate policy inside your plugin.
Assume you have a policy target being e.g. a user (bad example maybe).
Create a policy with e.g. php artisan make:policy UserInteractsWithBlogPolicy
class UserInteractsWithBlogPolicy {
// Boilerplate and custom code
}
Then you can register this when the plugin boots:
public function boot() {
// TODO: Implement boot() method.
$this->enableRoutes();
$this->enableViews();
\Gate::policy(User::class, UserInteractsWithBlogPolicy::class);
}
Then you can use the normal policy rules like e.g.
$user->can("view", Blog::find(1));
Or basically do whatever can be done according to https://laravel.com/docs/5.6/authorization#authorizing-actions-using-policies
As far as I understood from your question your CMS may has several gates that you do not want to define separate gates for each of them. You may try before method to define dynamic gates.
public function boot(){
Gate::before(function (User $user, $ability) {
Gate::define($ability, function (User $user) use ($ability) {
return $user->hasPermission($ability);
});
});
}
In the above example hasPermission is a method in User model that check if user has the right permission/ability.
Now you can use it like this in controller:
Gate::authorize('CUSTOME_ABILITY');
Or as middleware:
Route::get('/', function (Request $request) {
// your code
})->can('CUSTOME_ABILITY');

Final Route URL Change Laravel 5.5

I am working on a school project. while working on a schools detail page I am facing an issue with the URL. My client needs a clean URL to run AdWords. My school detail page URL: http://edlooker.com/schools/detail/4/Shiksha-Juniors-Ganapathy. But he needs it like http://edlooker.com/Shiksha-Juniors-Ganapathy. If anyone helps me out it will be helpful, thanks in advance.
You need to define this route after all routes in your web.php (if laravel 5.x) or in routes.php (if it is laravel 4.2).
Route::get('{school}','YourController#getIndex');
And your controller should be having getIndex method like this,
public function getIndex($school_name)
{
print_r($school_name);die; // This is just to print on page,
//otherwise you can write your logic or code to fetch school data and pass the data array to view from here.
}
This way, you don't need to use the database to get URL based on the URL segment and you can directly check for the school name in the database and after fetching the data from DB, you can pass it to the school details view. And it will serve your purpose.
Check Route Model Binding section in docs.
Customizing The Key Name
If you would like model binding to use a database column other than id when retrieving a given model class, you may override the getRouteKeyName method on the Eloquent model:
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
In this case, you will have to use one front controller for all requests and get data by slugs, for example:
public function show($slug)
{
$page = Page::where('slug', $slug)->first();
....
}
Your route could look like this:
Route::get('{slug}', 'FrontController#show');

Middleware for checking if resource is owned by user

I'm having some trouble making middleware that checks if the user owns the resource being requested.
For example, if the user goes to /playlists/1/edit, and they do not own playlist 1, it should display a 401 error.
Here's what I have so far:
class CheckOwnership {
public function handle(Request $request, Closure $next)
{
if (Playlist::find($request->route()->parameters()['playlists'])->user_id !== $request->user()->id)
{
return response('Unauthorized.', 401);
}
return $next($request);
}
}
This is terrible and only works for the Playlist resource, but I can't find any better way of doing this.
Thanks
This can easily be achieved with the newly added Form Request Validation.
You can see it in detail here (Authorizing Form Requests):
http://laravel.com/docs/5.0/validation#form-request-validation
The example given is actually about a user attempting to edit a comment they own.
Extract:
The form request class also contains an authorize method. Within this
method, you may check if the authenticated user actually has the
authority to update a given resource. For example, if a user is
attempting to update a blog post comment, do they actually own that
comment?
In your case, simply return false from the authorize method if they do no own the Playlist.
Currently, Laravel 5 does not support passing parameters to middlewares. I use sessions instead.
On your playlist controller, fetch the owner of the playlist and store it on a session. Let's say you have a playlists table with columns userID and playlistID.
public function __construct($playlistID){
$owner = Playlist::where('playlistID',$playlistID)->pluck('userID');
Session::put('OWNER',$owner);
$this->middleware('CheckOwnership',['only'=>'edit']); // apply it to edit function only, assuming you are using a route resource
}
Then, simply retrieve it on your middleware handle function.
public function handle(Request $request, Closure $next)
{
if (Session::get('OWNER') != $request->user()->id)
{
return response('Unauthorized.', 401);
}
return $next($request);
}
So far, this is a simple workaround. We have to wait till Otwell considers filter-like middlewares. Hope this helps!
For those using Laravel 8, I recommend using using Policies. This would let you organize authorization logic for specific models (e.x. the Playlist model for #ntzm).
So for example, a PlaylistPolicy class can be generated,
php artisan make:policy PlaylistPolicy --model=Playlist
and then the update function could look like this.
public function update(User $user, Playlist $playlist)
{
return $user->id === $playlist->user_id;
}
There are multiple way of enforcing this policy. If you would like to use middleware, Laravel has the can middleware that can enforce policies, so new middleware won't need to be written. In your route file this would look something like this,
Route::put('playlists/{playlist}/edit', ...)
->middleware(['can:update,playlist']);
Note: If the --model option isn't used, the policy will have to be registered manually, and example policy methods won't be automatically generated.

Categories