I am trying to implemnt ACL using Laravel Polices and Gate. Below is my code.
UserPolcy:
class UserPolicy
{
use HandlesAuthorization;
public function isRoleAllowed(User $user, $role)
{
return in_array( $role , $user->getRoles());
}
}
AuthServiceProvider:
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
public function boot()
{
$this->registerPolicies();
// User Policy
foreach ( get_class_methods(new \App\Policies\UserPolicy) as $method ) {
Gate::define($method, "App\Policies\UserPolicy#{$method}");
}
}
}
How I am try to access isRoleAllowed in my controller,
Gate::allows('isRoleAllowed', 'APPROVE_INVOICE')
It is always return true. It is not executing isRoleAllowed function. I don't know where I am getting wrong.
Related
So basically I want to create something like #IsGranted.
I used #IsGranted on my application to access control to prevent a simple user from accessing an admin page for example.
On my entity, I have a boolean field called is_Active
if it's true (1) then the user can use his account
if it's false (0) then he gets redirected to an error page!
In this case, I am not going to test on the Rolesfield of the user but I am gonna test on the is_Active field that's why I can't use the #IsGranted.
I created an error twig page active.html.twig
and I place it on templates folder, and I found myself FORCED to add those 2 lines on every controller function.
if ($this->getUser()->getIsActive()==false) {
return $this->render('active.html.twig');}
Here is an example:
/**
* #IsGranted("ROLE_ADMIN")
* #Route("/", name="user_index", methods={"GET"})
*/
public function index(UserRepository $userRepository): Response
{
if ($this->getUser()->getIsActive()==false) {
return $this->render('active.html.twig');}
return $this->render('user/index.html.twig', [
'users' => $userRepository->findAll(),
]);
}
This is very heavy and bad to add this if statement on every function (I have +30 functions on the app)
Maybe I can create something similar to #IsGranted and use it on the annotation of each function instead?
You can keep using #IsGranted with a custom voter. https://symfony.com/doc/current/security/voters.html#creating-the-custom-voter
Create new voter like in the documentation
public const ACTIVE = 'active';
protected function supports(string $attribute, $subject)
{
return $attribute === self::ACTIVE;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
if ($user instanceof User && !$user->isActive()) {
throw new InactiveUserException();
}
return true;
}
Then you can create a listener for InactiveUserException and show what ever you want to the client.
In your controller you'll need to put #IsGranted("active") or #Security(expression="is_granted('active')") before the route method or controller
I would use the authentication for this then you don't have to touch your controllers. You can check if they are logged in and active then they can view the content or if they fail auth then you can direct them to another route with your active.html.twig.
You can also just have this set on certain routes or all of them.
https://symfony.com/doc/current/security/guard_authentication.html
Sample Authenticator and set this just for your admin routes then you can have a normal authenticator without checking for an active user on the checkCredentials for all other routes.
<?php
namespace App\Security;
use App\Entity\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Twig\Environment;
class AdminAuthenticator extends AbstractGuardAuthenticator
{
/** #var Environment */
private $twig;
public function __construct(Environment $twig)
{
$this->twig = $twig;
}
public function supports(Request $request): bool
{
$email = $request->request->get('email');
$password = $request->request->get('password');
return $email && $password;
}
public function getCredentials(Request $request)
{
$email = $request->request->get('email');
$password = $request->request->get('password');
return [
'email' => $email,
'password' => $password
];
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$email = $credentials['email'];
return $userProvider->loadUserByUsername($email);
}
public function checkCredentials($credentials, UserInterface $user)
{
$password = $credentials['password'];
if (!$this->passwordEncoder->isPasswordValid($user, $password)) {
throw new CustomUserMessageAuthenticationException(
'Sorry, you\'ve entered an invalid username or password.'
);
}
if (!$user->isActive()) {
throw new NotActiveUserException(
'This account is not active'
);
}
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($exception instanceof NotActiveUserException) {
// You should redirect here but you get the idea!
$this->twig->render('active.html.twig');
}
// Do something else for any other failed auth
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new JsonResponse('success', Response::HTTP_OK);
}
public function start(Request $request, AuthenticationException $authException = null)
{
return new JsonResponse('Not Authorized', Response::HTTP_UNAUTHORIZED);
}
public function supportsRememberMe()
{
return false;
}
}
Then in your security.yaml
firewalls:
admin:
pattern: ^/admin
provider: user
guard:
authenticators:
- App\Security\AdminAuthenticator
I'm developing an API that uses not standard database connection that is not supported by Laravel by default. Because of that I am not able to use Eloquent Models to create JWT tokens and authenticate users. I have already implemented a custom user model:
use App\Repositories\Technician as TechnicianRepository;
use Illuminate\Contracts\Auth\Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Technician implements Authenticatable, JWTSubject {
public function __construct(array $data) {
$this->repository = new TechnicianRepository;
foreach ($this->repository->create($data) as $attribute => $value) {
$this->{$attribute} = $value;
}
}
public function getAuthIdentifierName() {
return 'id';
}
public function getAuthIdentifier() {
return $this->{$this->getAuthIdentifierName()};
}
public function getAuthPassword() {
return decrypt($this->password);
}
public function getRememberToken() {}
public function setRememberToken($value) {}
public function getRememberTokenName() {}
public function getJWTIdentifier()
{
return $this->id;
}
public function getJWTCustomClaims()
{
return [];
}
}
Using this model I am able to successfully generate a JWT token in my AuthController like this:
$technician = new Technician([
'email' => $request->email,
'phone' => $request->phone,
'password' => encrypt($request->password)
]);
$token = app('auth')->login($technician);
However I have no idea how to furtherly authenticate users based on the generated JWT token that is passed with a request.
For now I have the following contents in boot method of AuthServiceProvider:
public function boot()
{
$this->app['auth']->viaRequest('api', function ($request) {
return app('auth')->setRequest($request)->user();
});
}
And the following logic in Authenticate middleware:
if ($this->auth->guard($guard)->guest()) {
return Response::fail([
'auth' => array('Access denied - authorization is required')
], 401);
}
return $next($request);
Even if providing the right JWT token for a user, the middleware denies access.
Any help is appreciated, because I have no idea how to develop user authentication furtherly.
So I was reading about using laravel policies for granting authorities on the resources of my application but there seems to be a problem there though I followed the tutorial.
I have a user model which can't be created via HTTP requests except by other users who have the Entrust role of 'Admin' or 'Broker'. What I understood and succeeded to make it work on other actions like indexing users was the following:
Inside the AuthServiceProvider.php inside the private $policies array, I registered that User class with the UserPolicy class like that
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
Insured::class => InsuredPolicy::class
];
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
}
}
Define the UserPolicy controller class:
class UserPolicy {
use HandlesAuthorization;
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function index(User $user) {
$is_authorized = $user->hasRole('Admin');
return $is_authorized;
}
public function show(User $user, User $user_res) {
$is_authorized = ($user->id == $user_res->id);
return $is_authorized;
}
public function store() {
$is_authorized = $user->hasRole('Admin');
return $is_authorized;
}
}
Then inside the UserController class, before performing the critical action I use this->authorize() check to halt or proceed depending on the privilege of the user
class UserController extends Controller
{
public function index()
{
//temporary authentication here
$users = User::all();
$this->authorize('index', User::class);
return $users;
}
public function show($id)
{
$user = User::find($id);
$this->authorize('show', $user);
return $user;
}
public function store(Request $request) {
$user = new User;
$user->name = $request->get('name');
$user->email = $request->get('email');
$user->password = \Hash::make($request->get('password'));
$this->authorize('store', User::class);
$user->save();
return $user;
}
}
The problem is that $this->authorize() always halts the process on the store action returning exception: This action is unauthorized.
I tried multiple variations for arguments of the authorize() and can't get it to work like the index action
In store() function of UserPolicy::class you are not passing the User model object:
public function store(User $user) {
$is_authorized = $user->hasRole('Admin');
return true;
}
missing argument User $user.
Maybe this is the cause of the problem.
I am totally new in laravel.
I install laravel 5.2 . I have done with CRUD in laravel. Now i want to integrate laravel authentication package. so i choose zizaco\entrust.
I follow each steps from doc link. but i don't understand what is wrong. In doc there is not mentioned that in which file i have to add following code.
$owner = new Role();
$owner->name = 'owner';
$owner->display_name = 'Project Owner'; // optional
$owner->description = 'User is the owner of a given project'; // optional
$owner->save();
$admin = new Role();
$admin->name = 'admin';
$admin->display_name = 'User Administrator'; // optional
$admin->description = 'User is allowed to manage and edit other users'; // optional
$admin->save();
and other below code in document.
Even
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
Not mentioned about implements class.
i do
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
but i got error
Trait 'App\Authenticatable' not found
New learner can't get where to place code. i search alot but i can't get perfect document which give right direction.
Where to create role,permissions? Anyone please help me.
1.Install "zizaco/entrust": "5.2.x-dev"
2.add provider and aliases in app.php
3.php artisan vendor:publish
4.php artisan migrate
Now add in user.php
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword, EntrustUserTrait;
}
Next Create a model for Role
use Zizaco\Entrust\EntrustRole
class Role extends EntrustRole
{
protected $table = 'roles';
protected $fillable = ['name', 'display_name', 'description']
}
Now create a model for permmission
use Zizaco\Entrust\EntrustPermission;
class Permission extends EntrustPermission
{
protected $table = 'permissions';
protected $fillable = ['name', 'display_name', 'description'];
}
Now create a seeding table for roles and permissions
UserTableSeeder.php
<?php
use Illuminate\Database\Seeder;
use App\Permission;
use App\Role;
use App\User;
class UserTableSeeder extends seeder
{
public function run()
{
Permission::truncate();
Role::truncate();
User::truncate();
\DB::table('role_user')->delete();
\DB::table('permission_role')->delete();
//create a user
$veeru = User::create([
'name' => 'veeru',
'email' => 'something#something.com',
'password' => bcrypt('qwerty'),
]);
//create a role of admin
$admin = Role::create([
'name' => 'admin',
'display_name' => 'Admin',
'description' => 'Only one and only admin',
]);
//create a permission for role
$manage_users = Permission::create([
'name' => 'manage-users-roles-and-permissions',
'display_name' => 'Manage Users,Roles and Permissions',
'description' => 'Can manage users,roles and permission"s',
]);
//here attaching permission for admin role
$admin->attachPermission($manage_users);
//here attaching role for user
$veeru->attachRole($admin);
//here iam creating another role and permisssion
$application = Role::create([
'name' => 'appapirequestlogs',
'display_name' => 'AppApiRequestLogs',
'description' => 'This has full control on Application Core Request logs',
]);
$corereq = Permission::create([
'name' => 'appapireqlogindex',
'display_name' => 'AppApiReqLogIndex',
'description' => 'This has control on Application Core Request Logs index only',
]);
here attaching roles and permissions
$application->attachPermission($corereq);
$veeru->attachRole($application);
}
After seeding that create a routes file and controller for roles and permissions
routes.php
Route::group(['middleware' => 'web'], function () {
Route::group(['prefix' => 'admin'], function () {
Route::controller('roles', 'RolesController');
Route::controller('permissions', 'PermissionsController');
Route::controller('users', 'UsersController');
});
});
RolesController.php
use App\Permission;
use App\Role;
use Illuminate\Http\Request;
use App\Http\Requests;
class RolesController extends Controller
{
function __construct()
{
$this->middleware('auth');
$this->middleware('role:admin');
$this->middleware('permission:manage-users-roles-and-permissions');
}
public function getIndex(Request $request)
{
$roles = Role::with('perms')->get();
return view('roles.index', ['roles' => $roles]);
}
public function getCreate()
{
return view('roles.create');
}
public function postCreate(Request $request)
{
$role = Role::create([
'name' => $request->name,
'display_name' => $request->display_name,
'description' => $request->description
]);
return redirect()->to('admin/roles/index');
}
public function getUpdate($id)
{
$role = Role::findOrFail($id);
return view('roles.update', ['role' => $role]);
}
public function postUpdate(Request $request)
{
$role = Role::findOrFail($request->get('id'));
$role->name = $request->get('name');
$role->display_name = $request->get('display_name');
$role->description = $request->get('description');
$role->save();
return redirect();
}
public function getDelete($id)
{
$role = Role::findOrFail($id);
$role->delete();
return redirect();
}
public function getAttach(Request $request)
{
$role = Role::where('id', '=', $request->id)->with('perms')->first();
$permissions_id = $role->perms->pluck('id')->toArray();
$permissionsNotAttached = Permission::whereNotIn('id', $permissions_id)->get();
return view('roles.attach', compact('role', 'permissionsNotAttached'));
}
public function postAttach(Request $request)
{
$role = Role::findOrFail($request->id);
$permission = Permission::findOrFail($request->permission);
$role->attachPermission($permission);
return redirect();
}
public function getDetach(Request $request)
{
$role = Role::findOrFail($request->role_id);
$permission = Permission::findOrFail($request->permission_id);
$role->detachPermission($permission);
return redirect()->to('/admin/roles/index');
}
}
Similarly create PermissionsController.php
use App\Http\Requests\PermissionRequest;
use App\Permission;
use App\Http\Requests;
class PermissionsController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('role:admin');
$this->middleware('permission:manage-users-roles-and-permissions');
}
public function getIndex()
{
$permissions = Permission::all();
return view('permissions.index', ['permissions' => $permissions]);
}
public function getCreate()
{
return view('permissions.create');
}
public function postCreate(Request $request)
{
$permission = Permission::create([
'name' => $request->name,
'display_name' => $request->display_name,
'description' => $request->description
]);
return redirect();
}
public function getUpdate($id)
{
$permission = Permission::findOrFail($id);
return view('permissions.update', ['permission' => $permission]);
}
public function postUpdate(Request $request)
{
$permission = Permission::findOrFail($request->get('id'));
$permission->name = $request->get('name');
$permission->display_name = $request->get('display_name');
$permission->description = $request->get('description');
$permission->save();
return redirect();
}
public function getDelete($id)
{
$permission = Permission::findOrFail($id);
$permission->delete();
return redirect();
}
}
After Creating first given process
create roles middleware example CheckRole
<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param $role
* #return mixed
*/
public function handle($request, Closure $next, $role)
{
if (\Auth::user()->hasRole($role)) {
return $next($request);
} else {
return response()->view('errors.401');
}
}
}
now create Check Permission
<?php namespace App\Http\Middleware;
use Closure;
class CheckPermission
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param $permission
* #return mixed
*/
public function handle($request, Closure $next, $permission)
{
if (\Auth::user()->can($permission)) {
return $next($request);
} else {
return response()->view('errors.401');
}
}
}
add these middlewares in kernal.php
'role' => CheckRole::class,
'permission' => CheckPermission::class
Now create a role with a name and add permissions to it.
example:
1.create a role name festivals.
2.now create multiple permissions for that
->fesindex (only forr index viewing)
->fesedit (only for edit and update)
->fesadd (only for adding)
->fesdelete (only for deleting)
3.now attach which permissions you want to give to that role festivals
4.if you want a user to see only index page then
for role festivals attach only fesindex
5.if you want a user to see index and add a new festival then create a new role for him and add fesindex and fes add
Now created permissions are placed in your controller
function __construct(FestivalRepository $repo)
{
$this->middleware('auth');
$this->middleware('permission:fesindex', ['only' => 'getFestivals']);
$this->middleware('permission:fesedit', ['only' => ['getFestival', 'postUpdateFestival']]);
$this->middleware('permission:fesadd', ['only' => ['getAddFestival', 'postAddFestival']]);
$this->middleware('permission:fesapprove', ['only' => 'getChangeStatus']);
$this->middleware('permission:fesdelete', ['only' => 'getDeleteFestival']);
}
here getFestivals,getAddFestival etc are methods in controller.
I am getting this error
"Call to a member function isATeamManager() on a non-object".
(RedirectIfNotAManager.php)
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Closure;
class RedirectIfNotAManager
{
public function handle($request, Closure $next)
{
if(!$request->user()->isATeamManager())
{
return redirect('articles');
}
return $next($request);
}
}
I have googled it and didn't get any solution,since i am new to laravel kindly help me to solve this problem .its in laravel 5.1 . I have tried other examples and still getting this error..
(This is the User.php Model code:)
<?php
namespace App;
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
protected $table = 'users';
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
public function articles()
{
return $this->hasMany('App\Article');
}
public function isATeamManager()
{
return false;
}
}
That means that your request doesn't have a user stored on it. So no one is logged in or your session isn't working correctly. $request->user() is a function that runs to try to pull the current user, by default if someone is logged in it will return a user object or a null value I believe. So most likely you are getting a null value back. You could change your if statement to this:
if(!$request->user() || !$request->user()->isATeamManager()) {
i got the solution..
This is working!!
public function handle($request, Closure $next)
{
if ($request->user()) { // This will return null if the user is not logged in, which evaluates to false
if (!$request->user()->isATeamManager()) {
return redirect('articles');
}
}
return $next($request);
}
}