today i was creating USER profile page with is controlled in ProfileController it returning views to profile page, profile settings, etc.
so i decide to make some Policy rules to Edit profile and etc.
so i found i should use Middleware / Gates / Policy, based on Laravel Doc i chose Policy because profil page is public but only specific part of it can author edit so i needed #can
So my steps:
php artisan make:policy ProfilePolicy ( without model )
Registered policy to AuthServiceProvider in $policies property
writed methods like edit inside ProfilePolicy
then i started thinking how i define it to my Controller hmmm, documentation doesnt helps me :/
so i tryed blade #can('edit', $user) method and it worked, but HOW ?, how to define specific policy to one Controller ? ( not Model ), how to define multiple Policy to single Controller
i m lost how laravel Magic done this maybe because of Naming ? ProfileController => ProfilePolicy ?
In the controller you can write this
public function edit(Profile $profile) {
$this->authorize('edit', $profile)
}
Laravel does this:
Check the type of $profile, and it's a Profile::class
Check policies registered for that class (your step 2)
Looks for the edit method in that policy, if not found, return false meaning user is not authorized
Executes the edit() function that returns true/false
In blade the #can directive does exactly the same thing.
Policies are meant to be tied to Models, it's a convenient way to write rules to handle single models, but they can be triggered in many ways (like the authorize() method in controllers and #can directive in blade).
Related
How to Dynamically add gates in Laravel?
I built my CMS on laravel, and it support plugins. Plugin can add menu item in dashboard, some page , routes etc. I need use gate for protect edit / delete / add / change page for example, that an authorized user did not create.
So I need that each plugin can Dynamically add this permissions (view /add/edit/update/delete/)
How I can do this?
And btw plugin locate in APP/Plugins folder.
And Admin can attach this permission to some roles http://prntscr.com/kidnxz
https://pastecode.xyz/view/bcb9d00f here example table that I have.
and problem that I need also add in permissions table permission
I use laravel 5.6
Here example of simple plugin pastecode.xyz/view/75d2fa28 Calls boot when plugin loaded And onActivate when user activate this plugin
I'm looking for something like dynamic Policies
or like https://octobercms.com/docs/plugin/registration
'permissions' => ['acme.blog.*'],
You can use Gate::policy(PolicyTarget::class, PolicyImplementation::class) to register a new gate policy inside your plugin.
Assume you have a policy target being e.g. a user (bad example maybe).
Create a policy with e.g. php artisan make:policy UserInteractsWithBlogPolicy
class UserInteractsWithBlogPolicy {
// Boilerplate and custom code
}
Then you can register this when the plugin boots:
public function boot() {
// TODO: Implement boot() method.
$this->enableRoutes();
$this->enableViews();
\Gate::policy(User::class, UserInteractsWithBlogPolicy::class);
}
Then you can use the normal policy rules like e.g.
$user->can("view", Blog::find(1));
Or basically do whatever can be done according to https://laravel.com/docs/5.6/authorization#authorizing-actions-using-policies
As far as I understood from your question your CMS may has several gates that you do not want to define separate gates for each of them. You may try before method to define dynamic gates.
public function boot(){
Gate::before(function (User $user, $ability) {
Gate::define($ability, function (User $user) use ($ability) {
return $user->hasPermission($ability);
});
});
}
In the above example hasPermission is a method in User model that check if user has the right permission/ability.
Now you can use it like this in controller:
Gate::authorize('CUSTOME_ABILITY');
Or as middleware:
Route::get('/', function (Request $request) {
// your code
})->can('CUSTOME_ABILITY');
I have a String with a middleware rule (like in routes):
$middleware = "can:index,App\Models\Order";
Is there any possiblity to check if a given user has access with this middleware rule?
That is how laravel policies define authorization rules in middleware. See here: https://laravel.com/docs/5.5/authorization#via-middleware
Create a policy class for the model you are authorizing then register the policy to model.
It is possible to see if a given user is authorized to carry out a certain action in many different ways. One is by using the middleware in your question and attaching it to a route or group. Here are some other ways:
Using the can method on the User object. The can method is inherited from the Authorizable trait (So it's not limited just to users):
if ($user->can('index', 'App\Models\Order')) {
// User is allowed to index orders.
}
Using the authorize method on a controller. The authorize method is inherited from the AuthorizesRequests trait (So this is not limited to just controller). This method throws an exception if authorization fails:
$this->authorize('index', 'App\Models\Order');
In a view, it is possible to use the #can Blade directive to see if a user is authorized to carry out the given action:
#can('index', 'App\Models\Order')
This user can index orders.
#endcan
If you have that specific string, you could do a little bit of manipulation to extract the bits you need and then pass it to one of the above methods:
$middleware = "can:index,App\Models\Order";
list($rule, $parameters) = explode(':', $middleware);
list($ability, $model) = explode(',', $parameters);
if ($user->can($ability, $model)) {
// User can index orders.
}
Of course it would be wise to do more error checking etc.
I am using Laravel 5.1 for my project. I am trying to secure Routes and make sure only logged in user can access certain routes. I am aware about middlewares but I am wondering if anyone post an example or a link explaining about middleware and how to protect a page using middleware.
Thanks
To build on the answer given by Joe Rose, you can also specify the middleware in your controller rather than in your routes.php file.
E.g you could have your routes set out like
Route::get('/example', 'ExampleController#index');
Route::post('/example/post', 'ExampleController#post');
Route::resource('blog', 'BlogController');
And then inside your controller reference it like so:
class ExampleController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
//....
If you're looking for more info, check out the link to the docs Joe gave, and also this blog post which explains what middleware is really well and how to create your own if you need to.
You are correct about using middleware. The included Auth middleware is what you should use, as long as you are also using the included Auth controller to authenticate users. You would write your route like this:
Route::get('/page', array(
'uses' => 'Controller#method',
'middleware'=>'auth'
));
(The above example is using a GET request, but it could other request types, like POST for example).
This will use the default behavior of the middleware which checks to see if the user is logged in (authenticated). You can also extend or overwrite the built-in functions to allow you to direct the application on where to send the user if they are or are not logged in, etc. Laravel's official documentation is a good starting point: link
In Laravel 5.1 I need to pass some data to the register view.
The getRegister() function in RegistersUsers trait is responsible to return the view.
At first I modified the function to pass my data but then I realized that the modifications would be overridden in case of an update.
So I made a new controller registerController and modified the route for getRegister like this: Route::get('auth/register', 'Auth\RegisterController#getRegister')
Inside the controller I redefined the getRegister function to return the view with my additional data.
Now I am thinking.. am I doing this correctly or do I need to use some other method and use the original AuthController some other way?
Also, default auth is set to use email for post login, how do I change it to use username without touching the foundation files?
Are all these matters regarding "extending the framework" ?
Thanks
First of all, it's always a bad idea to modify vendor files as the changes would be overwritten in case of any update in vendor package.
Answering your first question:
If you want to provide some additional data in registration view, you could do 2 things.
First one is to add your own getRegister() method:
public function getRegister()
{
return view('auth.register')->with(<parameter name>, <parameter value>);
}
The drawback of this solution is that in case of any future changes in trait's getRegister method those changes will not be incorporated into your controller.
So the better approach is to reuse trait's getRegister() method in your controller and add your parameters to whatever trait returns:
In your controller do:
use RegistersUsers {
RegistersUsers::getRegister as traitGetRegister;
}
public function getRegister()
{
return $this->traitGetRegister()->with(<parameter_name>, <parameter_value>);
}
Answering your second question:
Laravel's Auth::attempt() method that is used to login users uses DatabaseUserProvider to load users from the DB and that provider is flexible enough to use any credential set you provide. However, if you want to use AuthenticatesUsers trait to login users, you have to override its postLogin method, because of the validation it does for user credentials:
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
UPDATE FOR LARAVEL 5.1: Since 5.1 there is no need to override postLogin() to change the column that is used to fetch users from the database. It is enough to set username property in the controller to the name of the column:
public $username = 'login';
I am currently experimenting with the new Laravel 5 and got the authentication to work (register/login).
To get the authenticated user in my controller I currently inject Guard into the controller action:
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
class ClientController extends Controller {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index(Guard $auth)
{
return view('client.index', ['user' => $auth->user()]);
}
...
First Question: Is this the recommended way?
Second Question: How would I go about implementing some kind of roles/permissions? Something like client.edit, client.add, ... Does Larval 5 offer some kind of convenience here?
How would I set the necessary role/permission for a route/controller action?
I am thinking that I might need to write my own middleware for that. Any suggestions on how to approach the problem?
After spending some more time on Laravel 5 I can an answer my own question:
Is injecting Guard the recommended way? No: If you need to access Auth in your view, you can do so already like this:
#if( Auth::check() )
Current user: {{ Auth::user()->name }}
#endif
This uses the Auth facade. A list of all available facades is in config/app.php under aliases:
What if I need Auth in my controller? Injecting an instance of Guard like shown in the question works, but you don't need to. You can use the Auth facade like we did in the template:
public function index()
{
if(\Auth::check() && \Auth::user()->name === 'Don') {
// Do something
}
return view('client.index');
}
Be aware that the \ is needed before the facade name since L5 is using namespaces.
I want to have permissions/roles using the new auth mechanism in L5: I implemented a lightweight permission module using the new middleware, it is called Laraguard. Check it out on Github and let me know what you think: https://github.com/cgrossde/Laraguard
UPDATE: For the sake of completeness I want to mention two more projects. They provide everything you need to save roles and permissions in the DB and work perfectly together with Laraguard or on their own:
https://github.com/caffeinated/shinobi
https://github.com/romanbican/roles
If you want make your own custom authentification, you need to keep the User model from Laravel 5 with all the dependency. After you will be able to login your user in your controller. Dont forget to put (use Auth;) after the namespace of your controller.