Laravel 8 enable maintenance mode permanently - laravel

I am using this command to enable maintenance mode php artisan down --secret="1630542a-246b-4b66-afa1-dd72a4c43515.
And then, I access my site with exapmple.com/1630542a-246b-4b66-afa1-dd72a4c43515, to bypass the maintenance mode.
However, it seems to only work for one browsing session. That is, when I restart the computer, the maintenance cookie seems to be deleted and my site shows the 503 error.
So, how can I set an infinity lifetime maintenance cookie?

1. Override Create method from MaintenanceModeBypassCookie class. For this we will create a new file /App/Http/MaintenanceModeByoassCookie.php where we will include the following:
<?php
namespace App\Http;
use Illuminate\Support\Carbon;
use Symfony\Component\HttpFoundation\Cookie;
class MaintenanceModeBypassCookie extends \Illuminate\Foundation\Http\MaintenanceModeBypassCookie
{
/**
* Create a new maintenance mode bypass cookie.
*
* #param string $key
* #return \Symfony\Component\HttpFoundation\Cookie
*/
public static function create(string $key)
{
$expiresAt = Carbon::now()->addHours(1000);//Time expiration for your cookie
return new Cookie('laravel_maintenance', base64_encode(json_encode([
'expires_at' => $expiresAt->getTimestamp(),
'mac' => hash_hmac('sha256', $expiresAt->getTimestamp(), $key),
])), $expiresAt);
}
}
Note where we are overriding the expiration time for the cookie.
2. Add the following code in your /App/Http/Middleware/CheckForMaintenanceMode.php in order to override bypassResponse method:
/**
* #override
* Redirect the user back to the root of the application with a maintenance mode bypass cookie.
*
* #param string $secret
* #return \Illuminate\Http\RedirectResponse
*/
protected function bypassResponse(string $secret)
{
return redirect('/')->withCookie(
\App\Http\MaintenanceModeBypassCookie::create($secret)
);
}

Related

How can I prevent Laravel from setting a session cookie when the user is not auth?

By default, Laravel sets a Cookie called [APP_NAME]_session on every request. It's used for features as redirect()->back().
This cookie prevents my cache mechanism to work properly (FastCGI, Varnish, you name it)
If I'm one hundred percent sure I won't need it, is there a way to remove this cookie when the user is not auth, without preventing them to log in as usual ?
I'd like to show a different menu when my user is authed, so I can't apply a different middleware on some routes.
I created a new class, which extends the StartSession Middleware (referenced in app/Middleware/Kernel.php, inside the web group).
<?php
namespace App\Http\Middleware;
use Illuminate\Contracts\Session\Session;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Symfony\Component\HttpFoundation\Response;
class StartSession extends \Illuminate\Session\Middleware\StartSession
{
/**
* Start the session for the given request.
*
* #param Request $request
* #param Session $session
* #return Session
*/
protected function startSession(Request $request, $session): Session
{
return tap($session, function ($session) use ($request) {
$session->setRequestOnHandler($request);
if (Cookie::get(config("session.cookie"))) {
$session->start();
}
});
}
/**
* Add the session cookie to the application response.
*
* #param Response $response
* #param Session $session
* #return void
*/
protected function addCookieToResponse(Response $response, Session $session)
{
if (!auth()->check()) {
return;
}
if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
$response->headers->setCookie(new \Symfony\Component\HttpFoundation\Cookie(
$session->getName(), $session->getId(), $this->getCookieExpirationDate(),
$config['path'], $config['domain'], $config['secure'] ?? false,
$config['http_only'] ?? true, false, $config['same_site'] ?? null
));
}
}
}
The two importants part are :
in startSession() :
if (Cookie::get(config("session.cookie"))) {
$session->start();
}
This part prevents the session from being created when the user wasn't already authed.
in addCookieToResponse() :
if (!auth()->check()) {
return;
}
This part prevents Laravel from setting the cookie as long as the user is not authed.

What is the database password of a tenant's database (Laravel Hyn)

I have deployed a multi-tenant site on the server and I'm using Laravel Hyn (5.6). I am able to create tenant's database. But I wonder what is the password used to create the tenant's database. I'm asking this because if ever we need to check on a tenant's database, how can we access it? And I'm trying to access it remotely. I can get into the app's main database which holds the tenant's database name. But I don't know how to access the tenant's actual database because I don't know what password Hyn assigned to it.
Checking Laravel Hyn's docs, It doesnt mention anything about it.
The functionality you are looking for is in class Hyn\Tenancy\Generators\Database\DefaultPasswordGenerator function generate
Possibly if we can adapt this functionality to come up with a command that accepts the database name and returns the password. We can come up with something like this
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Hyn\Tenancy\Models\Website;
use Hyn\Tenancy\Contracts\Website as WebContract;
class GeneratePassword extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'password:generate {--database=}';
/** php artisan password:generate --database=6003c07826144979a4176b3290963ba3
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
$database=$this->option('database');
$website =Website::where('uuid', $database)->first();
$password=$this->generate($website);
$this->info('password :'.$password);
return 0;
}
/**
* #param Website $website
* #return string
*/
private function generate(WebContract $website) : string
{
$key = config('tenancy.key');
// Backward compatibility
if ($key === null) {
return md5(sprintf(
'%s.%d',
$this->app['config']->get('app.key'),
$website->id
));
}
return md5(sprintf(
'%d.%s.%s.%s',
$website->id,
$website->uuid,
$website->created_at,
$key
));
}
}
You will now be able to generate passwords via artisan console for example
php artisan password:generate --database=6003c07826144979a4176b3290963ba3
Use the generated password to connect to your database using tenant credentials (username is the same as database name).

Laravel policy not running on update/delete model

I'm trying to make this policy stuff work, and i have followed the documentation. But it doesn't seem like the policy code is not even run.
I have Role model. And i created RolePolicy. The thing i want to do in the policy is to ensure that the role with the ID of 1 never-ever gets updated or deleted.
My RolePolicy looks like this:
<?php
namespace App\Policies;
use App\Models\Role;
use Illuminate\Support\Facades\Response;
class RolePolicy
{
/**
* Determine whether the user can update the model.
*
* #param \App\User $user
* #param \App\Models\Role $role
* #return mixed
*/
public function update(Role $role)
{
return $role->id === 1
? Response::deny('Cannot change super-admin role')
: Response::allow();
}
/**
* Determine whether the user can delete the model.
*
* #param \App\User $user
* #param \App\Models\Role $role
* #return mixed
*/
public function delete(Role $role)
{
return $role->id === 1
? Response::deny('Cannot delete super-admin role')
: Response::allow();
}
}
I even tried to do a dd() inside both delete and update method in the policy, but when i try to delete/update the model with the ID of 1, nothing happens. The dd wont run, nor will the response in the current code above.
I have registered the policy in the AuthServiceProvider where i also have this gate to give the super-admin all the permissions.
<?php
namespace App\Providers;
use App\Models\Role;
use App\Policies\RolePolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
Role::class => RolePolicy::class
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
// Implicitly grant "Super Admin" role all permissions
// This works in the app by using gate-related functions like auth()->user->can() and #can()
Gate::before(function($user, $ability) {
return $user->hasRole('super-admin') ? true : null;
});
}
}
Here is also my RoleController method for updating the Role model:
/**
* Edit role
*
* #param Edit $request
* #param Role $role
* #return void
*/
public function postEdit(Edit $request, Role $role)
{
# Validation checks happens in the FormRequest
# Session flash also happens in FormRequest
# Update model
$role->update([
'name' => $request->name
]);
# Sync permissions
$permissions = Permission::whereIn('name', $request->input('permissions', []))->get();
$role->syncPermissions($permissions);
return redirect(route('dashboard.roles.edit.get', ['role' => $role->id]))->with('success', 'Changes saved');
}
Does the gate i use to give all permissions have anything to do with the policy not running? Or what am i doing wrong here?
Thanks in advance if anyone can point me in the right direction.
The User model that is included with your Laravel application includes two helpful methods for authorizing actions: can and cant. The can method receives the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given Role model:
if ($user->can('update', $role)) {
//
}
If a policy is registered for the given model, the can method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the can method will attempt to call the Closure based Gate matching the given action name.
Via Controller Helpers
In addition to helpful methods provided to the User model, Laravel provides a helpful authorize method to any of your controllers which extend the App\Http\Controllers\Controller base class. Like the can method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the authorize method will throw an Illuminate\Auth\Access\AuthorizationException, which the default Laravel exception handler will convert to an HTTP response with a 403 status code:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Role;
use Illuminate\Http\Request;
class RoleController extends Controller
{
/**
* Update the given role.
*
* #param Request $request
* #param role $role
* #return Response
* #throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Role $role)
{
$this->authorize('update', $role);
// The current user can update the role...
}
}
The Gate::before method in the AuthServiceProvider was the problem. Removed this and rewrote the permissions, policies and some gates to get the error messages from the policies.
Decided to give the role super-admin the permission * and check for this with $user->can() and middleware .....->middlware('can:*') and everything is working now.

Laravel run code when new user registrated with default registration code

Im running a laravel 6.9 application with default authentication/registration.
I want to maintain the default registration process but i want to run a curl command if a user has registered.
Is it possible to hook into the default registration process and extend it with extra code?
Observer is good point in code where you can, well, observe if user is just registered but good place to put additional code after user has been registered is event/listener group. There is already Registered event set in EventServiceProvider so you would need to put additional listener beside one already set there (for sending email to newly registered user if opted). To have all sorted next steps should be followed (disclaimer: I am taking that you use all default auth code so far):
First copy registered(Request $request, $user) method from Illuminate\Foundation\Auth\RegistersUsers.php trait to default App\Http\Controllers\Auth\RegisterController
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
//
}
So you would override that piece of default code which is meant to stay intact (as should every code from vendor directory).
Then, you would need to create listener. In App\Providers\EventServiceProvider::listen array, add one more class into value array so it should looks like
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
\App\Listeners\FooBarBaz::class,
],
];
Don't bother for not having created class already, next artisan command will do that for you:
php artisan event:generate
Now, in \App\Listeners\FooBarBaz::class you can make your custom code related to new user:
namespace App\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class FooBarBaz
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param Registered $event
* #return void
*/
public function handle(Registered $event)
{
// $event->user is accessible here
//
// this method should return void, it is just executed
}
}
Now, back to registered method of RegisterController. Here you would need to initiate event:
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
event(new \Illuminate\Auth\Events\Registered($user));
}
And you are done.
I wanted to show you use of already lot of prebuilt code although Observer is also good place. But also for calling event; I wouldn't put more code than this event(new \Illuminate\Auth\Events\Registered($user)); line into UserObserver::created(User $user). Whole part with event/listener is very good and decoupled now. Of course, you can make even custom event not using default Illuminate's one, just set that new key => value into EventServiceProvider.
Events
Observers

Laravel Passport - Authenticating web app against api with Custom UserProvider

I'm really new building laravel apps, I have a restful laravel API and a web app, I want the client web app to authenticate against the API and store the user in the session, I've registered a new UserProvider and set it on the config`s auth like bellow
ServiceProvider
public function boot()
{
$this->registerPolicies();
Auth::provider('apiAuthServiceProvider', function ($app, $config) {
return new UserProvider(new ApiUserService());
});
}
Config/Auth
'providers' => [
'users' => [
'driver' => 'apiAuthServiceProvider',
],
],
UserProvider Class
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Auth\UserProvider as IlluminateUserProvider;
class UserProvider implements IlluminateUserProvider
{
private $userService;
public function __construct($userService)
{
$this->userService = $userService;
}
/**
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
// Get and return a user by their unique identifier
}
/**
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
// Get and return a user by their unique identifier and "remember me" token
}
/**
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
// Save the given "remember me" token for the given user
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// Get and return a user by looking up the given credentials
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Check that given credentials belong to the given user
}
}
The Custom UserProvider injects a UserService class, with is responsible for making requests to the API and return the user...
I`m so lost, what UserProvider methods should i override from "UserProvider" Interface? "retrieveById", "retrieveByToken", "updateRememberToken", "retrieveByCredentials" and "validateCredentials" ? Or should I override all of them? Considering the the client web app will have a login form, and the user will authenticate sending the email and password (grant_type = password), I'm also confusing about the token, how should I store the token and refresh token in the session? Is that possible to set session timeout as the same as the token expiration time? Where would I call the retrieveByCredentials's UserProvider to pass the authentication params? Thanks in advance....
You should override only the functions you need. Most of the standard functionality should be already defined in the User Provider you are inheriting from. I've only inherited my custom user providers from Illuminate\Auth\EloquentUserProvider (Laravel 5.4 here), so please double check how the class you are inheriting from works. If, for example, you need to retrieve your user by an ID different than your default id field, you should override retrieveById.

Resources