I have a multi-user application with the following entity/model relationship.
User belongs to a Company and has many SupportTicket
Users of one company should not have access to the support tickets of other companies - if a user of one company navigates to a url pointing to a support ticket of another company, they should receive a 404 or something equivalent.
I was hoping to define a global scope in the SupportTicket class as follows:
class SupportTicket extends Model {
protected $fillable = ['company_id'];
public static function boot() {
static::addGlobalScope('ofCompany', function(Builder $builder) {
$authUser = auth()->user();
$builder->where('company_id', $authUser->company_id);
});
}
}
The benefit of a global scope is that it will apply to all queries. Other developers working on the project need not work when retrieving tickets as the right results will always be returned.
For example SupportTicket::all() or SupportTicket::find(5) will always restrict access by user's company.
The challenge I have now is that auth()->user() always returns null as the authenticate middleware has not yet been run.
Is there a way around this?
Has anyone here faced a similar problem and if so how did they go around it?
Is it possible to access the authenticated user in the boot method where the global scope is registered?
Many thanks in advance for any and all responses.
Laravel 5.6.26 introduced Auth::hasUser() to determine if the current user is already authenticated without querying the User model in your database. You can find more details at https://laravel-news.com/laravel-5-6-26
So you could do something like this:
static::addGlobalScope('ofCompany', function(Builder $builder) {
if( auth()->hasUser() ){
$builder->where('company_id', auth()->user()->company_id);
}
});
You can check inside the boot method if there's an authenticated user using \Auth::check() before calling \Auth::user().
If the Auth middleware is wrapping routes or the controller itself then you can access the authenticated user anywhere using the facade \Auth::user()
If you are using Tymon's JWT Auth package, you can get auth user like this
$authUser = \JWTAuth::parseToken()->authenticate();
Related
I currently can't pinpoint which solution is best in the following situation.
I need 2 different accounts namely: Customer and Admin.
However, both account types have different login Credentials i.e.
Admin logs in using email and password.
Customer logs in using username and customerCode.
I am currently using Laravel 8 with Jetstream.
I also created separate user models which both inherit Users Model, which is created automatically by JetStream
Is there a way where I can use different log in credentials for 2 different account types please? If so, what's the best way to go about this.
Thanks in advance.
Set up different auth guards, e.g. users and admin. This way, you can have separate login
https://laravel.com/docs/master/authentication#adding-custom-guards
You can specify which guard to use for relevant parts of your application. This could be done using route middleware, and you can set a default in config/auth.php
e.g.
use Illuminate\Support\Facades\Auth;
Auth::shouldUse('admin');
// or
Auth::guard('admin')->login(...);
// or
Auth::guard('users')->attempt($request->only('username', 'customer_code'));
Auth::guard('admin')->attempt($request->only('email', 'password'));
You can extend FortifyServiceProvider class and in boot method define your logic based on user role. This is a simple example.
public function boot()
{
// authenticate user using email or phone number
Fortify::authenticateUsing(function (Request $request)
{
$user = User::where('email', $request->username)
->orWhere('phone', $request->username)->first();
->orWhere('username', $request->username)->first();
if ($user &&
Hash::check($request->password, $user->password)
) {
return $user;
}
});
}
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 having some trouble with my application when trying to create the user profiles. Here is the issue:
I am trying to create a view called userprofile.blade.php which will output any given users profile (based on id or username...doesn't really matter right now). Each profile page will show name, description, location, profile pic, and the given users posts. I used Laravel's Make:auth command to create the necessary authentication, customized the authentication forms, and then migrated all the columns I needed in my database.
My create and update methods work just fine (registering new users and updating their information). All the information is saved correctly in the database. However, I can only access it in my views with {{Auth::user()->}}. Whenever I try to use the Model in my Controller to access the data I need, it doesn't work. It seems to me as though I need to separate Laravel's default 'User' model with a custom model which I would call 'Account' or something along those lines.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Recipe;
class UserController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth', ['except' => [
'index', 'show'
]]);
}
public function index(){
$user = User::find($id);
return view('user.userprofile')->with('user');
}
I only included the index method from my UserController to keep it simple. It breaks down and tells me that 'id' in $user = User::find($id); is an undefined variable. That tells me that it isn't accessing my database table.
My question is, should I create a new fresh model that isn't mixed up with authentication to handle all the user profile information? If so, how do I access my current database table 'users' from this new model?
Please let me know if I need to clarify things for you guys. I'm not very experienced and I understand if my question is fuzzy. Thanks so much for your time!! I really appreciate any help I can get!
Hello and welcome to developing with Laravel!
You're on the right track - you need to identify which user's profile you're viewing, retrieve it from the database, and pass it to the Blade view. You don't need a new model or anything, though! Just need to complete what you've started.
You should start by defining a route parameter in your route, that will capture the dynamic data you want from the URL. Let's use the numeric ID for now. If you want a URL that looks like example.com/user/439, your route should look something like Route::get('user/{id}', 'UserController#index');.
Once you have that, the id parameter will get passed to your controller's method. Define it as a method parameter, and it'll be usable: public function index($id) { ... }
I think you can take it from there. :)
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.
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