mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
- Require write access on the target environment to clone or move a resource - Check read access on shared variables before a reference is added or resolved - Limit team credentials, git sources, and imports of other backups to team-wide writers - Require the delete level for backup, volume, storage, and task deletes - Require create access for previews, new projects, and git sources - Use the token team for the access scope, and check team-wide access on Livewire updates - Require instance admin to toggle the API and MCP server - Hide server pickers and broadcast details from members without access
198 lines
6.8 KiB
PHP
198 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace App\Providers;
|
|
|
|
use App\Auth\Oidc\OidcDiscoveryService;
|
|
use App\Auth\Oidc\OidcTokenValidator;
|
|
use App\Auth\Oidc\Socialite\OidcProvider;
|
|
use App\Http\Middleware\EnsureTeamWideAccess;
|
|
use App\Models\PersonalAccessToken;
|
|
use App\Models\Server;
|
|
use App\Rules\SafeExternalUrl;
|
|
use App\Services\Access\AccessResolver;
|
|
use App\Support\AtomicFilesystem;
|
|
use App\Support\RetryHttpConnectionFailures;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Foundation\Console\ConfigCacheCommand;
|
|
use Illuminate\Foundation\Console\RouteCacheCommand;
|
|
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
|
use Illuminate\Foundation\Http\Middleware\TrimStrings;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\App;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Illuminate\Support\Once;
|
|
use Illuminate\Support\ServiceProvider;
|
|
use Illuminate\Validation\Rules\Password;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use Laravel\Socialite\Contracts\Factory as SocialiteFactory;
|
|
use Livewire\Livewire;
|
|
use Stripe\StripeClient;
|
|
|
|
class AppServiceProvider extends ServiceProvider
|
|
{
|
|
public function register(): void
|
|
{
|
|
$this->app->scoped(AccessResolver::class);
|
|
$this->app->bind(StripeClient::class, fn () => new StripeClient(config('subscription.stripe_api_key')));
|
|
|
|
// Requests must never read a partly written cache file. See AtomicFilesystem.
|
|
$this->app->extend(ConfigCacheCommand::class, fn () => new ConfigCacheCommand(new AtomicFilesystem));
|
|
$this->app->extend(RouteCacheCommand::class, fn () => new RouteCacheCommand(new AtomicFilesystem));
|
|
}
|
|
|
|
public function boot(): void
|
|
{
|
|
$this->configureCommands();
|
|
$this->configureModels();
|
|
$this->configurePasswords();
|
|
$this->configureSanctumModel();
|
|
$this->configureHttpClient();
|
|
$this->configureGitHubHttp();
|
|
$this->configureGitLabHttp();
|
|
$this->configureOidcSocialite();
|
|
$this->configureQueue();
|
|
$this->configureServerTransferImport();
|
|
$this->configureLivewire();
|
|
}
|
|
|
|
/**
|
|
* Livewire updates skip route middleware. Run the team-wide access check again on each update,
|
|
* so a page that is already open stops working when an admin limits the member to some projects.
|
|
*/
|
|
private function configureLivewire(): void
|
|
{
|
|
Livewire::addPersistentMiddleware([EnsureTeamWideAccess::class]);
|
|
}
|
|
|
|
/**
|
|
* A server transfer bundle carries stored values (files, variables, keys, compose files) that must
|
|
* arrive unchanged, so the import request skips the global trimming and empty-string conversion.
|
|
*/
|
|
private function configureServerTransferImport(): void
|
|
{
|
|
$isImport = fn (Request $request): bool => $request->is('api/v1/servers/import');
|
|
TrimStrings::skipWhen($isImport);
|
|
ConvertEmptyStringsToNull::skipWhen($isImport);
|
|
}
|
|
|
|
/**
|
|
* Queue workers are long-running processes, so once() values (e.g. instanceSettings())
|
|
* and the Server identity map would stay stale across jobs. Flush them before each job,
|
|
* like a fresh web request.
|
|
*/
|
|
private function configureQueue(): void
|
|
{
|
|
Queue::before(function (): void {
|
|
Once::flush();
|
|
Server::flushIdentityMap();
|
|
});
|
|
}
|
|
|
|
private function configureCommands(): void
|
|
{
|
|
if (App::isProduction()) {
|
|
DB::prohibitDestructiveCommands();
|
|
}
|
|
}
|
|
|
|
private function configureModels(): void
|
|
{
|
|
// Disabled because it's causing issues with the application
|
|
// Model::shouldBeStrict();
|
|
}
|
|
|
|
private function configurePasswords(): void
|
|
{
|
|
Password::defaults(function () {
|
|
return App::isProduction()
|
|
? Password::min(8)
|
|
->mixedCase()
|
|
->letters()
|
|
->numbers()
|
|
->symbols()
|
|
->uncompromised()
|
|
: Password::min(8)->letters();
|
|
});
|
|
}
|
|
|
|
private function configureSanctumModel(): void
|
|
{
|
|
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
|
|
Sanctum::authenticateAccessTokensUsing(
|
|
fn (PersonalAccessToken $token, bool $isValid): bool => $isValid && ! $token->isPaused()
|
|
);
|
|
}
|
|
|
|
private function configureOidcSocialite(): void
|
|
{
|
|
if (! $this->app->bound(SocialiteFactory::class)) {
|
|
return;
|
|
}
|
|
|
|
$this->app->make(SocialiteFactory::class)->extend('oidc', function ($app) {
|
|
return new OidcProvider(
|
|
$app['request'],
|
|
$app->make(OidcDiscoveryService::class),
|
|
$app->make(OidcTokenValidator::class),
|
|
'',
|
|
'',
|
|
'',
|
|
);
|
|
});
|
|
}
|
|
|
|
private function configureHttpClient(): void
|
|
{
|
|
Http::globalOptions(['connect_timeout' => 10, 'timeout' => 30]);
|
|
Http::globalMiddleware(new RetryHttpConnectionFailures);
|
|
}
|
|
|
|
private function configureGitHubHttp(): void
|
|
{
|
|
Http::macro('GitSource', function (string $url) {
|
|
return Http::withOptions([
|
|
...SafeExternalUrl::httpClientOptions(
|
|
$url,
|
|
allowPrivateNetworks: SafeExternalUrl::gitSourcesMayUsePrivateNetworks(),
|
|
),
|
|
'allow_redirects' => SafeExternalUrl::sameOriginRedirectOptions(),
|
|
]);
|
|
});
|
|
|
|
Http::macro('GitHub', function (string $api_url, ?string $github_access_token = null) {
|
|
if ($github_access_token) {
|
|
return Http::GitSource($api_url)->withHeaders([
|
|
'X-GitHub-Api-Version' => '2022-11-28',
|
|
'Accept' => 'application/vnd.github.v3+json',
|
|
'Authorization' => "Bearer $github_access_token",
|
|
])->baseUrl($api_url);
|
|
} else {
|
|
return Http::GitSource($api_url)->withHeaders([
|
|
'Accept' => 'application/vnd.github.v3+json',
|
|
])->baseUrl($api_url);
|
|
}
|
|
});
|
|
}
|
|
|
|
private function configureGitLabHttp(): void
|
|
{
|
|
Http::macro('GitLab', function (string $api_url, ?string $access_token = null) {
|
|
$client = Http::GitSource($api_url)->withHeaders([
|
|
'Accept' => 'application/json',
|
|
])->baseUrl($api_url);
|
|
|
|
return $access_token ? $client->withToken($access_token) : $client;
|
|
});
|
|
|
|
Http::macro('Gitea', function (string $api_url, ?string $access_token = null) {
|
|
$client = Http::GitSource($api_url)->withHeaders([
|
|
'Accept' => 'application/json',
|
|
])->baseUrl($api_url);
|
|
|
|
return $access_token ? $client->withToken($access_token) : $client;
|
|
});
|
|
}
|
|
}
|