Laravel - Implicit route model binding with soft deleted data - php

I am having a small issue. There are two user roles and one is a normal member and one is an admin. The member can delete a blog and they will not be able to see the blog after they delete (soft delete) it while the admin can still see the blog, even if it's soft deleted.
Example code:
// Route file
Route::get('/blog/{blog}', 'BlogController#show');
// BlogController
public function show(App\Blog $blog) {
// It never gets to here if the blog has been soft deleted...
// Automatically throws an 404 exception
}
I want the admin to be able to visit the blog even if it's soft deleted but it doesn't really work. I am trying to edit the route service provider but I haven't gotten any luck as it doesn't let me use the Auth::user() function to get the logged in user so I can check if they have permission.
My RouteServiceProvider
$router->bind('post', function($post) {
if (Auth::user()->isAdmin()
return Post::withTrashed()->where('id', $post)->firstOrFail();
});
This does not work as it doesn't know what Auth::user() is. I have imported Auth facade but still doesn't work.
Edit: It gives me a null value when I dump and die Auth::user().
Any help is highly appreciated.

As this question pop ups when googling for this, in newer Laravel versions you can do this:
Route::get('posts/{post}', [PostController::class, 'show'])->withTrashed();
See: Implicit Soft Deleted Models

I just found out that getting the current logged in user is not possible in Route Service Provider because it loads before all session service provider.
Instead I simply did:
//Route Service Provider
$router->bind('post', function($post)
return Post::withTrashed()->where('id', $post)->firstOrFail();
});
// Controller
public function show(Post $post) {
// If the post has been trashed and the user is not admin, he cannot see it
if (!Auth::user()->isAdmin() && $post->trashed())
abort(404);
// Proceed with normal request because the post has not been deleted.
}

Related

Laravel 8 Auth middleware protected route failing

I am building my first Laravel app with the Metronic 8 Laravel theme. It uses Breeze for authentication. I changed a couple of things around - created a welcome page for non-logged-in users, and moved the main template that was the index to an auth protected "/dashboard". The problem is that it still tries to load the dashboard Blade template, regardless of authentication, resulting in an error.
Route
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
Here's Authenticate, where it should redirect non-authenticated users to the login page.
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
When I'm not logged in and navigate to the dashboard URL, it attempts to load the dashboard Blade template, which calls a menu function that checks the user permissions for menu items. Unfortunately, since there is no user, the application blows up from passing a null value to a method expecting a user array/object.
Any ideas on where to look for the problem? It seems to me that the auth middleware should redirect to the login page before trying to load the Blade template when not logged in.
I would put the middleware at the beginning of the route like this, though I'm sure it's not causing the problem-
Route::middleware(['auth'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Aside from that, please provide some information on the error itself like what the error is about/what is says..etc...
First of all, make sure you have a login named route defined in your routes/web.php file. It should look something like:
Route::get('/login', '<controller>#<method>')->name('login');
The important bit is ->name('login') so that the Authenticate middleware can correctly identify the route to redirect to. Change <controller>#<method> appropriately to route to the login method of your app.
Wakil's answer is irrelevant and actually opposite of the documentation. Your syntax is correct.
I figured out the issue. Keen Themes put a call to a method to build an array of menu items in the web routes file. That was making the call to the offending code. After I wrapped that in an auth check the error was fixed, and everything works as expected.

Pass current user to route in Laravel?

I'm trying to make a "Profile settings" section in an application.
The thing is, I learned how to do this the "Admin" way, the route would be /users/{user}/edit, the would call the edit method on the controller and it would return the edit view. There I would have a form which the user would patch to the route users/{user} and it would call the update method on the controller.
But I don't want anyone editing other users, so I'd like to know if there's a way to limit this route to the current user only.
Thanks in advance.
Since version 5.1 Laravel has Policies which are exactly what you need.
You can create a new policy by typing in command:
php artisan make:policy UserPolicy
In your UserPolicy class you can include the following method:
public function updateProfile(User $user, User $updatedUser) {
return $user->id === $updatedUser->id;
}
Please note: The first parameter $user is resolved automatically behind the scenes and is the currently logged in user. When checking the policy through the Gate facade in your application you need to pass only the second parameter $updatedUser.
Then you need to register your policy in the AuthServiceProvider:
use Acme\User;
use Acme\Policies\UserPolicy;
...
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
User::class => UserPolicy::class
]
Now when you have your policy registered you can check it across your app using the Gate facade like so:
if(Gate::allows('updateProfile', $user)) {
// Your logic goes here
}
Or the other approach with I like more using the denies method and include it at the beginning of my controller methods and return http error:
public function edit($id) {
if(Gate::denies('updateProfile', $user)) {
abort(403, 'You do not have permissions to access this page!');
}
// The check is passed and your can include your logic
}
You can also check for permissions in your blade files using can and cannot like so:
#can('updateProfile', $user)
// Show something only to the user that can edit the $user's profile
#endcan
For more info check the docs.
you should not need to pass in the user id as there user is already logged in and there for should be able to edit themselves, thus only targetting the logged in user.
So you can use the routes /user/editand /user/updateetc
Just get the current user details like
Auth::user()->id
or something else like
$user = Auth::user();
Thus only the logged in user (themselves) can be edited.
In the view there should be a button or link, on click pass the ID to the desired route that's it.
Example:
For Grabbing the current logged in User id you should do like
$user = Auth::user()->id;
And directly in the route you can get it like
Edit
Now when someone clicks on the Edit Button/Link, the route will look like route/currentuserid.

Laravel: simplest/right way to save a new step after login?

I'm having my first interaction with the core laravel code so I want to be careful not to break anything.
In my project, my users also correspond to person records (via user->person_id), so I have a get_person_from_user() function that takes the \Auth::user() (conveniently accessible anywhere) and returns the person object, so I can grab the person record for the authenticated user from any controller and pass it to a view.
The problem: there's a piece of data from the person record that I'd like to include in a nav partial in my default blade view (which gets extended by a bunch of different views), so it's the one case where I'm not going through a controller first. I'm unclear on how I can make the logged in user's person record available here. Any suggestions?
I think I need to add some step after the user logs in, to save the person record (globally? in the session?) so it's generally accessible. The login stuff happens in AuthenticatesUsers.php, and reading around it sounds like I'll want to add an override of postLogin to my AuthController.
But I tried copying that function from AuthenticatesUsers.php into my AuthController (not adding anything else to it yet), and AuthController gives me a new error when I try to log in:
ReflectionException in RouteDependencyResolverTrait.php line 81:
Class App\Http\Controllers\Auth\Request does not exist
Any advice on a good way to go about accessing the person object for the authenticated user, when I don't have a controller to pass it along?
You can setup the correct relationship on the User model to Person model.
public function person()
{
return $this->belongsTo(Person::class);
}
Then you can do:
Auth::user()->person;
For having a variable available to a particular view you can use a View Composer. (You can create and register a Service Provider and add this to the register method.) Potentially something like this:
view()->composer('someview', function ($view) {
if ($user = Auth::user()) {
$somevar = $user->person->somevar;
} else {
$somevar = null; // or some default
}
$view->with('somevar', $somevar);
});
If this view is also used in a scenario where someone doesn't have to be authed you will want to check if Auth::user() is null before trying to use the relationship.
Laravel Docs - Views - View Composers
I suggest you to use Eloquent relation
User.php
public function person()
{
return $this->belongsTo('NAMESPACE_TO_YOUR_MODEL\Person'); //also you can specify FK, more info in docs
}
then you can access Auth facade in your view
Auth::user()->person

Laravel 5 Authorize User to see certain Buttons

I just started learning Laravel 5 and came across a problem i could't solve:
How to tell that a certain user can only see certain things.
For example if i view a profile which is not my own, the "Edit Profile" button should not be visible. But if i look at my own profile this button should be visible.
What i already got is to authorize certain requests. For example to authorize a user to actually update a profile:
public function updateProfile(Profile $profile, UpdateProfile $request){
//update the given profile
}
So UpdateProfile is here a Request Class which has a authorize() and a rule() method and in the authorize() Method i check if the logged User is updating his own profile.
So i thought maybe i can use the authorize() method on its own, but i am not really sure how.
Now of course i could always check sth like:
if($user -> userID == Auth::user() -> userID)
But what if i need to check sth more complex, for example when i write a post and want to show a delete button for that post i want to check:
Is the user admin, if not is the user writer of that post, if any of this is true, show delete button.
So my question would be, where would i check sth like this in laravel 5?
You could write a userCanEdit method on your Post class. Something like this:
function userCanEdit(User $user)
{
return $user->isAdmin() || $this->user_id == $user->id;
}
And then just call it on your view:
#if ($post->userCanEdit(Auth::user()))
Edit
#endif
The advantage of this is that you keep your view clean and centralize the business logic in a single, reusable, method. If the definitions for a user who can edit a post ever change, that is the only place you'll have to worry about.
So my question would be, where would i check sth like this in laravel 5?
In your views. For example, let's assume that you are loading a blog content like this:
// controller
public function showPost(Post $post){
return view('views.post', ['post' => $post]);
}
In the view, we want that only the author make changes to it.
// post view
<h2>{{ $post->getTitle() }}</h2>
#if ($post->getAuthor()->getId() === Auth::user()->getId())
Edit
#endif
As you can see above, if the author is the same user as the authenticated, then she/he can see an editing link for this post.

How can I set a specific method of aLaravel 4 resource route in my ACL

I have a fairly simple ACL system set up. The filter checks if the user is part of a user group which has access to the route or if the user has access to the route. It works for individual routes and works for resources in general. However I want some users to have access to a specific method of a resource route, but not all of it. for example, user1 is part of the admin group and always has access to the admin resource route but user2 is not a part of the admin user group and I want to give him access to teh resource admin/create. How can I go about this in Laravel 4 with my setup
Database
Routes:
id
route
created_by
last_editted_by
created
updated
deleted_at
acl (table it looks at to see if user has access)
id
routes_id
user_id
group_id
created
updated
deleted_at
Filter
if (Auth::check()){
$route = Request::segment(1);
$user_id = Auth::user()->id;
$acl_count = Acls::join('routes','routes.id','=','acl.routes_id')
->where('routes.route','=',$route)
->Where(function($in_parenthesis) use($user_id){
$in_parenthesis->whereIn('acl.group_id',function($where_in) use($user_id){
$where_in->select('group_id')
->from('user_group_junction')
->where('user_id','=',$user_id);
})
->orWhere('acl.user_id','=',$user_id);
})
->count();
if($acl_count < 1){
return Redirect::to('/');
}
}else{
return Redirect::to('/');
}
Routes
Route::get('/','HomeController#index');
Route::get('login','AuthorizationController#loginForm');
Route::post('authenticate','HomeController#authenticate');
Route::get('logout','HomeController#logout');
Route::group(array('before'=>'auth'),function(){
Route::group(array('before'=>'user_permission'),function(){
Route::get('protected','HomeController#protectedPage');
Route::resource('sources', 'SourcesController');
Route::resource('admins', 'AdminsController');
});
});
You can use beforeFilter inside the __construct method of the AdminBaseController like this (create a different one for admin controllers only)
class AdminController extends AdminBaseController {
function __construct() {
// Use filter on all methods but not on create
$this->beforeFilter('admin', array('except' => array('create')));
}
}
Also, you may directly use this beforeFilter inside your resource controller and use except or you can use only (only works reverse of except, allows access to all but filters only mentioned methods in the array). You can also check conditions inside the constructor method s well.
I figured out that part of my problem is with the filter. I am only looking at the first segment of the url which doesn't work correctly if my route (whether it is a resource or just a route with a "/" in it) won't work. Thus I asked another question located here

Categories