From fa5fee50a0f6c1edf1544ff7f8b7d38fcddc9d07 Mon Sep 17 00:00:00 2001 From: Vehikl Date: Thu, 1 May 2025 16:40:21 -0400 Subject: [PATCH] Refactor OauthProviders to use Singleton --- app/Extensions/OAuth/OAuthProvider.php | 41 +++++++++++++++++ app/Extensions/OAuth/OAuthSchemaInterface.php | 33 +++++++++++++ .../OAuth/Providers/CommonProvider.php | 38 --------------- .../AuthentikSchema.php} | 15 +----- app/Extensions/OAuth/Schemas/CommonSchema.php | 31 +++++++++++++ .../DiscordSchema.php} | 15 +----- .../GithubSchema.php} | 15 +----- .../GitlabSchema.php} | 15 +----- .../OAuthSchema.php} | 20 ++------ .../SteamSchema.php} | 15 +----- app/Filament/Admin/Pages/Settings.php | 11 ++++- app/Filament/Pages/Auth/EditProfile.php | 9 ++-- app/Filament/Pages/Auth/Login.php | 9 ++-- app/Http/Controllers/Auth/OAuthController.php | 14 +++--- app/Providers/AppServiceProvider.php | 46 +++++++++++-------- phpstan.neon | 2 +- 16 files changed, 174 insertions(+), 155 deletions(-) create mode 100644 app/Extensions/OAuth/OAuthProvider.php create mode 100644 app/Extensions/OAuth/OAuthSchemaInterface.php delete mode 100644 app/Extensions/OAuth/Providers/CommonProvider.php rename app/Extensions/OAuth/{Providers/AuthentikProvider.php => Schemas/AuthentikSchema.php} (83%) create mode 100644 app/Extensions/OAuth/Schemas/CommonSchema.php rename app/Extensions/OAuth/{Providers/DiscordProvider.php => Schemas/DiscordSchema.php} (84%) rename app/Extensions/OAuth/{Providers/GithubProvider.php => Schemas/GithubSchema.php} (86%) rename app/Extensions/OAuth/{Providers/GitlabProvider.php => Schemas/GitlabSchema.php} (85%) rename app/Extensions/OAuth/{Providers/OAuthProvider.php => Schemas/OAuthSchema.php} (80%) rename app/Extensions/OAuth/{Providers/SteamProvider.php => Schemas/SteamSchema.php} (84%) diff --git a/app/Extensions/OAuth/OAuthProvider.php b/app/Extensions/OAuth/OAuthProvider.php new file mode 100644 index 000000000..9ecb4aa9b --- /dev/null +++ b/app/Extensions/OAuth/OAuthProvider.php @@ -0,0 +1,41 @@ +providers; + } + + /** @return OAuthSchemaInterface[] */ + public function getEnabled(): array + { + return collect($this->providers) + ->filter(fn (OAuthSchemaInterface $provider) => $provider->isEnabled()) + ->all(); + } + + public function register(OAuthSchemaInterface $provider): void + { + if (array_key_exists($provider->getId(), $this->providers)) { + return; + } + + config()->set('services.' . $provider->getId(), array_merge($provider->getServiceConfig(), ['redirect' => '/auth/oauth/callback/' . $provider->getId()])); + + if ($provider->getProviderClass()) { + Event::listen(fn (SocialiteWasCalled $event) => $event->extendSocialite($provider->getId(), $provider->getProviderClass())); + } + + $this->providers[$provider->getId()] = $provider; + } +} diff --git a/app/Extensions/OAuth/OAuthSchemaInterface.php b/app/Extensions/OAuth/OAuthSchemaInterface.php new file mode 100644 index 000000000..6fd568e27 --- /dev/null +++ b/app/Extensions/OAuth/OAuthSchemaInterface.php @@ -0,0 +1,33 @@ + + */ + public function getServiceConfig(): array; + + /** @return Component[] */ + public function getSettingsForm(): array; + + /** @return Step[] */ + public function getSetupSteps(): array; + + public function getName(): string; + + public function getIcon(): ?string; + + public function getHexColor(): ?string; + + public function isEnabled(): bool; +} diff --git a/app/Extensions/OAuth/Providers/CommonProvider.php b/app/Extensions/OAuth/Providers/CommonProvider.php deleted file mode 100644 index db48ff03c..000000000 --- a/app/Extensions/OAuth/Providers/CommonProvider.php +++ /dev/null @@ -1,38 +0,0 @@ -id; - } - - public function getProviderClass(): ?string - { - return $this->providerClass; - } - - public function getIcon(): ?string - { - return $this->icon; - } - - public function getHexColor(): ?string - { - return $this->hexColor; - } - - public static function register(Application $app, string $id, ?string $providerClass = null, ?string $icon = null, ?string $hexColor = null): static - { - return new self($app, $id, $providerClass, $icon, $hexColor); - } -} diff --git a/app/Extensions/OAuth/Providers/AuthentikProvider.php b/app/Extensions/OAuth/Schemas/AuthentikSchema.php similarity index 83% rename from app/Extensions/OAuth/Providers/AuthentikProvider.php rename to app/Extensions/OAuth/Schemas/AuthentikSchema.php index f6be54a99..93e7f8ad8 100644 --- a/app/Extensions/OAuth/Providers/AuthentikProvider.php +++ b/app/Extensions/OAuth/Schemas/AuthentikSchema.php @@ -1,19 +1,13 @@ id; + } + + public function getProviderClass(): ?string + { + return $this->providerClass; + } + + public function getIcon(): ?string + { + return $this->icon; + } + + public function getHexColor(): ?string + { + return $this->hexColor; + } +} diff --git a/app/Extensions/OAuth/Providers/DiscordProvider.php b/app/Extensions/OAuth/Schemas/DiscordSchema.php similarity index 84% rename from app/Extensions/OAuth/Providers/DiscordProvider.php rename to app/Extensions/OAuth/Schemas/DiscordSchema.php index 5981172f9..7217eff04 100644 --- a/app/Extensions/OAuth/Providers/DiscordProvider.php +++ b/app/Extensions/OAuth/Schemas/DiscordSchema.php @@ -1,23 +1,17 @@ @@ -25,25 +25,13 @@ abstract class OAuthProvider return $id ? static::$providers[$id] : static::$providers; } - protected function __construct(protected Application $app) + public function __construct() { - if (array_key_exists($this->getId(), static::$providers)) { - if (!$this->app->runningUnitTests()) { - logger()->warning("Tried to create duplicate OAuth provider with id '{$this->getId()}'"); - } - - return; - } - - config()->set('services.' . $this->getId(), array_merge($this->getServiceConfig(), ['redirect' => '/auth/oauth/callback/' . $this->getId()])); - if ($this->getProviderClass()) { Event::listen(function (SocialiteWasCalled $event) { $event->extendSocialite($this->getId(), $this->getProviderClass()); }); } - - static::$providers[$this->getId()] = $this; } abstract public function getId(): string; diff --git a/app/Extensions/OAuth/Providers/SteamProvider.php b/app/Extensions/OAuth/Schemas/SteamSchema.php similarity index 84% rename from app/Extensions/OAuth/Providers/SteamProvider.php rename to app/Extensions/OAuth/Schemas/SteamSchema.php index 85e61ee1b..5ace4db7a 100644 --- a/app/Extensions/OAuth/Providers/SteamProvider.php +++ b/app/Extensions/OAuth/Schemas/SteamSchema.php @@ -1,22 +1,16 @@ |null */ public ?array $data = []; @@ -59,6 +61,11 @@ class Settings extends Page implements HasForms $this->form->fill(); } + public function boot(OAuthProvider $oauthProvider): void + { + $this->oauthProvider = $oauthProvider; + } + public static function canAccess(): bool { return auth()->user()->can('view settings'); @@ -523,7 +530,7 @@ class Settings extends Page implements HasForms { $formFields = []; - $oauthProviders = OAuthProvider::get(); + $oauthProviders = $this->oauthProvider->get(); foreach ($oauthProviders as $oauthProvider) { $id = Str::upper($oauthProvider->getId()); $name = Str::title($oauthProvider->getId()); diff --git a/app/Filament/Pages/Auth/EditProfile.php b/app/Filament/Pages/Auth/EditProfile.php index d0243fff9..cac547e5a 100644 --- a/app/Filament/Pages/Auth/EditProfile.php +++ b/app/Filament/Pages/Auth/EditProfile.php @@ -3,7 +3,7 @@ namespace App\Filament\Pages\Auth; use App\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid; -use App\Extensions\OAuth\Providers\OAuthProvider; +use App\Extensions\OAuth\OAuthProvider; use App\Facades\Activity; use App\Models\ActivityLog; use App\Models\ApiKey; @@ -51,9 +51,12 @@ class EditProfile extends BaseEditProfile { private ToggleTwoFactorService $toggleTwoFactorService; - public function boot(ToggleTwoFactorService $toggleTwoFactorService): void + protected OAuthProvider $oauthProvider; + + public function boot(ToggleTwoFactorService $toggleTwoFactorService, OAuthProvider $oauthProvider): void { $this->toggleTwoFactorService = $toggleTwoFactorService; + $this->oauthProvider = $oauthProvider; } public function getMaxWidth(): MaxWidth|string @@ -63,7 +66,7 @@ class EditProfile extends BaseEditProfile protected function getForms(): array { - $oauthProviders = collect(OAuthProvider::get())->filter(fn (OAuthProvider $provider) => $provider->isEnabled())->all(); + $oauthProviders = $this->oauthProvider->getEnabled(); return [ 'form' => $this->form( diff --git a/app/Filament/Pages/Auth/Login.php b/app/Filament/Pages/Auth/Login.php index 60179fff1..3cd9eba31 100644 --- a/app/Filament/Pages/Auth/Login.php +++ b/app/Filament/Pages/Auth/Login.php @@ -3,7 +3,7 @@ namespace App\Filament\Pages\Auth; use App\Extensions\Captcha\Providers\CaptchaProvider; -use App\Extensions\OAuth\Providers\OAuthProvider; +use App\Extensions\OAuth\OAuthProvider; use App\Models\User; use Filament\Facades\Filament; use Filament\Forms\Components\Actions; @@ -25,9 +25,12 @@ class Login extends BaseLogin public bool $verifyTwoFactor = false; - public function boot(Google2FA $google2FA): void + protected OAuthProvider $oauthProvider; + + public function boot(Google2FA $google2FA, OAuthProvider $oauthProvider): void { $this->google2FA = $google2FA; + $this->oauthProvider = $oauthProvider; } public function authenticate(): ?LoginResponse @@ -147,7 +150,7 @@ class Login extends BaseLogin { $actions = []; - $oauthProviders = collect(OAuthProvider::get())->filter(fn (OAuthProvider $provider) => $provider->isEnabled())->all(); + $oauthProviders = $this->oauthProvider->getEnabled(); foreach ($oauthProviders as $oauthProvider) { diff --git a/app/Http/Controllers/Auth/OAuthController.php b/app/Http/Controllers/Auth/OAuthController.php index 313bca976..80a10978c 100644 --- a/app/Http/Controllers/Auth/OAuthController.php +++ b/app/Http/Controllers/Auth/OAuthController.php @@ -2,17 +2,17 @@ namespace App\Http\Controllers\Auth; -use App\Extensions\OAuth\Providers\OAuthProvider; +use App\Extensions\OAuth\Schemas\OAuthSchema; use App\Filament\Pages\Auth\EditProfile; -use Filament\Notifications\Notification; -use Illuminate\Auth\AuthManager; -use Illuminate\Http\RedirectResponse; -use Laravel\Socialite\Facades\Socialite; use App\Http\Controllers\Controller; use App\Models\User; use App\Services\Users\UserUpdateService; use Exception; +use Filament\Notifications\Notification; +use Illuminate\Auth\AuthManager; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Laravel\Socialite\Facades\Socialite; class OAuthController extends Controller { @@ -27,7 +27,7 @@ class OAuthController extends Controller public function redirect(string $driver): RedirectResponse { // Driver is disabled - redirect to normal login - if (!OAuthProvider::get($driver)->isEnabled()) { + if (!OAuthSchema::get($driver)->isEnabled()) { return redirect()->route('auth.login'); } @@ -40,7 +40,7 @@ class OAuthController extends Controller public function callback(Request $request, string $driver): RedirectResponse { // Driver is disabled - redirect to normal login - if (!OAuthProvider::get($driver)->isEnabled()) { + if (!OAuthSchema::get($driver)->isEnabled()) { return redirect()->route('auth.login'); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 7ba52a820..0b3d48032 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -12,19 +12,20 @@ use App\Checks\ScheduleCheck; use App\Checks\UsedDiskSpaceCheck; use App\Extensions\Avatar\Providers\GravatarProvider; use App\Extensions\Avatar\Providers\UiAvatarsProvider; -use App\Extensions\OAuth\Providers\GitlabProvider; -use App\Models; use App\Extensions\Captcha\Providers\TurnstileProvider; use App\Extensions\Features\GSLToken; use App\Extensions\Features\JavaVersion; use App\Extensions\Features\MinecraftEula; use App\Extensions\Features\PIDLimit; use App\Extensions\Features\SteamDiskSpace; -use App\Extensions\OAuth\Providers\AuthentikProvider; -use App\Extensions\OAuth\Providers\CommonProvider; -use App\Extensions\OAuth\Providers\DiscordProvider; -use App\Extensions\OAuth\Providers\GithubProvider; -use App\Extensions\OAuth\Providers\SteamProvider; +use App\Extensions\OAuth\OAuthProvider; +use App\Extensions\OAuth\Schemas\AuthentikSchema; +use App\Extensions\OAuth\Schemas\CommonSchema; +use App\Extensions\OAuth\Schemas\DiscordSchema; +use App\Extensions\OAuth\Schemas\GithubSchema; +use App\Extensions\OAuth\Schemas\GitlabSchema; +use App\Extensions\OAuth\Schemas\SteamSchema; +use App\Models; use App\Services\Helpers\SoftwareVersionService; use Dedoc\Scramble\Scramble; use Dedoc\Scramble\Support\Generator\OpenApi; @@ -104,20 +105,25 @@ class AppServiceProvider extends ServiceProvider Scramble::registerApi('application', ['api_path' => 'api/application', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens); Scramble::registerApi('client', ['api_path' => 'api/client', 'info' => ['version' => '1.0']])->afterOpenApiGenerated($bearerTokens); - // Default OAuth providers included with Socialite - CommonProvider::register($app, 'facebook', null, 'tabler-brand-facebook-f', '#1877f2'); - CommonProvider::register($app, 'x', null, 'tabler-brand-x-f', '#1da1f2'); - CommonProvider::register($app, 'linkedin', null, 'tabler-brand-linkedin-f', '#0a66c2'); - CommonProvider::register($app, 'google', null, 'tabler-brand-google-f', '#4285f4'); - GithubProvider::register($app); - GitlabProvider::register($app); - CommonProvider::register($app, 'bitbucket', null, 'tabler-brand-bitbucket-f', '#205081'); - CommonProvider::register($app, 'slack', null, 'tabler-brand-slack', '#6ecadc'); + $this->app->singleton(OAuthProvider::class, function ($app) { + $provider = new OAuthProvider(); + // Default OAuth providers included with Socialite + $provider->register(new CommonSchema('facebook', null, 'tabler-brand-facebook-f', '#1877f2')); + $provider->register(new CommonSchema('x', null, 'tabler-brand-x-f', '#1da1f2')); + $provider->register(new CommonSchema('linkedin', null, 'tabler-brand-linkedin-f', '#0a66c2')); + $provider->register(new CommonSchema('google', null, 'tabler-brand-google-f', '#4285f4')); + $provider->register(new GithubSchema()); + $provider->register(new GitlabSchema()); + $provider->register(new CommonSchema('bitbucket', null, 'tabler-brand-bitbucket-f', '#205081')); + $provider->register(new CommonSchema('slack', null, 'tabler-brand-slack', '#6ecadc')); - // Additional OAuth providers from socialiteproviders.com - AuthentikProvider::register($app); - DiscordProvider::register($app); - SteamProvider::register($app); + // Additional OAuth providers from socialiteproviders.com + $provider->register(new AuthentikSchema()); + $provider->register(new DiscordSchema()); + $provider->register(new SteamSchema()); + + return $provider; + }); // Default Captcha provider TurnstileProvider::register($app); diff --git a/phpstan.neon b/phpstan.neon index 3320b0842..fc6673e16 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -21,5 +21,5 @@ parameters: paths: - app/Console/Commands/Environment/*.php - app/Extensions/Captcha/Providers/*.php - - app/Extensions/OAuth/Providers/*.php + - app/Extensions/OAuth/Schemas/*.php - app/Filament/Admin/Pages/Settings.php