laravel / horizon

Dashboard and code-driven configuration for Laravel queues.
https://laravel.com/docs/horizon
MIT License
3.86k stars 654 forks source link

Gate in production does not work (403) #563

Closed windows1087 closed 5 years ago

windows1087 commented 5 years ago

I'm using Laravel 5.7 with Horizon.

It works in local, but if i set production in my .env file on our server, it gives me the 403 status code.

This is my service provider:

    protected function gate()
    {
        Gate::define('viewHorizon', function ($user) {
            return true;
        });
    }

Do i need to register this gate somewhere? I'm logged in as a user on my application.

driesvints commented 5 years ago

Can you please fill out the issue template?

wokes commented 5 years ago

You have to register Horizon's service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],
driesvints commented 5 years ago

Hi there,

Thanks for reporting but it looks like this is a question which can be asked on a support channel. Please only use this issue tracker for reporting bugs with the library itself. If you have a question on how to use functionality provided by this repo you can try one of the following channels:

Thanks!

mfn commented 5 years ago

I had the same problem (Laravel 5.8, Horizon 3.2.2)

My issue was this default code in the published HorizonServiceProvider:

        Gate::define('viewHorizon', function ($user) {
            return true;
        });

In my case I've no auth middleware because it's done on the webserver already via IP-addresses matching VPN, etc.

So, no authenticated user present.

However: when defining the closure like this function ($user) {… } and there is no authenticated user, this gate is entirely skipped because of this code https://github.com/laravel/framework/blob/9b7520a2ac0009d4c1ff2322e3ef673ef702e7e2/src/Illuminate/Auth/Access/Gate.php#L527-L528

The canBeCalledWithUser performs PHP reflection on the closure and if there's no auth user but $user is present, it will not execute the gate.

The solution: change the closure to have user optional: function ($user = null) { … }

driesvints commented 5 years ago

@mfn might be worth a pr to the docs if you have some time :)

shijunti19 commented 5 years ago

解决方案:更改闭包以使用户可选: function ($user = null) { … } Official Repair to Reduce User Difficulty

drbyte commented 5 years ago

Docs PR submitted: https://github.com/laravel/docs/pull/5351

hellowords commented 5 years ago

It is possible that you are not using the default guard。change the default guard in config/auth.php

such as

    'defaults' => [
        'guard'     => 'admin',
        'passwords' => 'front_users',
    ],
rajnishmishra20 commented 5 years ago

I had the same problem (Laravel 5.8, Horizon 3.2.2)

My issue was this default code in the published HorizonServiceProvider:

        Gate::define('viewHorizon', function ($user) {
            return true;
        });

In my case I've no auth middleware because it's done on the webserver already via IP-addresses matching VPN, etc.

So, no authenticated user present.

However: when defining the closure like this function ($user) {… } and there is no authenticated user, this gate is entirely skipped because of this code https://github.com/laravel/framework/blob/9b7520a2ac0009d4c1ff2322e3ef673ef702e7e2/src/Illuminate/Auth/Access/Gate.php#L527-L528

The canBeCalledWithUser performs PHP reflection on the closure and if there's no auth user but $user is present, it will not execute the gate.

The solution: change the closure to have user optional: function ($user = null) { … }

This solution worked . I am using Auth::guard('guard')->user().

Thanks.

vesper8 commented 4 years ago

I am using a non-default guard since my default guard is for my API and uses JWT tokens. So I had some trouble getting this to work correctly. In the end this is what works:

I created a login form using laravel/ui, but I am using a custom login controller which looks like this:

$credentials = $request->only('email', 'password');

if (auth()->guard('web')->attempt($credentials)) {
    return redirect()->intended('home');
}

Then in my horizon.php config I set this for my middlewares:

'middleware' => ['web', 'auth:web'],

Note that I specify that the auth middleware should use the web guard since that is not the default

And finally in my HorizonServiceProvider I have this

Gate::define('viewHorizon', function ($user = null) {
    return in_array(\Auth::guard('web')->user()->email, [
        'example@gmail.com',
    ]);
});
ryancwalsh commented 4 years ago

You have to register Horizon's service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],

Thank you! This took me hours to find. I don't think it's in the docs, and I don't know why.

vesper8 commented 4 years ago

@ryancwalsh that's because these lines get added when you run php artisan horizon:install so the docs just assume you will run this command

ryancwalsh commented 4 years ago

@vesper8 That's not my experience. I've run php artisan horizon:install multiple times (and even just tried it again after reverting my manual changes to config/app.php), and my config/app.php does not mention Horizon at all unless I add App\Providers\HorizonServiceProvider::class, manually.

drbyte commented 4 years ago

@ryancwalsh could it be that your config file is customized to a degree that the installer can't find the anchors it's trying to install against?

ie: it adds its content by doing a string replacement, but must find the string in the first place: https://github.com/laravel/horizon/blob/3.0/src/Console/InstallCommand.php

ryancwalsh commented 4 years ago

@drbyte Interesting. No, I think my config/app.php looks pretty normal. I see App\Providers\EventServiceProvider::class, in mine, which is what https://github.com/laravel/horizon/blob/3.0/src/Console/InstallCommand.php#L63 seems to be looking for. The double backslashes \\ aren't intuitive to me, but I assume that file has those intentionally.

ultrasamad commented 4 years ago

I am using a non-default guard since my default guard is for my API and uses JWT tokens. So I had some trouble getting this to work correctly. In the end this is what works:

I created a login form using laravel/ui, but I am using a custom login controller which looks like this:

$credentials = $request->only('email', 'password');

if (auth()->guard('web')->attempt($credentials)) {
    return redirect()->intended('home');
}

Then in my horizon.php config I set this for my middlewares:

'middleware' => ['web', 'auth:web'],

Note that I specify that the auth middleware should use the web guard since that is not the default

And finally in my HorizonServiceProvider I have this

Gate::define('viewHorizon', function ($user = null) {
    return in_array(\Auth::guard('web')->user()->email, [
        'example@gmail.com',
    ]);
});

Specifying auth:web in the horizon config is what I was missing. I also realized the HorizonServieProvider wasn't added automatically to config/app.php Since web wasn't my default auth guard. Thanks

brucek2 commented 4 years ago

I also ran into not having HorizonServiceProvider installed. My app initially integrated with Horizon 1.0, which does not seem to require this, and the upgrade guide to v3 did not mention it.

The other gotcha I ran into was needing to change the default production PHP 7.4 php.ini to allow pcntl_async_signals, pcntl_alarm, and pcntl_signal.

camiant commented 4 years ago

In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
    ...
        App\Providers\RouteServiceProvider::class,
        App\Providers\HorizonServiceProvider::class
    ],

HorizonServiceProvider must always be listed after RouteServiceProvider to make gate() work correctly.

shijunti19 commented 4 years ago

APP_ENV!=local to make gate() work correctly. APP_ENV=production

admench commented 4 years ago

I can confirm this worked for me:

Gate::define('viewHorizon', function ($user = null) {
            $u = Auth::guard('web')->user();
            return in_array($u->email, [
                'my@email.com'
            ]);
        });

...combining the null user and then manually setting the guard.

stuartcusackie commented 1 year ago

You have to register Horizon's service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],

I would have thought this was done automatically in 2023, but it turns out I still had to add it manually. Maybe because I am using an unusual setup - Laravel Splade JetStream

zsoltjanes commented 10 months ago

@taylorotwell can you confirm that we should add HorizonServiceProvider to the config/app.php (this part is missing in the documentation)?

vlad-004 commented 8 months ago

You have to register Horizon's service provider. In config/app.php:

   'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
    ...
        App\Providers\TelescopeServiceProvider::class,
        App\Providers\HorizonServiceProvider::class,
    ],

I already registered this, but I am still get problem with ALL requests to backend (they returns 403 error), with this text: image

Problem reproduced only on prod server and on local I cant to get this error. And I dont understand what is the problem. Maybe somebody knows?

Also I am already rewrite gate() function in app/Providers/HorizonServiceProvider.php , bit it doesn`t helps image

Lara 9, php 8.1 local, php8.2 server;

Stehos commented 8 months ago

I can not make it start to work. This protected horizon route with checking the user email is not working for me on php 8.2.

        "laravel/framework": "^10.3.0",
        "laravel/horizon": "^5.21",

it seems like I never access viewHorizon closure:

    protected function gate(): void
    {
        Gate::define('viewHorizon', function ($user = null) {
            dd('test');
            dd($user);
            return in_array($user->email, config('test.horizon_auth_emails'));
        });
    }

still receive Forbidden for all users

carma100 commented 8 months ago

Hi there!

In the beginning you should check that in your .env file environment variable APP_ENV=production or gate will won't to work.

Here is the way how I can solved it:

protected function gate(): void
    {
        Gate::define('viewHorizon', function ($user = null) {
            return Auth::guard('moonshine')->user()->hasRole("Admin");
        });
    }

don't forget to change guard to your own.

On my other project gate wants to work, seems like Gate::define not working, I'm solved problem by checking a role for user in the authorization function to:

/**
 * @return void
 */
protected function authorization(): void
{
    Horizon::auth(function ($request) {
        return Auth::user()->hasRole('your_role_here');
    });
}