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.
Related
I'm using Laravel 5.5 and I'm trying to use Gate facade to allow admins to access resources like users. First, I define a gate in AuthServiceProvider.php like following:
Gate::define('view-users', 'App\Policies\UserPolicy#view');
Then, I write view method in Policy class like this:
public function view(Admin $admin, User $user)
{
return true;
}
And, I apply the authorization like following:
//UsersController.php
$user = User::first();
if (Gate::allows('view-users', $user)) {
$users = User::all();
return view('admin.users.list', compact('users'));
}
return abort(403);
I note that, the $user argument is useless variable and I don't need it to perform authorization.
By the way, when I use allows() method of Gate facade, it always returns false. While, when I use denies() instead, these steps work fine.
what's wrong with allows() method?!
However, corresponding to the Laravel Docs, I tested other ways to apply authorization via middleware(), Model or authorize(). But, I got the same result.
Edit:
I should note that I'm using custom guard named web_admin
Thanks for any help.
Change your policy method to this:
public function view(User $user)
{
return $user->isAdmin;
}
The first argument of the policy method is always the current authenticated user. Note that you are not required to pass the currently authenticated user to these methods. Laravel will automatically take care of passing the user into the gate Closure:
if (Gate::allows('view-users')) {
// The current user can view all users...
}
If you want to check if the current user can update a specific user your policy method would be:
public function update(User $authenticatedUser, User $beeingEditedUser)
{
return $authenticatedUser->isAdmin;
}
Then authorize like this:
if (Gate::allows('update-user', $beeingEditedUser)) {
// The current user can update the user...
}
If you're using custom guard (according to your comment), you may have 2 options:
Use forUser method:
use Illuminate\Support\Facades\Auth;
if (Gate::forUser(Auth::guard('web_admin')->user())->allows('view-users')) {
// The current user can view all users...
}
Protecting the routes, specifying the guard:
Route::middleware('auth:web_admin')->group(function () {
Route::get('/users', 'UserController#index');
});
This causes Larvel to set your default auth driver and resolve the auth user based on your custom guard.
I am trying to make a Laravel website that uses
Laravel's Auth package. I'm using Laravel 5.3.2.
I have created a field in user table called role.
Now I want to know how to check the users role during the authentication process and then redirect to a required view based on the role.
Please help me figure out how this would be possible.
Thank you very much in advance.
When a user logs in, this is done through your LoginController.php which is located at app\Http\Controllers\Auth
This controller uses a trait called AuthenticatesUsers.
This trait has a method called authenticated() which by default is empty. This method is called if it's not empty by the trait - after all the necessary loggin in stuff has been done.
You could override this method in your AuthenticationController.php and add the functionality you are asking for. An example would be:
// You actually get an Auth\User object passed to you by the trait!
public function authenticated(Request $request, $user)
{
if($user->role == 'admin') {
// You could do anything here
return redirect()->route('admin-dashboard');
} else {
return redirect()->route('home');
}
}
Beside solution overriding some default Laravel method. I suggest an other approach: redirect user to a route which is responsible for redirect user base on user's role
In AuthController
protected $redirectTo = '/redirect';
In routes
Route::get('redirect', function(){
switch(auth()->user()->role){
case 1:
return redirect()->to();
break;
case 2:
return redirect()->to();
break;
}
})
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
I'm building an API for user and admin.
Got stuck at edit user profile routing.
on admin route i use Route::resource('user', 'UserController')
on user route i use Route::get('profile', 'UserController#show')
At the show method Laravel default has
public function show($id)
{
}
the different between them is on admin I can use /id but on user i check their token from middleware and merge the request to get their user_id so there is no need for the API to use profile/{id}.
The question is how can I use the same method but there is an argument to fill and the route still /profile?
One of my solution is :
public function show($id){
if ($request->has('user_id')):
$id = $request->query('user_id');
endif;
}
It working but when i read the code, it's really redundant always checking it and replace the id.
Just place the request object as a parameter in your controller and get the input from the request object when you use your user route.
Thanks
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