I'm using a ressource controller and it is possible to edit a user like this: users/edit/8 (8 is the id).
How can I check that only user 8 can edit his account?
Because another user could set any ID in the URL and edit any account.
I could use:
if (!Auth::user()->id === $id) {
//throw exception ...
}
But that is not the point of laravel "dont repeat yourself".
Should I use ressource controllers for user related stuff (like account settings etc) ?
Or just for admin related stuff?
Shouldn't be $id?:
if (!Auth::user()->id === $id) {
// still throw exception? Which one?
}
If you don't want a user to be able to have access to a particular route 'users.edit', then you need to create a filtered route:
Route::get('users/edit/{id}', array(
'as' => 'users.edit',
'before' => 'auth|checkUser',
'uses' => 'UserController#edit'
)
);
Route::filter('checkUser', function($route, $request, $value)
{
if (! Auth::use()->id === $route->parameter('id'))
{
return Redirect::to('operation-not-permitted-route');
}
});
As you are using RESTful controllers, this route should be added before your Route::controller() command.
This is one way. Another one would be to create a new controller just for your profile stuff and create specific routes and filters for this controller.
About using RESTful and Resourceful routes, I always like to point this post from Phil Sturgeon: http://philsturgeon.co.uk/blog/2013/07/beware-the-route-to-evil.
Related
The delivered auth middleware that comes with Laravel 5 is great for user-only routes and controllers, however I want to add the ability to also check if the user is an administrator.
Currently, in my controllers, I have this for every class:
if (Auth::user()->level <= 1) {
// admin can do these actions
} else {
// redirect
}
It's very redundant and I'd like to look at what my options are. Since I want to retain the original auth middleware for user authentication, should I be building a new one for administrator authentication, or can I make some simple change in the original auth middleware that can account for my code above?
Middleware in Laravel 5.0 do not support arguments (this will be added in the upcoming 5.1 release).
Your options are to either create a separate middleware for this, or use route filters.
You can create a route filter by putting this in your RouteServiceProvider's boot method:
$router->filter('userLevel', function($route, $request, $level)
{
$user = auth()->user();
if ( ! $user || $user->level > $level)
{
return response('Unauthorized', 401);
}
});
Then use that filter in your routes:
Route::group(['before' => 'userLevel:1'], function($router)
{
// define routes...
});
In a Laravel 4 app, i'm using subdomain routing around a bunch of Route::resource's like this:
Route::group(['domain' => '{account}.my.app'], function()
{
Route::group(['before' => 'auth'], function($account)
{
Route::resource('organisations', 'OrganisationsController');
Route::resource('clients', 'ClientsController');
Route::resource('domains', 'DomainsController');
});
});
In my auth filter i'm doing the following:
Route::filter('auth', function($route)
{
// you could now access $account in a controller if it was passed as an argument to the method
$account = $route->getParameter('account');
// share account variable with all views
View::share('account', $account);
// Auth::guest Returns true if the current user is not logged in (a guest).
if (Auth::guest()) return Redirect::guest('login');
});
Within my views I can now access $account, but if I want a call to URL::route() to be correct I have to manually pass the account variable, like URL::route('clients.show',['account' => $account]) otherwise it generates URLs like %7Baccount%7D.my.app.
This is a bit of a pain and doesn't seem that elegant, is there any other or better way to achieve this? I guess I could create my own route helper to use instead of the built-in one.
However, I also do redirects with Redirect::route() within controllers so I would also need to make updates here.
EDIT
As suggested in the comments it may be that extending the Route API is the best approach here. Does anyone have any suggestions how this should be done?
Thanks.
With laravel we have a few auths setup for us which we can control in the routes file or setup in the controller constructor however I need to find a better way of doing this, and not sure if a route can handle it?
With my users, I allow themselves and the admin to edit them. So my controller looks like this.
function edit($id)
{
$user = User::findOrFail($id);
if(!Auth::user()->isAdmin() && $user->id != Auth::user()->id)
{
return Redirect::route('users.index')->withError('Unable to access that user');
}
return View::make('users.edit', compact('user'));
}
which is ok, but then on my update code I also have to do the same auth/user check to make sure a user or admin can only make changes to themselves.
So we get a double up, in some controllers this is repeated 3 times. Another example of context would be a forum post, the person who posted it or the admin can edit it.
Is there a way with route filters to handle doing this?
This may or may not be of use to you, I have a similar problem in my application where a user can only access their own user profile, but management can access anyone's.
My routes look like:
Route::get('user_profile/{user_id?}', array('as' => 'user_profile', 'uses' => 'UserController#get_user_profile'))->where(array('user_id' => '[0-9]+', 'tab' => '[A-Za-z]+'))->before('auth');
Route::when('user_profile/*', 'management');
This applies a management filter if the user attempts to go to a specific user, it defaults to their own profile if no ID is given.
Route::filter('management', function()
{
if(Helper::managementStatus() === NOT_MANAGEMENT)
return Redirect::route('home')
->with('flash_error', 'You must be logged in as a manager to view this page!');
});
Alternatively you could create a filter something like:
Route::filter('management', function()
{
$user = User::findOrFail(Input::get('user_id'));
if(!Auth::user()->isAdmin() && $user->id != Auth::user()->id)
{
return Redirect::route('users.index')->withError('Unable to access that user');
}
}
And then just apply that filter to the relevant routes.
I hope that's helpful.
I'm new to Zend 2, I started first with laravel and I need something to do that I know Laravel's Route Filter can solve but I'm using Zend 2.
Laravel Route Filter
I checked the documentation of Zend 2 and I can't seem to find it.
I need to do some logging and stuffs only on selected routes and I don't want to add that code on every actions of every routes because I have over 50 different routes here, but in laravel I could make use of route filter so that in selected routes, it will go first in the filter before going to that route.
In laravel's route:
Route::get('route1',array('before'=>'generic','uses'=>'GenericController#getIndex'));
Route::get('route2',array('before'=>'generic','uses'=>'GenericController#getIndex'));
Route::filter('generic', 'RouteFilter');
I have not used Laravel before, but I followed to the link and I am very afraid to say that,
No, it does not exist
You will have use the controller:
public function somethingAction()
{
if (!condition true) {
return $this->notFoundAction();
// or return $this->redirect()->toRoute('MyRoute');
}
// Route is filtered
}
You can also attach a callback to the MvcEvent::EVENT_ROUTE event:
public function(MvcEvent $e)
{
$e->getApplication()->getEventManager()->attach(MvcEvent::EVENT_ROUTE, function(EventInterface $e) {
// check the route and do whatever you like
$matchedRouteName = $event->getRouteMatch()->getMatchedRouteName();
if ($matchedRouteName = 'admin') {// or whatever you want to check
// check if user is admin
}
});
}
Not only MvcEvent::EVENT_ROUTE, there are a lot of events triggered such as MvcEvent::EVENT_DISPATCH. You just need to attach a callback function!
For, full list of all the Mvc Events, view this link!
I am building a restful api in laravel 4 where there are users with different types of permission. I want to restrict access to different routes depending on the user role (which is saved in the user table in db)
How would I do that? Here is what I have so far (it's not working so far).
filters.php
//allows backend api access depending on the user's role once they are logged in
Route::filter('role', function()
{
return Auth::user()->role;
});
routes.php
Route::group(array('before' => 'role'), function($role) {
if($role==1){
Route::get('customer/retrieve/{id}', 'CustomerController#retrieve_single');
Route::post('customer/create', 'CustomerController#create');
Route::put('customer/update/{id}', 'CustomerController#update');
}
});
Is it possible that I'm writing the syntax wrong for a "group filter"?
Try the following:
filters.php
Route::filter('role', function()
{
if ( Auth::user()->role !==1) {
// do something
return Redirect::to('/');
}
});
routes.php
Route::group(array('before' => 'role'), function() {
Route::get('customer/retrieve/{id}', 'CustomerController#retrieve_single');
Route::post('customer/create', 'CustomerController#create');
Route::put('customer/update/{id}', 'CustomerController#update');
});
There are different ways you could implement this, for starts Route filters accept arguments:
Route::filter('role', function($route, $request, $value)
{
//
});
Route::get('someurl', array('before' => 'role:admin', function()
{
//
}));
The above will inject admin in your Route::filter, accessible through the $value parameter.
If you need more complicated filtering you can always use a custom route filter class (check Filter Classes):
http://laravel.com/docs/routing#route-filters
You can also filter within your controller, which provides a better approach when you need to filter based on specific controllers and methods:
http://laravel.com/docs/controllers#controller-filters
Finally you can use something like Sentry2, which provides a complete RBAC solution to use within your project:
https://cartalyst.com/manual/sentry
From laravel 5.1.11 you can use the AuthServiceProvider: https://laravel.com/docs/5.1/authorization