I am using Laravel 5.1. My controller is specifically for admin users. So I check whether user is admin or not.This is my code.
public function getAdminData()
{
$this->checkAdminStatus();
return response()->json(array('admin-data'));
}
public function checkAdminStatus()
{
$userManager = new UserManager();
if(!$userManager->isAdmin())
{
return redirect()->route('returnForbiddenAccess');
}
}
My route is
Route::any('api/app/forbidden',['uses' =>'ErrorController#returnNonAdminErrorStatus','as'=>'returnForbiddenAccess']);
Now if user is not admin, then it should not return admin-data yet it returns. Shouldn't it stop processing logic after redirect()->route call?
Also this is purely REST application.
Why don't you use Laravel Middleware solution for your need ? You can link a middleware to your controller, checking if the current user is an administrator, and redirect if not :
//You Middleware Handle method
public function handle($request, Closure $next)
{
if ($this->auth->guest() || !($this->auth->user()->isAdmin))
{
return redirect('your/url')->with('error','no admin');;
}
return $next($request);
}
You can add on or multiple middleware for a controller in his construct method
//your controller
public function __construct(Guard $auth, Request $request){
$this->middleware('auth', ['except' => ['index', 'show']]); //here one 'auth' middleware
$this->middleware('admin', ['only' => ['index', 'show', 'create','store']]); //here the admin middleware
}
Notice the onlyand except parameters that allow or disallow the middleware for some controller methods
Check laravel documentation on Middleware for more information :)
No, your logic is slightly flawed. The return value you are sending back from checkAdminStatus() is simply being ignored and thrown away:
public function getAdminData()
{
// You don't have $redirectValue in your code, but imagine it
// is there. To actually redirect, you need to return this value
// from your controller method
$redirectValue = $this->checkAdminStatus();
Nothing is being done with that redirect. The only time something is being returned from your controller is happening on every single request. I would suggest something more like this:
public function getAdminData(UserManager $userManager)
{
if($userManager->isAdmin()) {
return response()->json(array('admin-data'));
}
return redirect()->route('forbidden-access');
}
I think this captures the spirit of your question: the only time anything is being returned here is if the user is an admin.
As an aside, you are returning JSON data in one case, and a redirect in another. This may not be a very good idea. My reasoning is because, normally, JSON data is returned in response to AJAX requests, which in my experience are seldom followed up with an actual redirect in the event of failure. (YMMV)
Related
I'm writing a server based solution. In database there are many users with different permissions and I have to check if they have permission to access module they are trying to.
In every Controller I have included something like:
protected $module = "moduleName";
I tried to solve it like:
function __construct()
{
$perm = session()->get('perm');
if (!isset($perm[$this->module]) || !$perm[$this->module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return back();
}
}
It shows the message but it still displays the page not redirects back.
As you see I'm reading permissions from session and modules name is saved in controller so I don't think this could be solved by middleware unless I'm making middleware for each module (I'm talking about 30 modules).
Thanks for taking the time to read this
Middleware actually solved this.
Route:
Route::group(['middleware' => 'module:moduleName'], function () {
// Routes...
});
Custom middleware:
public function handle($request, Closure $next, $module)
{
$perm = session()->get('perm');
if (!isset($perm[$module]) || !$perm[$module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return redirect()->back();
}
return $next($request);
}
Also I'll mention that Route groups can be nested. So you can wrap multiple groups with something like auth middleware as well
There is a very easy fix to your code, you forgot the define the redirect, so instead of using
return back();
use
return redirect()->back();
This will do the redirect.
There is also a mistake in your reasoning, you could and probably should use middleware.
Middleware does have access to the user, session and can be passed parameters. These are the necessary requirements for your system.
You can also assign middleware on a controller basis.
Everytime before I call the function in laravel, I check if the person who requests the ajax link is logged in.
I have to put this check function inside every function to make it work. if I write outside the function it simply prints
please login to see this content work.
even though I am logged in. Is there a way to put the check function in the beginning of the controller file so I don't have to write it it everytime inside the functions.
function index()
if(!\Auth::check())
return response()->json(array(
'status'=>'error',
'message'=> 'Please login to see this content'
));
return redirect('/login')->with('msgstatus', 'error')->with('messagetext','Please login to see this content'); }
function loadcalendar()
if(!\Auth::check())
return response()->json(array(
'status'=>'error',
'message'=> 'Please login to see this content'
));
//somedefinition
}
function savecalendar()
if(!\Auth::check())
return response()->json(array(
'status'=>'error',
'message'=> 'Please login to see this content'
));
//somedefinition
}
Sounds like you are looking for a middleware.
Out of the box laravel has a auth middleware which checks if a user is logged in. To protect specific routes on your controller simply add this to your route:
Route::get('/', 'YourController#index')->middleware('auth');
or define it in the constructor or the controller:
public function __construct()
{
$this->middleware('auth');
}
When using the constructor method, you can also specify for which method the middleware should be checked.
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
See Controller Middleware in the documentation.
From the Laravel Docs:
Even though it is possible to determine if a user is authenticated
using the check method, you will typically use a middleware to verify
that the user is authenticated before allowing the user access to
certain routes / controllers. To learn more about this, check out the
documentation on protecting routes.
https://laravel.com/docs/5.4/authentication#protecting-routes
There is build-in middleware that does this:
Defining:
public function __construct()
{
$this->middleware('auth');
}
in your controller for example will check every method on that controller against the auth middleware.
Yes. You may want construct here:
public function __construct()
{
$this->middleware('auth');
}
Every function requires authentication now.
In your controller,
add this constructor method. This will use the built in Auth middleware and redirect any non logged in users to login page
public function __construct()
{
$this->middleware('auth');
}
I am today in bit confusion about my website security and some extra code that is written to make the website secure. Below are 2 locations where security is applied.
Inside Route Config, To secure the route, I have used Middleware to check the user role.
Route::group(['middleware' => ['web', 'SuperAdmin', 'auth']], function () {
Route::get('/Create-Department', 'DepartmentController#CreateDepartment');
});
I mentioned 2 Middlewares.
Auth Middleware : This is for authentication.
SuperAdmin Middleware: This is for Authorization.
Second location is Request class. Here is the code. In authorize method, again same thing is being checked as already done in route
class DepartmentRequest extends Request
{
public function authorize()
{
if(\Auth::user() == null) {
return false;
}
if(\Auth::user()->isSuperAdmin()) {
return true;
}
return false;
}
public function rules()
{
return [
'Department' => 'required',
];
}
}
Question: Should I remove check in Request class? Is that an unwanted validation to secure the request ? As route.config is already doing the job.
What's the use of authorize method? I meant, I am using Request class to validate form inputs. Should I always return true from authorize method?
yes, you should remove that checks in the Request class: if you're already doing that checks in your middleware you should not repeat them
When you specify this:
Route::group(['middleware' => ['web', 'SuperAdmin']], function () {
Route::get('/Create-Department', 'DepartmentController#CreateDepartment');
});
You're telling laravel that, when it finds a /Create-Department route, it should trigger the handle method of these middleware: ['web', 'SuperAdmin'], before the request is sent to the DepartmentController
So, if you check for authentication and authorization in the middlewares, when the request will get to your controller you're sure that it has satisfied all the middleware it went through
Regarding the purpose of the authorize method: the authorize method is usually used to authorize the actual request basing on some policy you'd like to respect. For example, if you have a request to edit a Post model, in the authorize method you'd check that the specific user trying to edit the post has the permissions to do it (for example being the author of the post )
EDIT
Even if you want to use a middleware for your authorization, it's fine. Anyhow, usually the authorize method within form requests is used to do authorization checks on the specific request.
For instance check this example from the docs :
public function authorize()
{
$postId = $this->route('post');
//here the authorization to edit the post is checked through the Gate facade
return Gate::allows('update', Post::findOrFail($postId));
}
In conclusion: if you're doing your authentication and authorization tasks in middlewares, you don't need to repeat them in the authorize method, but keep in mind that the native purpose of the method is to authorize the specific request
I am looking to redirect after login to a page with url /dashboard/start
My routes.php contains the following route:
Route::get('/dashboard/start', ['uses' => 'Settings\OrganisationController#index', 'as' => 'app.home']);
In Laravel Auth process you can implement a login redirect override by adding a declaration at the start of the app\Http\Controllers\Auth\AuthController controller like so:
protected $redirectPath = '/dashboard/start';
I want to use named routes throughout the code so that if I change the url in the routes file, it does not impact the code so long as the name has not changed.
I tried this but is fails:
protected $redirectPath = route('app.home');
I cannot find an example nor a mention of this. Any ideas? Thanks!
You have a few options. You could override the redirectPath method on the controller.
protected $redirectPath = 'app.home';
public function redirectPath()
{
return route($this->redirectPath);
}
You also could make an authenticated method. The handleUserWasAuthenticated method (that gets called when Auth::attempt is successful) checks for existence of the authenticated method and if it exists it will call it and return the results instead of the normal redirect()->intended($this->redirectPath()). This method would receive the current request and authed user.
How handleUserWasAuthenticated calls authenticated:
return $this->authenticated($request, Auth::user());
You can redirect many way . you can redirect by route .
write your routes.php
Route::get('first-route',['uses'=>'TestController#first','as'=>'first']);
Route::get('second-route',['as'=>'second','uses'=>'TestController#second']);
and write your controller
public function first(){
return "This is a simple Test Controller";
}
public function second(){
return redirect()->route('first');
}
and you can use action method like this
public function second(){
return redirect()->action('TestController#first');
}
or
public function second(){
$url=action('TestController#first');
return redirect($url);
}
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.