spatie / laravel-permission

Associate users with roles and permissions
https://spatie.be/docs/laravel-permission
MIT License
12.17k stars 1.78k forks source link

can't use permission and role in API routes #1417

Closed jackpit93 closed 4 years ago

jackpit93 commented 4 years ago

My stack is : Laravel 6.2 PHP:7.3 Mysql:5.7.24

This is my web routes:

Route::group(['prefix'=>'/admin','namespace'=>'FrontEnd','middleware'=>['role:superAdmin']],function (){
    Route::get('/','AdminPanel\AdminPanelController');
    Route::get('/{path}', 'AdminPanel\AdminPanelController')->where('path', '(.*)');
});

user after login go to '/admin' (That's actually the dashboard). my dashboard make with vue and i use axios for http request. Auth::routes();

this is my api routes:

Route::group(['prefix' => 'v1'], function () {
    Route::post('/register', 'Auth\AuthController@register');
    Route::post('/login', 'Auth\AuthController@authenticate');
    Route::get('/user', 'Auth\AuthController@getAuthenticatedUser');
    Route::get('/me', 'Auth\AuthController@me');
    Route::get('/fresh', 'Auth\AuthController@refresh');
    Route::group(['middleware' => ['mix.auth']], function () {
        Route::group(['namespace'=>'Api\V1'],function (){
            Route::get('/users', 'UserController@index')->middleware('permission:createUser');
            Route::post('/users', 'UserController@store')->middleware('permission:createUser');
            Route::get('users/{id}', 'UserController@show')->middleware('permission:readUser');
            Route::delete('users/{id}', 'UserController@delete')->middleware('permission:deleteUser');
            Route::patch('users/{id}', 'UserController@update')->middleware('permission:updateUser');
        });
    });
});

In admin page there is a Get request to/api/v1/users/+this.$route.params.userId after request give me response User is not logged in. My API works with both Auth and JWT for authenticate.

class MixAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
       $jwt = $this->ApiAauthenticate($request);
        if (Auth::check() || $jwt ) {
            return $next($request);
        }
        return $next($request);

    }

    public function ApiAauthenticate($request){

        if ($request->hasHeader('Authorization')){
            try {
               return JWTAuth::parseToken()->authenticate();
            }
            catch (Exception $e) {
                if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                    return response()->json(['status' => 'Token is Invalid']);
                }else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
                    return response()->json(['status' => 'Token is Expired']);
                }else{
                    return response()->json(['status' => 'Authorization Token not found']);
                }
            }
        }
        return false;

    }
}

And now here's the point,when i use postman and get token and set authorization header everything is working properly but when i use browser and normal login(Auth) not work Permission Middleware.

drbyte commented 4 years ago

Are you using ONLY the api guard? Or other guards as well? How do those guards compare with the guard_name associated with all your roles and permissions? How do those compare with the order of guards listed in config/auth.php?

In v2 and v3 of this package, you must create multiple roles and permissions for each guard you want them to apply to. ie: role "foo" for guard_name "api" is treated entirely separately from role "foo" for guard_name "web". If you actually "use" both "api" and "web" guards, then you need to define roles/permissions for both guards, and make sure your app interacts with the correct variant of each, because each has a separate db record and separate Eloquent relationship.

jackpit93 commented 4 years ago

if give request with authorization header , i use jwt and Otherwise I'll check with Auth.

this is my config/auth.php:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the amount of seconds before a password confirmation
    | times out and the user is prompted to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => 10800,

];

and this is my seeder for create role and permission:

<?php

use App\Models\User\User;
use App\Models\User\UserRoles;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

class RoleAndPermissionsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // Reset cached roles and permissions
        app()[PermissionRegistrar::class]->forgetCachedPermissions();
        $this->createPermissions();
        $this->createRoles();
        $this->assignSuperAdminRoleToUser();

    }

    public function createPermissions()
    {
        $resources = ['User', 'Product', 'Order', 'OrderItem', 'Payment', 'Category', 'Card', 'OrderItemCard'];
        $functions = ['create', 'update', 'read', 'delete'];
        $guards=['web','api'];
        foreach ($guards as $guard){
            foreach ($resources as $resource) {
                foreach ($functions as $function) {
                    Permission::create(['name' => $function . $resource,'guard_name'=>$guard]);
                }
            }
        }

    }

    public function createRoles()
    {
        Role::create(['name' => UserRoles::SUPER_ADMIN,'guard_name'=>'web']);
        Role::create(['name' => UserRoles::SUPER_ADMIN,'guard_name'=>'api']);
        Role::create(['name' => UserRoles::USER,'guard_name'=>'web'])->givePermissionTo($this->getUserPermissions());
        Role::create(['name' => UserRoles::USER,'guard_name'=>'api'])->givePermissionTo($this->getUserPermissions());
    }

    public function assignSuperAdminRoleToUser()
    {
        factory(User::class)->create(
            [
                'first_name' => 'mohammad',
                'last_name' => 'najjary',
                'email' => 'example@gmail.com',
                'email_verified_at' => now(),
                'password' => bcrypt('123456'), // password
                'mobile' => '09381234567',
                'remember_token' => Str::random(10),
            ]
        )->assignRole(UserRoles::SUPER_ADMIN);
    }

    private function getUserPermissions()
    {
        return [
            'readUser',
            'updateUser',
            'readProduct',
            'readOrder',
            'readOrderItem',
            'readPayment',
            'readCategory',
            'readCard',
            'readOrderItemCard',
        ];
    }
}

after create api and web guard for each role and permission.There is still the same problem.

drbyte commented 4 years ago

Would the changes in #1384 solve your situation? Or does implementing the ideas posted in #1156 (use '*' for guard_name) solve it?

BoGnY commented 4 years ago

@drbyte I have exactly the same problem..

I've already built 90% of the project I'm working on, and now I'm stuck, with the delivery deadline in a few days, and the project is unusable due to this problem...

A role created through api middleware (via axios, just like @MohammadNajjary), even if guard_name is forced to web, it gets api on guard_name (which causes an unsolvable GuardDoesNotMatch exception)!

And #1384 not solve the issue, because I'm using version 3.11.0 of this package!

drbyte commented 4 years ago

@BoGnY you could use the suggestions in #1156. Or, to test #1384 you could edit the files directly in the vendor directory to test it. Then at least you'd know whether that would be a good option for you once it gets merged.