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
Related
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.
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.
I have a problem when using user model's can and cannot method. I have a post model and an user model. And I define an add method in PostPolicy class:
public function add(User $user){
if($user->isGuest()) return false;
return true;
}
I know when use can or cannot method, the policy class which will be used will be resolved automatically by model instance we pass to can or cannot method, like $user->can('update',$post). But in this example. the add method does not need a post instance, So I can't do like this $user->can('add',$post). Can someone tell me how to get done it?
It's a good question.
The funny thing is this isn't documented for Laravel 5.2 but it is for 5.4. So you will have to check if this works for you in Laravel 5.2.
When you don't have an object model to pass into the policy check the documentation says you should pass this instead Model::class. In your case, it'd be Post::class as the parameter. So it'd be:
$user->can('update', Post::class);
If this does not work for you in Laravel 5.2 you can always write a Gate instead of a policy and just use the Gate::allows() or Gate::denies methods:
https://laravel.com/docs/5.2/authorization#gates
I'm using Laravel 5.1's authorization features, documented here. My controllers implement AuthorizesRequests and I have my policies set up connecting policies to their models to create an ACL of sorts. In my controllers, I'm checking for authorization in each method. For example, in an 'AgencyController' the 'update' method calls $this->authorize($agency), which then checks my AgencyPolicy's update method to know rather or not the current user is allowed to update the agency, just as described in the documentation. This works the way I want it to.
However, what I can't seem to figure out is how to use authorization for other methods like index() and create() where there isn't a specific model being used. Calling $this->authorize('index') seems to return false, even if I have an index($user) function in my policy class that only returns true.
I'm new to using Laravel's authorization helpers, so I might be going about this wrong or missing something obvious. Any help pointing me in the right direction would be gretaly appreciated!
You have to pass it the class name of the model you're checking:
$this->authorize('index', Agency::class);
With some help from someone in the Laravel slack group I was able to find the answer to this myself.
Without an instance of the model, the authorize() calls couldn't map to the correct policies. But by simply passing the class to them, it is able to and works.
For example, instead of calling $this->authorize('index') in my controller's index method, I'm now calling $this->authorize('index', Agency::class) to give it the correct model to use.
I have a unique requirement that I'm not sure how to handle. We are working with an underlying repository that allows for a quite a bit of flexibility with regards to parameters. The Controller services are injected as "service" objects into a "Service" Property for our controller classes and they wrap the calls to entity framework.
eg.
public UserController:ApiController{
public IUserSvc Service {get;set}
public UserController(IUserSvc service){
this.Service=service;
}
}
This part is handled by Autofac and all is well here.
Next, we have the standard GET,POST,PUT,DELETE,PATCH methods in the controller that accept a SINGLE user model. In other words, there is only ONE user model that is ever used, and therein lies the problem.
While the underlying service class just handles the passing of data back and forth, it pushes the requirement of the granular control of validation up the pipe. This isn't abnormal to me.
Given that we have a single API that uses models with validation attributes, we've run into an issue where different apps that call into the api require different types of validation. The model structure is fine. It's just that some properties, depending on "who you are" and what HTTPMethod is sent, either are or are not validated.
ex:
public class User{
public int UserID {get;set}
public string Name {get;set;}
}
Let's take 2 scenarios:
1. Basic User
2. System User
As a basic user, UserID is required for: GET, PUT,PATCH ( and technically delete too but it's not used). It is not required for POST because that would be creating their own user.
As a system user, UserID is NOT required in GET but is required in PUT, PATCH, DELETE.
So I am having a hard time trying to figure out how to do this without making one api for Basic User with their own models and one for System User.
I wanted to keep this high in the pipe so I wasn't doing this in the controller api methods, i.e. Check roles/claims, create/cast/or otherwise map bound model to specific concrete model per role/claim and then run validation and THEN return model binding errors if any. That's a lot of gunk in the method and I would prefer this to be on an attribute of the method or api controller.
Is there any way to do a type of model binding and or model validation based on "who you are"? That could be by role or claim?