How to Dynamically add gates in Laravel? - php

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');

Related

How to Restrict controllers or routes for different type of users in API

I'm working on a project with laravel. in my project there's two type of users one of them are admins and other one is normal users.
btw project is only provides API and there's no blade views.
I give a token to any user or admin logins with the api. and application will identify user or admin by sending that token with an authorization header and I check if token is validate and the user type is admin then give access to the admin features for that client.
here's my code for this part:
$admin = Auth::guard('admin-api')->user();
if ($admin) {
// allow to use admin features
}
else {
return response()->json(['error' => 'token is invalid'], 401);
}
I read something about applying Restrictions on a controller class in laravel and it was written there to add a constructor like this into controller class:
public function __construct() {
$this->middleware('admin-api');
}
and also there's something like that just for Restricting routes. like this
but I just want to know is it necessary to add same constructor to my controller class while the project just provides API? or the way that I'm doing is correct?
You are doing it right.
I would prefer restricting the request via routes, so there is no need to add constructor on each new Controllers.
Route::middleware(['admin-api'])
->group(function () {
Route::get('cart', 'Carts\CartController#retreive');
Route::post('cart/coupon', 'Carts\CartCouponController#addCoupon');
Route::delete('cart/coupon', 'Carts\CartCouponController#deleteCoupon');
Route::delete('cart/flyer', 'Carts\CartController#deleteFlyer');
});
This will apply the admin-api middleware on all the routes in the group and there is no need to add a constructor on Carts\CartController and Carts\CartCouponController, just to have middleware restriction.

Laravel policy autodetect

today i was creating USER profile page with is controlled in ProfileController it returning views to profile page, profile settings, etc.
so i decide to make some Policy rules to Edit profile and etc.
so i found i should use Middleware / Gates / Policy, based on Laravel Doc i chose Policy because profil page is public but only specific part of it can author edit so i needed #can
So my steps:
php artisan make:policy ProfilePolicy ( without model )
Registered policy to AuthServiceProvider in $policies property
writed methods like edit inside ProfilePolicy
then i started thinking how i define it to my Controller hmmm, documentation doesnt helps me :/
so i tryed blade #can('edit', $user) method and it worked, but HOW ?, how to define specific policy to one Controller ? ( not Model ), how to define multiple Policy to single Controller
i m lost how laravel Magic done this maybe because of Naming ? ProfileController => ProfilePolicy ?
In the controller you can write this
public function edit(Profile $profile) {
$this->authorize('edit', $profile)
}
Laravel does this:
Check the type of $profile, and it's a Profile::class
Check policies registered for that class (your step 2)
Looks for the edit method in that policy, if not found, return false meaning user is not authorized
Executes the edit() function that returns true/false
In blade the #can directive does exactly the same thing.
Policies are meant to be tied to Models, it's a convenient way to write rules to handle single models, but they can be triggered in many ways (like the authorize() method in controllers and #can directive in blade).

How is Localization in Laravel implemented?

I want to implement localization on my website using Laravel 5.5.
However, I am not sure what the standard practice when using localization should be. I have used the LocalizationController module from the Laravel documentation. My goal is to have the localization option selected via a dropdown. Then the user's selection should be remember.
Do I store their selection in a database for future use?
Or, is this something to keep in a cookie?
Side note:
(I want to avoid having their selection in the url. I'll either pass the data in a request or get method.)
For registered and logged-in users i recommend to store the users language in the database. Everytime a user logs in the application should set the language for the current user. Maybe you take a closer look on middleware. Build a language middleware, register it as new middlewaregroup and assign it to every route (-group) you need. A middleware could look like this:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class LanguageMiddleware
{
public function handle($request, Closure $next)
{
if(Auth::check()){
// user is logged in
App::setLocale(Auth::user()->language);
return $next($request);
}
App::setLocale(config('app.locale'));
return $next($request);
}
}
Now register the new middleware in app/Http/Kernel.php as new middleware-group under protected $middlwareGroups:
// other middleware-groups
'language' => [
\App\Http\Middleware\LanguageMiddleware::class
]
Finally assign middelware-group to route (-group):
Route::group(['middleware' => ['language']], function(){
// Routes...
});
Unfortunately there is no build-in function to show a dropdown-language-select. But you can simply build a blade-partial which you can integrate in your navbar or where-ever you want to show/use it. You could ask new users during registration for their preferred language.
Guests/unregistered users could use the dropdown. By default they should see the default language.
Hopefully this helps you.

Laravel - How to restrict certain functionality based on user authentication?

Laravel newbie here (obviously :D ). I've set up a new model & controller for a model named Pages.
Every User has many Pages.
Each Page has a single User.
I've created the following functioning controller actions (& views):
PagesController::index
PagesController::create
PagesController::store
PagesController::show
PagesController::edit
PagesController::delete
So you can edit a Page by going to url.dev/pages/{id}/edit.
The problem is, you can access all of these routes regardless of your session status. So random users can edit any given Page. Which, obviously, is terrible.
Can anyone point me in the direction of what I should read up on, to limit the ability to access my model's controller actions based on whether or not the user is logged in (and if it's the correct user, at all)?
To force a specific route to be only accessible by authenticated users you can specify middleware auth in the controller constructor, like so:
public function __construct()
{
$this->middleware('auth');
}
Also you can restrict which methods you want auth to be applied to in the controller, using the only or except parameters. Using only you could do:
public function __construct()
{
$this->middleware('auth', ['only' => ['create', 'store', 'edit', 'delete']]);
}
You´re looking for middleware..
You can read more here
public function __construct()
{
$this->middleware('auth')->only('index');
$this->middleware('admin')->except('store');
}
Other answers are good, but I prefer to use middleware on route groups.
So when I have several routes like this:
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
I would add them inside Laravel Route group. Like this:
Route::group(['middleware' => 'auth'], function() {
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
});

Multiple prefixed routes and DRY principle

I use CakePHP 2.2.7
In my app I have a public area and admin area.
I use prefixed routes so for admin actions I use
admin_index() etc.
Now I need to add additional admin area for managers. This manager area will be different in some cases against the admin area.
Different layout, not all actions allowed.
My question is:
Should I simply duplicate actions which already implemented for admin area (and add another prefix, for example manager_index() ) or there is a more simple and DRY solution?
You can do this for example
public function manager_edit($fooId = null) {
$this->admin_edit($fooId);
}
But if you did a good job most of your code should be already in the model and your code look like this (just a basic example);
public function manager_edit($fooId = null) {
if ($this->Foo->edit($fooId, $this->request->params, $this->Auth->user('id')) { /*....*/ }
}

Categories