diff --git a/api/app/Http/Controllers/Auth/OAuthController.php b/api/app/Http/Controllers/Auth/OAuthController.php index dfb1a407..18ad9761 100644 --- a/api/app/Http/Controllers/Auth/OAuthController.php +++ b/api/app/Http/Controllers/Auth/OAuthController.php @@ -93,6 +93,7 @@ class OAuthController extends Controller $oauthProvider->update([ 'access_token' => $socialiteUser->token, 'refresh_token' => $socialiteUser->refreshToken, + 'scopes' => $socialiteUser->approvedScopes ]); return $oauthProvider->user; @@ -139,6 +140,7 @@ class OAuthController extends Controller 'refresh_token' => $socialiteUser->refreshToken, 'name' => $socialiteUser->getName(), 'email' => $socialiteUser->getEmail(), + 'scopes' => $socialiteUser->approvedScopes ] ); return $user; diff --git a/api/app/Http/Controllers/Settings/OAuthProviderController.php b/api/app/Http/Controllers/Settings/OAuthProviderController.php index f477fe78..6f6d281b 100644 --- a/api/app/Http/Controllers/Settings/OAuthProviderController.php +++ b/api/app/Http/Controllers/Settings/OAuthProviderController.php @@ -26,8 +26,10 @@ class OAuthProviderController extends Controller $userId = Auth::id(); cache()->put("oauth-intention:{$userId}", $request->input('intention'), 60 * 5); + // Connecting an account for integrations purposes + // Adding full scopes to the driver return response()->json([ - 'url' => $service->getDriver()->getRedirectUrl(), + 'url' => $service->getDriver()->fullScopes()->getRedirectUrl(), ]); } @@ -47,6 +49,7 @@ class OAuthProviderController extends Controller 'refresh_token' => $driverUser->refreshToken, 'name' => $driverUser->getName(), 'email' => $driverUser->getEmail(), + 'scopes' => $driverUser->approvedScopes ] ); diff --git a/api/app/Http/Resources/OAuthProviderResource.php b/api/app/Http/Resources/OAuthProviderResource.php index a7fa2b47..58247ab6 100644 --- a/api/app/Http/Resources/OAuthProviderResource.php +++ b/api/app/Http/Resources/OAuthProviderResource.php @@ -32,6 +32,7 @@ class OAuthProviderResource extends JsonResource fn () => OAuthProviderUserResource::make($this->resource->user), null, ), + 'scopes' => $this->resource->scopes ]; } } diff --git a/api/app/Integrations/OAuth/Drivers/Contracts/OAuthDriver.php b/api/app/Integrations/OAuth/Drivers/Contracts/OAuthDriver.php index 78f04e29..1ca92a19 100644 --- a/api/app/Integrations/OAuth/Drivers/Contracts/OAuthDriver.php +++ b/api/app/Integrations/OAuth/Drivers/Contracts/OAuthDriver.php @@ -7,7 +7,14 @@ use Laravel\Socialite\Contracts\User; interface OAuthDriver { public function getRedirectUrl(): string; - public function setRedirectUrl($url): self; + public function setRedirectUrl(string $url): self; + public function setScopes(array $scopes): self; public function getUser(): User; public function canCreateUser(): bool; + + /** + * Set up all the scopes required by OpnForm for various integrations. + * This method configures the necessary permissions for the current OAuth driver. + */ + public function fullScopes(): self; } diff --git a/api/app/Integrations/OAuth/Drivers/OAuthGoogleDriver.php b/api/app/Integrations/OAuth/Drivers/OAuthGoogleDriver.php index aa73d6ad..e340f7f6 100644 --- a/api/app/Integrations/OAuth/Drivers/OAuthGoogleDriver.php +++ b/api/app/Integrations/OAuth/Drivers/OAuthGoogleDriver.php @@ -11,6 +11,7 @@ use Laravel\Socialite\Two\GoogleProvider; class OAuthGoogleDriver implements OAuthDriver { private ?string $redirectUrl = null; + private ?array $scopes = []; protected GoogleProvider $provider; @@ -22,7 +23,7 @@ class OAuthGoogleDriver implements OAuthDriver public function getRedirectUrl(): string { return $this->provider - ->scopes([Sheets::DRIVE_FILE]) + ->scopes($this->scopes ?? []) ->stateless() ->redirectUrl($this->redirectUrl ?? config('services.google.redirect')) ->with([ @@ -46,10 +47,20 @@ class OAuthGoogleDriver implements OAuthDriver return true; } - public function setRedirectUrl($url): OAuthDriver + public function setRedirectUrl(string $url): OAuthDriver { $this->redirectUrl = $url; return $this; } + public function setScopes(array $scopes): OAuthDriver + { + $this->scopes = $scopes; + return $this; + } + + public function fullScopes(): OAuthDriver + { + return $this->setScopes([Sheets::DRIVE_FILE]); + } } diff --git a/api/app/Models/OAuthProvider.php b/api/app/Models/OAuthProvider.php index 1ef6a095..2fef4cde 100644 --- a/api/app/Models/OAuthProvider.php +++ b/api/app/Models/OAuthProvider.php @@ -30,7 +30,8 @@ class OAuthProvider extends Model * @var array */ protected $hidden = [ - 'access_token', 'refresh_token', + 'access_token', + 'refresh_token', ]; protected function casts() @@ -38,6 +39,7 @@ class OAuthProvider extends Model return [ 'provider' => OAuthProviderService::class, 'token_expires_at' => 'datetime', + 'scopes' => 'array' ]; } diff --git a/api/database/migrations/2024_08_27_124933_add_scopes_to_oauth_providers.php b/api/database/migrations/2024_08_27_124933_add_scopes_to_oauth_providers.php new file mode 100644 index 00000000..163ae1e9 --- /dev/null +++ b/api/database/migrations/2024_08_27_124933_add_scopes_to_oauth_providers.php @@ -0,0 +1,35 @@ +json('scopes')->default(new Expression('(JSON_OBJECT())')); + } else { + $table->json('scopes')->default('{}'); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('oauth_providers', function (Blueprint $table) { + $table->dropColumn('scopes'); + }); + } +}; diff --git a/api/database/migrations/2024_08_29_110048_update_oauth_providers_token_columns.php b/api/database/migrations/2024_08_29_110048_update_oauth_providers_token_columns.php new file mode 100644 index 00000000..9adae6d7 --- /dev/null +++ b/api/database/migrations/2024_08_29_110048_update_oauth_providers_token_columns.php @@ -0,0 +1,33 @@ +text('access_token')->change(); + $table->text('refresh_token')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('oauth_providers', function (Blueprint $table) { + $table->string('access_token')->change(); + $table->string('refresh_token')->change(); + }); + } +}; diff --git a/client/components/forms/FlatSelectInput.vue b/client/components/forms/FlatSelectInput.vue index 7f6b4c41..a0aa6090 100644 --- a/client/components/forms/FlatSelectInput.vue +++ b/client/components/forms/FlatSelectInput.vue @@ -33,6 +33,9 @@ theme.FlatSelectInput.spacing.vertical, theme.FlatSelectInput.fontSize, theme.FlatSelectInput.option, + { + '!cursor-not-allowed !bg-gray-200': disableOptions.includes(option[optionKey]), + }, ]" @click="onSelect(option[optionKey])" > @@ -50,9 +53,15 @@ :theme="theme" /> -
- {{ option[displayKey] }} -
++ {{ option[displayKey] }} +
++ Adds new entry to spreadsheets on each form submission. +
+