How To: A Laravel Auth User Provider that finds users indirectly - laravel

My Laravel project involves businesses and their employees. The main Eloquent models are Businesses, Users and Roles. A User can have multiple Roles, each with a different Business.
When it comes to authentication, the Laravel Auth setup is a good fit, with one exception: A User does not have an email address. Instead, each of their Roles has an email address, and the user can log in using any one of these.
My user object has the password and remember_token fields. It seems to me that this object should still implement the Illuminate\Contracts\Auth\Authenticatable interface, and that it should do so by importing the Illuminate\Auth\Authenticatable trait. Does this sound right?
When it comes to the Illuminate\Contracts\Auth\UserProvider interface:
I think that I can extend the Eloquent provider implementation, Illuminate\Auth\EloquentUserProvider, and override only the retrieveByCredentials() method, but I am not sure. Will I need to override other methods as well?
If I do extend the Eloquent provider implementation, how do I go about injecting the $hasher and $model arguments when I register the new user provider in the boot() method of my AuthServiceProvider. Where do these values come from (see below)?
public function boot()
{
$this->registerPolicies();
Auth::provider('role', function ($app) {
$hasher = ''; // ????
$model = ''; // ???
return new UserViaRoleProvider($hasher, $model);
});
}

After some experimentation, I found that it was easier to create my own implementation of the UserProvider interface. The only method which required a bit of thought was retrieveByCredentials(). It searches for the Role, then returns the related user.
Although I was successful in logging in, I ultimately abandoned the approach because there are other parts of the Auth system that assume there is a 1-to-1 relationship between user and email. For example, the Password Reset functionality.

Related

How to make polymorph registration in Laravel?

The design system provides three types of accounts: client, carrier, seller.
Each one has own set of fields like name, direction, minprice.
Here is a register controller with register method.
public function register(Request $request) {}
Should I split that method on three methods by account type?
Another way is create a three services and factory.
The account type must have own validations rules, model etc.
Which way to choose to be flexible?

Does Cache work in an API route and should we use it?

I am creating an API. In this API I am accessing a (permissions) table from a database multiple times, in middleware as well as in controllers. I was thinking, instead of accessing the database multiple times, why don't I call it once and use it multiple times. After calling it once, I could store it in the cache within a service provider. But I am not sure if it is a good way to go because API routes don't load all the services like session.
There are other ways like storing data into the config. Or create a class and make a facade for it and then call it when ever it is needed. But I am curious if the cache would work in API routes and would it be a good idea?
Okay with the advice of #lagbox I created a dead simple class.
namespace App\Helpers;
use App\Permission;
class Provide
{
public $permissions = [];
function __construct() {
$this->permissions = Permission::whereNotNull('route_name')->get();
}
}
This may vary, it's just a class that will keep some collection data in it. I named it provide to keep it generic, just in case that I could need other data than permission in the future. Of course this class could be more detailed but just for storing and returning permissions it is enough.
Then I bound it as a singleton in my AppServiceProvider to run it only once.
public function register()
{
$this->app->singleton('App\Helpers\Provide', function ($app) {
return new \App\Helpers\Provide();
});
}
and when I need it I call it like
$provide->permissions->toArray()
All the features of the collection are available everywhere from the beginning to the end. Yes that may look like an overkill or an abuse of IoC but this über simple approach is in my case a superb solution.

Are there any methods in Laravel that automatically add a model to an authenticated user?

I find myself doing this frequently:
$model→user()→save(Auth::user());
This requires importing the Auth facade into every controllers where the relationship is being saved.
I was thinking of possibly create a simple method (saveWithAuthUser()) under User model to save the authenticated user.
Just wondering if this already exists as a Laravel method already or not, and if not, is this a pointless idea?
This requires importing the Auth facade into every controllers where
the relationship is being saved.
You can avoid this by getting the user from the Request instead.
public function create(Request $request) {
$model->user()->associate($request->user());
}
There is also a global helper for exactly this.
$user = auth()->user();

Treat Laravel multi auth users as a unique user class

I have been struggling with this problem for couple of days and I've searched everywhere but couldn't find a logical solution.
I need multiple types of users in my project(admin, customer) because I need completely different backend logic for each type. So I decided to use multi-auth method in laravel(which AFAIK is the best solution for these cases). So I have multiple user classes for each type(and multiple tables in DB) including Admin and User classes. AdminAuth and UserAuth classes manage the Login and Register logic and routes are handled using middlewares.
Up till now there is no problem. The problem is that I need to use a single user class in another classes. For example consider the messaging logic(and there are many many similar use cases):
a Message class Model should have:
protected $fillable = [
'from_id', 'to_id', 'content', 'state'
];
public function sender(){
return $this->belongsTo(**User::class**);
}
public function receiver(){
return $this->belongsTo(**User::class**);
}
...
In the above model, I need to specify the User::class for senders and receivers, which can be either admins or users. So how can I tell Eloquent to use both models. Is it even possible? If not, what is the solution here?
I thought of using a higher level class named Person, for example, to hold the Admin or User object instances, but this way ORM can't manage to retrieve or store users from/in the appropriate tables automatically.
Any Suggestion is greatly appreciated.
I would advise you to use the following guidelines to handle such functionality; create a model for each user type but all of them should have a relationship with Laravel's default user class by keeping the user's id. Also, keep general properties in the user class and specific properties in each sub class, like customer's can have addresses and admins can have phone numbers, while the common things like the username can be kept in the user model. Then you won't need multiply forms for login, when a user logs in, you redirectly accordingly to the user's type in the default user record. Now for your messaging problem, use user the default user model to establish the relationship in messages as you shown above. Then defenping on the user's type, grant him different priviledges or features in the chat.

Authorization Policy without model

I need to authorize users on a forum.
So in blade, I have #can('editPost', $post) before showing the form to reply to a topic. My PostPolicy class has a editPost method that validates to true if it's the users own post.
However, the issue appears when I want to do a simple check, like deletePost(). This checks to see if Auth::user()->isAdmin
public function deletePost(User $user) {
return Auth::user()->isAdmin;
// return $user->isAdmin
}
However, this won't even get called, since I'm not passing an instance of Post
My real world application is much more complicated, but I'm using isAdmin as a simple example.
I guess defining $gate->define('deletePost', 'App\Policies\PostPolicy#deletePost'); in AuthServiceProvider could work, but would end up separating my definitions and methods, and ultimately for a large app clutter the AuthServiceProvider
When you register a policy it is the classname that is used to route checks to the class, so in order to get routed to the policy you can just pass the class name of the type you registered it with.
Try using #can('delete', Post::class) and see if that gets you there
refer to
Illuminate\Auth\Access\Gate::firstArgumentCorrespondsToPolicy
EDIT
After a little more diggin I found this
https://github.com/laravel/framework/commit/70f75255808ffc96275e6f2f356616dd2e163434#diff-961368895033e553787b301c3be0e17a
so it looks like if you on version 5.1.23 then you will be able to pass a string otherwise your will need to just pass new Post
In controllers
$this->authorize('<ability>', <Class-With-Rule::class> | <Full-Path-To-Class>);
In Blade view
#can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>)
In Eloquent model
$user->can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>);
Methods Without Models:
Some policy methods only receive the currently authenticated user and not an instance of the model they authorize. This situation is most common when authorizing create actions. For example, if you are creating a blog, you may wish to check if a user is authorized to create any posts at all.
When defining policy methods that will not receive a model instance, such as a create method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:
https://laravel.com/docs/7.x/authorization

Resources