HttpException in Handler.php line 107: This action is unauthorized - laravel-5

I'm learning Laravel 5. I have finished the document's Quickstart - intermediate. I want to apply the authorize check for Task's actions to the User's. I want to check whether the target user is the current logged in user in order to use user's edit action. However, browser keeps telling me when I try to access http://myfirst.app/users/2/edit:
FatalThrowableError in UsersPolicy.php line 20:
Type error: Argument 1 passed to App\Policies\UsersPolicy::edit() must be an instance of Illuminate\Http\Request, instance of App\User given
Routes.php
Route::get('/users/{user}', 'UsersController#view');
Route::get('/users/{user}/edit', 'UsersController#edit');
Route::patch('/users/{user}', 'UsersController#update');
AuthServiceProvider.php
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\Task' => 'App\Policies\TaskPolicy',
'App\User' => 'App\Policies\UsersPolicy',
];
UsersPolicy.php
namespace App\Policies;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Auth\Access\HandlesAuthorization;
class UsersPolicy
{
use HandlesAuthorization;
public function edit(Request $request, User $user)
{
return $request->user()->id === $user->id;
}
public function update(Request $request, User $user)
{
return $request->user()->id === $user->id;
}
}
UsersController.php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UsersController extends Controller
{
protected $user;
public function __construct() {
$this->middleware('auth');
}
public function view(Request $request, User $user)
{
if($request->user()->id == $user->id){
return view('users.view', ['user' => $user]);
}
return redirect('/tasks');
}
public function edit(Request $request, User $user)
{
$this->authorize('edit', $user);
return view('users.edit', compact('user'));
}
public function update(Request $request, User $user)
{
$this->authorize('update', $user);
$user->update($request->all());
return redirect('/users/'.$user->id);
}
}
In the Document's TaskController's delete function, $user isn't passed into $this->authorized('destroy', $task) in order to allow TaskPolicy's destroy function to use $user:
TaskController.php
public function destroy(Task $task)
{
$this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}
TaskPolicy.php
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
Anyway, I followed the exception and added $request to UsersController's edit function's parameter
$this->authorize('edit', $request, $user);
And I get
HttpException in Handler.php line 107:
This action is unauthorized.
What should I do?

In your Request file set
public function authorize()
{
return true;
}

Try this: in UsersPolicy.php add:
enter code here/**
* #var User
*/
protected $user;
/**
* Create a new policy instance.
*
* #param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
And in your UsersController.php change $this->authorize('edit', $user); to $this->authorize('edit');
Hope that helps

as per documentation: "The Gate will automatically return false for all abilities when there is not an authenticated user". So before doing any authorization, please check Auth::user() if it returns a currently authenticated user.

Related

Laravel generates error while sending lists of posts to users

RegistrationController.php
use App\User;
use App\Post;
use App\Notifications\LatestPosts;
use App\Notifications\WelcomeEmail:
public function store()
{
auth()->login($user);
$allUsers = User::latest()->get();
$posts = Post::latest()->get();
$user->notify(new WelcomeEmail($user));
$allUsers->notify(new LatestPosts($posts));
return redirect(‘/dashboard’);
}
WelcomeEmail.php
use App\User;
class WelcomeEmail extends Notification
{
use Queueable:
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function toMail($notifiable)
{
$user = $this->user;
return (new MailMessage)
->subject(‘Thanks for registering’)
->markdown(‘emails.newusers.welcome’, compact(‘user’));
}
}
LatestPosts.php
use App\Post;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Post $posts)
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject(‘Latest posts for you’)
->markdown(‘emails.posts.latestposts’, compact(‘posts’));
}
}
New users register successfully, welcome email is sent successfully but it gives me this error for sending latest posts to users.
Argument 1 passed to App\Notifications\LatestPosts::__construct() must be an instance of App\Post, instance of Illuminate\Database\Eloquent\Collection given
Basically, I want to send a list of posts to all users (I know it’s not efficient to send it while new users register but just want to see how it will work out even if I send it while new users register) Someone please help me out in this. What do I do? Thanks in advance.
In registration controller
use App\User;
use App\Post;
use App\Notifications\LatestPosts;
use App\Notifications\WelcomeEmail:
public function store()
{
auth()->login($user);
$allUsers = User::latest()->get();
$posts = Post::latest()->get();
$user->notify(new WelcomeEmail($user));
foreach($allUsers as $u){
$u->notify(new LatestPosts($posts));
}
return redirect(‘/dashboard’);
}
LatestPost
use App\Post;
use Illuminate\Database\Eloquent\Collection;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Collection $posts)
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject(‘Latest posts for you’)
->markdown(‘emails.posts.latestposts’, compact(‘posts’));
}
}
You should change the signature of your constructor:
use App\Post;
use Illuminate\Database\Eloquent\Collection;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Collection $posts) // use `Collection`, not `Post`
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject('Latest posts for you')
->markdown('emails.posts.latestposts', compact('posts'));
}
}

Laravel: How the best way for redirect a default laravel user to admin page if user is admin or to user page if user is not admin?

The User model has an isAdmin() function to check if the user is an administrator. What to do next?
The best way is to use default laravel LoginController located under App\Http\Controllers\Auth\LoginController.
In that controller you can override authenticated method that is injected from AuthenticatesUsers trait, by simply adding that method in LoginController:
* #param Request $request
* #param $user
*/
protected function authenticated(Request $request, $user)
{
if ($user->isAdmin()) {
return redirect(route('admin-dashboard'));
//redirect to desired place since user is admin.
}
}
Best practique is whit roles, and you add role on your Routes::middleware,
Route::group(['middleware' => ['auth', 'roles:admin']], function () {
//Your routes
});
Kernel.php
'roles' => Middleware\CheckRole::class,
Create middleware
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
public function handle($request, Closure $next, ...$role)
{
if ($request->user()->hasAnyRole($role)) {
return $next($request);
}
return redirect(route('hour'));
}
}
create function on User model
public function authorizeRole($role)
{
if ($this->hasAnyRole($role)) {
return true;
}
return abort(401, 'Unauthorized.');
}
public function hasAnyRole($roles)
{
if (is_array($roles)) {
foreach ($roles as $role) {
if ($this->hasRole($role)) {
return true;
}
}
} else {
if ($this->hasRole($roles)) {
return true;
}
}
return false;
}
public function hasRole($role)
{
if ($this->role()->where('name', $role)->first()) {
return true;
}
return false;
}
public function role()
{
return $this->belongsTo('App\Role')->withDefault();
}
And Role model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function user()
{
return $this->hasMany('App\User');
}
}
Is more code, but best way for this action

How to get current user id in constructor in laravel?

I am using laravel 5.7, but i can't get current user id in __construct().
I also tried Auth:id(), but it also not working.
How to get current user id in constructor?
use Illuminate\Support\Facades\Auth;
class TestController extends Controller
{
public $id;
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$this->id = Auth::user()->id;
return $next($request);
});
dd($this->id);
}
}
Current output is null.
You can only access the session in the closure. Just refactor your code to this:
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$this->id = Auth::user()->id;
dd($this->id);
return $next($request);
});
}
You can now use the value $this->id in your controller methods.
In the example in your question, after you've set the value $this->id, you continue with the request. Since you try to access $this->id outside of the scope of the closure, it still is null in the datadump.
After return you will not go to next statement that's why it is not print.
If you want to use this in view then no need to pass in view you can simply access logged user id like this
{{Auth->user->id}}
if you wan to use this in controller make sure you are logged in.
Sometime session expired then you will not get user id
use Illuminate\Support\Facades\Auth;
class TestController extends Controller
{
public $id;
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$this->id = Auth::user()->id;
dd($this->id);
return $next($request);
});
}
}
The easiest solution is to create a middleware and call it later in the constructor.
php artisan make:middleware FoobarMiddleware
I recommend putting an alias in Kernel.php
protected $routeMiddleware = [
...
'foobar' => \App\Http\Middleware\FoobarMiddleware::class,
]
Constructor:
public function __construct()
{
$this->middleware('auth');
$this->middleware('foobar');
}
I recommend changing the focus of how you are creating everything

Laravel | Auth::user()->id isn't working in AppServiceProvider

I can get the Auth ID when i put it in any controller with
Auth::user()->id
But when i put it in AppServiceProvider.php , it returns `Trying to get property 'id' of non-object
i don't understand why ?
Eddit : I tried this but still not working
public function boot()
{
view()->composer('*', function ($view)
{
if (Auth::check())
{
$id=Auth::user()->id;
$idd=Person::where('user_id','=',$id)->get('photo');
$view->with('idd', $idd );
$view->with('id', $id );
}
});
}
Error :
Argument 1 passed to Illuminate\Database\Grammar::columnize() must be of the type array, string given, called in
To get the currently authenticated user's ID, use
Auth::id();
Another case may be that there is not a current user, in which case Auth::user() is returning NULL. Wrap the code in a
if (Auth::check())
{
// Do stuff
}
to make sure there is a user logged in.
view()->composer('*', function($view)
{$view->with('user',auth()->user());
});
it's work for me
<?php
namespace Fitness\Providers;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot(Request $request)
{
view()->share('user', $request->user());
}
}

Laravel 5.2 lock controller for some users

I want to lock AccountsController for all the users expect users for admin users..
For Exampe:
Auth::user() -> roll != 'Admin' then close the AccountsController..
AccountsController Construct Code:
public function __construct()
{
$this->middleware('auth');
}
Middleware is a completely valid solution for this, but I have switched to Gates for this type of situation. Gate is a bit more convenient to use. I use roles and permissions and a hasRole method to manage the level of access, but if your system is simple you can easily just have a isAdmin method on your User model that checks a flag in the database.
Middleware
Create you custome middleware.
AuthenticateAdmin.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class AuthenticateAdmin {
protected $auth;
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
public function handle($request, Closure $next)
{
if ($this->auth->user()->hasRole('admin'))
{
return $next($request);
}
}
}
Add that to your Kernel.
Kernel.php
protected $routeMiddleware = [
'auth' => Middleware\Authenticate::class,
'auth.admin' => Middleware\AuthenticateAdmin::class,
];
Then you can use the middleware in your controllers.
public function __construct()
{
$this->middleware('auth.admin');
}
Gate
With Gate you add you define your policy in AuthServiceProvider
AuthServiceProvider.php
public function boot(GateContract $gate)
{
parent::registerPolicies($gate);
$gate->define('user-admin', function($user){
return $user->hasRole('admin');
});
}
Then add it to your controller or where ever you need it.
Controller.php
public function show($slug)
{
if (Gate::allows('user-admin')){
return $yes;
}
return $no;
}

Resources