I'm using Laravel 5.3. I've been able to create policy classes but when I try to register a gate closure, it is always denied.
Here is the boot() method in AuthServiceProvider
public function boot()
{
$this->registerPolicies();
Gate::define('view-admin-index', function ($user, $company) {
return true;
});
}
Here is the output when dumped.
dd(Gate::has('view-admin-index')); => true
dd(Gate::allows('view-admin-index', $company)); => false
UPDATE
I also get false instead of "here" if do this:
// In AuthServiceProvider
Gate::define('view-admin-index', function ($user, $company) {
dd('here');
return true;
});
// In controller, output is false
dd(Gate::allows('view-admin-index', $company));
UPDATE 2
// In controller, there is an authenticated user and output is false
dd(auth()->user()); // => User
dd(Gate::allows('view-admin-index', $company)); // => false
Looks like you aren't even getting to call the closure. This will be the behavior if you don't have a user currently authenticated. See Source Here. Are you getting any output if you call dd(Auth::user()) right before Gate::allows('view-admin-index', $company)?
If you need to manually login a user you can always do:
Auth::login(User::find($id));
I have the same problem, it looks like a big bug of Laravel 5.3.28.
It just doesn't accepts Collections as arguments.
Here is my workaround:
Code:
Gate::define('xx', function ($auth, $user) {
return 1;
});
Route::get('example', function()
{
$user = User::first();
dd( Gate::allows('xx', $user) ); //false
});
My workaround:
Route::get('example', function()
{
$user = (object)User::first()->toArray();
dd( Gate::allows('xx', $user) ); //true
});
Related
I have this policy :
class ProjectPagePolicy
{
use HandlesAuthorization;
public function viewAny(User $user)
{
return true;
}
public function view(User $user, ProjectPage $projectPage)
{
return true;
}
public function create(User $user)
{
return $user->isAdmin() || $user->isDeveloper();
}
public function update(User $user, ProjectPage $projectPage)
{
return $user->isAdmin() || $user->isDeveloper();
}
..........
}
ProjectPageController :
class ProjectPageController extends Controller
{
public function __construct()
{
$this->authorizeResource(ProjectPage::class, 'project-page');
}
public function index(Request $request)
{
return response(
[],
HttpStatusCode::OK
);
}
public function store(ProjectPageRequest $projectPageRequest)
{
$inputs = $projectPageRequest->validated();
$projectPage = ProjectPage::create($inputs);
return response()->json([
'data' => new ProjectPageResource($projectPage)
], HttpStatusCode::Created);
}
public function update(
ProjectPageRequest $projectPageRequest,
ProjectPage $projectPage
) {
$inputs = $projectPageRequest->validated();
$projectPage->fill($inputs)->save();
return response(status: HttpStatusCode::NoContent);
}
In the routes file :
Route::middleware(['auth:sanctum'])->group(function () {
Route::post(
'/refresh-token',
fn () => app(RefreshTokenResponse::class)
);
Route::apiResources([
'project-page' => ProjectPageController::class,
]);
});
When I try to save a project page, I received 201 CREATED so all good in this case.
When I try to update it I have 403 forbidden.
Where the problem is ? Why is working on creation and not on updated ? Have an idea about that ?
TL;DR Remove the second argument ('project-page') from your authorizeResource call.
When using Route::resource(...), Laravel will convert a hyphenated route parameter to be snake-case (this will not have any affect on the URL itself, just how laravel accesses the parameter). This will mean that that when you call authorizeResource with project-page, it won't match. This will in-turn cause the authorize method to fail.
You can view your routes via the CLI with the following:
php artisan route:list
which should show your route param for your project-page routes to be project_page e.g. project-page/{project_page}
I'm defining gates in App\Providers\AuthServiceProvider for user roles. Users has a value for their role. like : (1,2,3,4) The roles giving in user management and all users can have multiple authorizations.
The user roles is:
Personnel Edit (value = 1)
Add institution (value = 2)
User Management (value = 3)
Adding Record (value = 4)
To hide some information from the sidebar, I'm using Blade directives (#can) .
I need to hide sections from the sidebar according to the authorization of each user.
The worked and long way I use is this :
AuthServiceProvider.php
....
Gate::define('personnel-method', function ($user) {
if(in_array(1,explode(",",$user->yetki)))
return true;
else
return false;
});
Gate::define('add-method', function ($user) {
if(in_array(2,explode(",",$user->yetki)))
return true;
else
return false;
});
Gate::define('user-method', function ($user) {
if(in_array(3,explode(",",$user->yetki)))
return true;
else
return false;
});
Gate::define('adding-method', function ($user) {
if(in_array(4,explode(",",$user->yetki)))
return true;
else
return false;
});
.....
sidebar.blade.php:
<ul>
#can('personnel-method') <li>Personnel Edit </li> #endcan
#can('add-method') <li>Add institution</li>#endcan
#can('user-method')<li>User Management</li>#endcan
#can('adding-method') <li>Adding Record</li>#endcan
</ul>
So Is there a less verbose code to do this?
You can use attribute casting. You can either use the "array" cast and change the value in the database to JSON ([1,2,3,4]) or define a custom cast which splits the string up.
Using the array cast:
class User extends Model {
protected $casts = [
'yetki' => 'array',
];
//...
}
So you can change the gates to:
Gate::define('personnel-method', function ($user) {
if(in_array(1, $user->yetki))
return true;
else
return false;
});
And since you're checking for a boolean, you don't have to use an if and just return the condition:
Gate::define('personnel-method', function ($user) {
return in_array(1, $user->yetki);
});
To make it even easier to read you can move that logic to a method on the User class:
class User extends Model {
public function hasRole($role)
{
return in_array($role, $this->yetki);
}
}
Finally, you can define the roles in a class:
abstract class UserRole {
const Personnel_Edit = 1;
const Add_institution = 2;
const User_Management = 3;
const Adding_Record = 4;
}
And change the gates to:
Gate::define('personnel-method', function ($user) {
return $user->hasRole(UserRole::Personnel_Edit);
});
To shorthand your code you can do the following
Gate::define('personnel-method', function ($user) {
return in_array(1,explode(",",$user->yetki));
});
Gate::define('add-method', function ($user) {
return in_array(2,explode(",",$user->yetki));
});
Gate::define('user-method', function ($user) {
return in_array(3,explode(",",$user->yetki));
});
Gate::define('adding-method', function ($user) {
return in_array(4,explode(",",$user->yetki))
});
If you're using php 7.4 you can also use arrow function
Gate::define('personnel-method', fn ($user) => in_array(1,explode(",",$user->yetki)));
Gate::define('add-method', fn ($user) => in_array(2,explode(",",$user->yetki)));
Gate::define('user-method', fn ($user) => in_array(3,explode(",",$user->yetki)));
Gate::define('adding-method', fn ($user) => in_array(4,explode(",",$user->yetki)));
I have a function in my User model which is called isAdmin, if "Admin" is set to 1 in the database, it returns true, if not it returns false.
How is this gonna work with Auth::user()?
When I do Auth::user()->isAdmin(), it returns "Property [admin] does not exist on this collection instance."
Thats why I came to the conclusion it may not use the User model?
User model
public function isAdmin() {
if($this->admin == 1) {
return true;
} else {
return false;
}
}
public function view ()
{
if(Auth::check() && Auth::user()->isAdmin()) {
$user = User::all();
$post = Post::all();
$visit = Visits::all();
return view('admin')->with('post', $post)->with('user', $user)->with('visit', $visit);
} else {
return redirect()->to('/');
}
}
If I may suggest, for this use case, you can actually make do without an extra function. You could just say auth()->user()->admin, specially if the 'admin' column in the database is boolean type.
Otherwise (even admin coloumn is not boolean type) you can set up a mutator method in the model, like so:
public function getIsAdminAttribute()
{
return (bool) $this->admin;
}
Then to check you can access it like so: Auth::user()->isAdmin or auth()->user()->isAdmin
And better yet, you might want to read about Gate and Policies to achieve more robust access controlling. https://laravel.com/docs/5.7/authorization
Suggestion, change the code to just this:
public function isAdmin() {
return $this->admin;
}
This code does exactly the same as you've got above..
Now in your admin.blade.php you are using:
$user->isAdmin();
But in the controller you have:
$user = User::all();
which returns collection.
You should iterate over it, and check on each user instance if it is an admin:
$users = User::all();
In the view:
#foreach($users as $user)
#if($user->isAdmin())
{{ $user->name }} // some code here..
#endif
#endforeach
No Need To do anything just check if login then auth()->check() is return true then auth()->user() return the user
public function view ()
{
if(auth()->check() && auth()->user()->isAdmin()) {
$user = User::all();
$post = Post::all();
$visit = Visits::all();
return view('admin')->with('post', $post)->with('user', $user)->with('visit', $visit);
} else {
return redirect()->to('/');
}
}
public function isAdmin()
{
return $this->admin;
}
Is it possible to access the authenticated user in a route binding.
Route::bind('account', function($account_id)
{
dd(auth()->user()); // it's null :(
$account = App\Models\Account::where('business_id', auth()->user()->business_id)
->where('account_id', $account_id)
->first()
return !is_null($account) ? $account : App::abort(404);
});
I've tried grouping the route binding within some auth middleware, no dice - is this a thing? It would be really useful to pull off, to avoid extra validation in the controller.
Help appreciated.
As long as the bind is inside the Auth middleware you should be able to access it using Auth::user()
Route::bind('account', function($account_id)
{
dd(Auth::user()); // Here is the change
$account = App\Models\Account::where('business_id', Auth::user()->business_id)
->where('account_id', $account_id)
->first()
return !is_null($account) ? $account : App::abort(404);
});
You can use \Auth. This works for me:
RouteServiceProvider:
public function boot(Router $router) {
parent::boot($router);
$router->bind('account', function () {
dd(\Auth::user());
});
}
routes.php
Route::get('account/{account}', function () {
//test
});
prints user object
I am using Sentinel to authenticate users and as the auth Middleware.
Middleware code:
public function handle($request, Closure $next)
{
var_dump(Sentinel::guest()); // prints false
if (Sentinel::guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('/login');
}
}
return $next($request);
}
Controller code:
public function getAccount() {
var_dump(Sentinel::guest()); // prints true
return Sentinel::getUser();
}
routes.php
Route::group(['middleware' => ['auth']], function () {
Route::get('api/v1/temp/users/account', 'App\Http\Controllers\UsersController#getAccount');
}
Then if i browse to api/v1/temp/users/account the var_dump() in the Middleware is printing false, while the var_dump() inside the controller is printing true which seems a nonsense.
What's wrong?
It turned out i was using the native facade instead of the Laravel facade Cartalyst\Sentinel\Laravel\Facades\Sentinel. That fixed.