I have a controller where most every method should be restricted to Role=Admin so the class instantiates with:
[Authorize(Roles = Admin)]
Every method takes on this check. I know I can override this attribute method by method so as to permit other user roles but I'd like to remove the Authorization check completely for one of the methods in this class.
What's the syntax for that?
thx
Wasn't able to implement Peter's suggested link so I removed the global attribute and added it back to each of the controller's methods except the one I wanted.
Related
Does the #action decorator only work for viewsets.ModelViewSets?
Can I use the #action decorator for say a generics.RetrieveAPI?
If so, how? E.g. the following doesn't work:
class ItemDetailView(generics.RetrieveAPI):
queryset = Item.objects.all()
serializer_class = ItemDetailSerializer
#action(detail=True, methods=["get"])
def custom(self, request, pk=None):
# Do whatever custom action
return Response(status=status.HTTP_200_OK)
ModelViewSet has default action such as list, put, etc, #action decorators are used for defining user-defined actions on the same modalviewset and can be mapped to urls.
Generics views are defined based on roles, for example, each view class (listApiView or RetrieveApiView, etc) has its own work to do (action), so I think action decorators are not to be used here.
what you can do is use the get_queryset method and define your own customized query, also you can read query params from url in your get method and change your query based on params value. So you can get your desired result.
Hope this helps and btw I'm not 100% sure about action decorators in generic views.
Both Faisal and Ranu make a valid point - you can't and shouldn't define custom actions for generics.X views.
I would just add a reference from the actual implementation:
Custom actions work for anything extending viewsets.ViewSetMixin: viewsets.ViewSet, viewsets.GenericViewSet, viewsets.ModelViewSet etc.
Here is the as_view method from ViewSetMixin that handles custom actions.
class ViewSetMixin:
"""
This is the magic.
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
On the other hand, generics do not have such feature (here's the code).
#action mark a ViewSetMixin method as a routable action.
In ViewSetMixin Overrides .as_view() so that it takes an actions keyword that performs the binding of HTTP methods to actions on the Resource.
So this decorator just works in every class that in inherit from ViewSetMixin. For example:
ViewSet
GenericViewSet
ReadOnlyModelViewSet
ModelViewSet
...
Can I use the #action decorator for say a generics.RetrieveAPI?
The short answer would be No, By default, it is not allowed to use actions.
The reason is that RetrieveAPIView extends REST framework's GenericAPIView, RetrieveModelMixin classes, adding commonly required behaviour for standard list and detail views. And provides the default set of get_object, get_queryset methods and other generic view base behavior, but does not include any actions by default. But you can override the class and either mixin the required mixin classes, or can define the action implementations explicitly. Explore Docs For More Details
Does the #action decorator only work for viewsets.ModelViewSets?
Anser would be No, custom actions work for anything extending from ViewSetMixin including ModelViewSets.
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 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
How can I call a controller method manually specifying some input parameters yet still have method injection work for the parameters not specified (example below).
routes.php
$myController->index($id);
controllers/MyControllerOne.php
class MyControllerOne
{
public function index($id, MyRequest $request)
{
}
}
extra information
The reason I need this is because I have special information in my routes that determines which controller should be executed such as /myroute/{data}/{id}. It's a bit unorthodox but it's a necessary evil given the scope of our system.
Once I resolve within my routes which controller needs to be called I then want to call the method on that controller. $controllerInstance->index($id).
If it's only for Request, I think you could manually pass this $this->app->make('Request'), like so
$controllerIntance->index($id, $this->app->make('Request'))
Note that you actually don't have to inject Request, since you might as well use App::make inside of your controller. But I'm not sure how good this decision is in case of testability and coupling.
For more info:
This function resolves 'Request' out of the container, that is instantiates or returns an existing instance (depending of the type of service provider).
Using make is described here http://laravel.com/docs/5.0/container (see "Resolving"). I also found this answer helpful, to understanding how the container works https://stackoverflow.com/a/25798288/1627227
I want to call a method before the execution of every and each controller's method. I don't want to go and call the method in every method. I just want to call it from one place and it will be called before any method of any controller in magento.
And I am sure we can do this but I don't know how it can be accomplished.
Please provide your suggestions.
Hope we can resolve this or may some expert guys already resolved this.
Thanks.
You need to create an Observer that binds to the controller_action_predispatch Event. That will fire before every controller in the Magento codebase. There's a useful wiki page here that walks you through the process.
You have to create a method called preDispatch in your controller. This method is executed before the requested controller action.
something like:
public function preDispatch()
{
parent::preDispatch();
//my code here
}