Protecting controller actions and routes in Symfony - php

Official documentation on Symfony pages suggest using ROLE_ADMIN (example) attribute in the ACL call
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
my question is if it is possible to use ACL call without the ROLE_ prefix.
The issue is that we have multiple systems without ROLES but with permissions as "USER.CREATE" or "USER_CREATE" which would protect the route POST /user/create and action Create() in UserController.
The best solution would be to have a Voter which would call the ACL as
$objectName = 'USER';
$action = 'CREATE';
$this->denyAccessUnlessGranted($objectName. '_'. $action, null, 'Unable to access this page!');
But this does not work because everything relays on ROLE_ prefix.
Did anyone tried to achieve something like this?

IIRC, ROLE is mandatory prefix in symfony role voter and I do not think it can be avoided (it is hardcoded) but you can create your own voters and add logic you need there, you can read more here:
https://symfony.com/doc/current/security/voters.html
Also a great tutoral for voters:
https://stovepipe.systems/post/symfony-security-roles-vs-voters

Related

How to prevent users from checking not existing urls, how to hide routes in Laravel 8.*

First question was solved with findOrFail method
Is there any way to prevent users from checking non-existing routes?
Example
I've got route to http://127.0.0.1:8000/event/9
but event with id 8 does not exist, if user would go to that id there is a massage:
Attempt to read property "photo_patch" on null (View: C:\xampp\htdocs\Laravel1\resources\views\frontend\eventView.blade.php)
Or any other error from db that record does not exist.
Second question
How to turn on preety URLs in laravel
So my page with display http://127.0.0.1:8000 not http://127.0.0.1:8000/events something...
I know that its somewere in config files but I cant find it.
Example class and route that uses it:
-----------------------------Class----------------
public function eventView($id)
{
$notDisplay = Auth::user();
$eventView = Event::findOrFail($id);
if(!$notDisplay){
$eventView->displayed = $eventView->displayed +1;
$eventView->save();
}
return view('frontend/eventView', ['eventView' => $eventView]);
}
----------------Route-----------------
Route::get('event/' . '{id}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
First off, use the container!
Laravel's service container is very powerful and your controller resolve use-case is one of the most common places you should be using it. The url argument and controller argument MUST match for this to work.
Your route:
Route::get('event/' . '{event}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Your Controller:
public function eventView(Event $event)
{
return view('frontend/eventView', ['event' => $event]);
}
When leveraging Laravel's dependency injection and container, you get your findOrFail() for free. You should also remove your auth check, and handle that with route middleware.
In terms of "prettifying" urls, Laravel's route model binding feature allows you to control what property of a model is used to for container resolution. For example, let's imagine your event has a unique slug you'd like to use instead of the auto-increment id:
Route::get('event/' . '{event:slug}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Laravel's routing functionality offers a fallback feature that would allow you to fine-tune where the user is redirected if the route model binding failed.
https://laravel.com/docs/8.x/routing#fallback-routes
With regard to preventing an unauthorized individual from editing someone else's event. The first place I would put protections in place would be at the time of persistence (when saving to the database). While you can do this in every place in your codebase where persistence occurs, Laravel's Observer feature could be a great fit. That way, you can be confident that no matter what code is added to your app, the ownership check will always be run before making any changes to events.
https://laravel.com/docs/8.x/eloquent#observers
The second place that I would put protections in place would be with a route middleware on any routes that can mutate the event. That way, you can redirect the user away from an event they don't own before they even have a chance to attempt to edit it.
https://laravel.com/docs/8.x/middleware#assigning-middleware-to-routes

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).

Protect routes in Laravel 5.1

I am using Laravel 5.1 for my project. I am trying to secure Routes and make sure only logged in user can access certain routes. I am aware about middlewares but I am wondering if anyone post an example or a link explaining about middleware and how to protect a page using middleware.
Thanks
To build on the answer given by Joe Rose, you can also specify the middleware in your controller rather than in your routes.php file.
E.g you could have your routes set out like
Route::get('/example', 'ExampleController#index');
Route::post('/example/post', 'ExampleController#post');
Route::resource('blog', 'BlogController');
And then inside your controller reference it like so:
class ExampleController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
//....
If you're looking for more info, check out the link to the docs Joe gave, and also this blog post which explains what middleware is really well and how to create your own if you need to.
You are correct about using middleware. The included Auth middleware is what you should use, as long as you are also using the included Auth controller to authenticate users. You would write your route like this:
Route::get('/page', array(
'uses' => 'Controller#method',
'middleware'=>'auth'
));
(The above example is using a GET request, but it could other request types, like POST for example).
This will use the default behavior of the middleware which checks to see if the user is logged in (authenticated). You can also extend or overwrite the built-in functions to allow you to direct the application on where to send the user if they are or are not logged in, etc. Laravel's official documentation is a good starting point: link

Laravel 5 new auth: Get current user and how to implement roles?

I am currently experimenting with the new Laravel 5 and got the authentication to work (register/login).
To get the authenticated user in my controller I currently inject Guard into the controller action:
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
class ClientController extends Controller {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index(Guard $auth)
{
return view('client.index', ['user' => $auth->user()]);
}
...
First Question: Is this the recommended way?
Second Question: How would I go about implementing some kind of roles/permissions? Something like client.edit, client.add, ... Does Larval 5 offer some kind of convenience here?
How would I set the necessary role/permission for a route/controller action?
I am thinking that I might need to write my own middleware for that. Any suggestions on how to approach the problem?
After spending some more time on Laravel 5 I can an answer my own question:
Is injecting Guard the recommended way? No: If you need to access Auth in your view, you can do so already like this:
#if( Auth::check() )
Current user: {{ Auth::user()->name }}
#endif
This uses the Auth facade. A list of all available facades is in config/app.php under aliases:
What if I need Auth in my controller? Injecting an instance of Guard like shown in the question works, but you don't need to. You can use the Auth facade like we did in the template:
public function index()
{
if(\Auth::check() && \Auth::user()->name === 'Don') {
// Do something
}
return view('client.index');
}
Be aware that the \ is needed before the facade name since L5 is using namespaces.
I want to have permissions/roles using the new auth mechanism in L5: I implemented a lightweight permission module using the new middleware, it is called Laraguard. Check it out on Github and let me know what you think: https://github.com/cgrossde/Laraguard
UPDATE: For the sake of completeness I want to mention two more projects. They provide everything you need to save roles and permissions in the DB and work perfectly together with Laraguard or on their own:
https://github.com/caffeinated/shinobi
https://github.com/romanbican/roles
If you want make your own custom authentification, you need to keep the User model from Laravel 5 with all the dependency. After you will be able to login your user in your controller. Dont forget to put (use Auth;) after the namespace of your controller.

Categories