Skip to content

[Feat] Storage Providers: Edit the connection - #1224

Merged
RichardAnderson merged 5 commits into
vitodeploy:4.xfrom
RichardAnderson:feat/edit-storage-providers
Aug 2, 2026
Merged

[Feat] Storage Providers: Edit the connection#1224
RichardAnderson merged 5 commits into
vitodeploy:4.xfrom
RichardAnderson:feat/edit-storage-providers

Conversation

@RichardAnderson

@RichardAnderson RichardAnderson commented Aug 2, 2026

Copy link
Copy Markdown
Member

Storage providers can now have their connection details edited in place - each provider class declares its own edit fields, non-secret values are prefilled while secrets stay blank and are only written when replaced, and changed credentials are re-verified against the provider before being saved.

Summary by CodeRabbit

  • New Features

    • Added provider-specific storage credential editing with dynamic fields for supported providers.
    • Added authorised visibility of editable provider settings while keeping secrets protected.
    • Added credential merging, validation, and connection checks when settings change.
  • Bug Fixes

    • Connection failures now show a clear, generic validation message without exposing technical details.
    • Preserved valid falsy form values such as false, 0, and empty strings.
  • Documentation

    • Updated API documentation for partial credential updates, secret handling, permissions, and re-verification.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Storage providers now support dynamic edit fields, partial credential updates, connection re-verification, cache invalidation, and authorised non-secret data exposure. The frontend renders provider-specific edit forms and validation errors.

Changes

Storage provider editing

Layer / File(s) Summary
Provider edit contracts and handlers
app/StorageProviders/*, app/Plugins/RegisterStorageProvider.php, tests/Unit/StorageProviders/S3Test.php
Providers define edit fields, classify secrets, merge credentials, validate input, clear cached state, and connect with explicit credentials.
Credential authorisation and response data
app/Policies/*, app/Models/*Provider.php, app/Http/Resources/*ProviderResource.php, app/Tables/StorageProviderTable.php, public/api-docs/openapi/schemas/*
Credential disclosure checks provider handlers, ownership, and token abilities. Responses include authorised non-secret editable data.
Create and update verification
app/Actions/StorageProvider/*, public/api-docs/openapi/*storage-providers.yaml, tests/Feature/*StorageProvidersTest.php
Create and update flows validate credentials, verify changed connections, preserve omitted secrets, log exceptions, and return generic connection errors.
Dynamic frontend edit form
resources/js/components/ui/dynamic-field.tsx, resources/js/pages/storage-providers/*, resources/js/types/*
The edit dialog renders provider-specific fields from bootstrap configuration and initialises them from authorised editable data.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EditDialog
  participant EditStorageProvider
  participant ProviderHandler
  participant StorageProvider
  User->>EditDialog: Submit provider fields
  EditDialog->>EditStorageProvider: Send edited credentials
  EditStorageProvider->>StorageProvider: Validate and merge credentials
  StorageProvider-->>EditStorageProvider: Return credentials and reconnect flag
  EditStorageProvider->>ProviderHandler: Verify connection with credentials
  ProviderHandler-->>EditStorageProvider: Return connection result
  EditStorageProvider-->>EditDialog: Return updated provider or validation error
Loading

Possibly related PRs

  • vitodeploy/vito#1166: Updates related storage-provider API documentation and credential fields.
  • vitodeploy/vito#1174: Updates related Dropbox credential handling, connection, token fetching, and cache invalidation.
  • vitodeploy/vito#1225: Adds related DNS provider editable-data and handler-validation changes.

Suggested reviewers: saeedvaziry

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: enabling in-place editing of storage provider connection details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/StorageProviders/FTP.php (1)

89-100: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Close the FTP connection even when login fails.

If FTP::connect() succeeds but FTP::login() fails, $isConnected is false, and FTP::close($connection) is skipped. The open connection resource is never closed. Since connect() now runs on every edit verification in addition to creation, this leak occurs more often. Close the connection whenever $connection is truthy, not only when $isConnected is true.

🔧 Proposed fix
 public function connect(array $credentials): bool
 {
     $connection = $this->connection($credentials);

     $isConnected = $connection && $this->login($connection, $credentials);

-    if ($isConnected) {
+    if ($connection) {
         \App\Facades\FTP::close($connection);
     }

     return $isConnected;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/StorageProviders/FTP.php` around lines 89 - 100, Update StorageProvider’s
connect method to close the FTP connection whenever the connection resource is
truthy, regardless of whether login succeeds. Keep returning the existing
$isConnected result and preserve the current connection and login flow.
public/api-docs/openapi/user-storage-providers.yaml (1)

171-182: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Mark name as required in the update request schema.

EditStorageProvider::edit() merges the rule ['name' => ['required']] and then reads $input['name'] unconditionally. A request without name returns 422. The deprecated project-scoped endpoint documents required: [name], but this schema does not. Add the required list so the two documents agree with the backend.

📘 Proposed fix
             schema:
               type: object
+              required:
+                - name
               properties:
                 name:
                   type: string

As per path instructions: "Keep OpenAPI schemas in sync with API Resources and backend enums."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/api-docs/openapi/user-storage-providers.yaml` around lines 171 - 182,
Update the request schema containing the name and global properties to declare
name in its required list, matching EditStorageProvider::edit() validation and
the deprecated project-scoped endpoint documentation; leave the existing
property definitions unchanged.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Actions/StorageProvider/EditStorageProvider.php`:
- Around line 44-49: Add a no-op forgetCachedState(): void hook to
StorageProvider and AbstractStorageProvider, override it in Dropbox to call
forgetAccessToken(), and invoke the hook unconditionally in the edit action
before save so changed credentials invalidate the cached token without
provider-specific logic.

In `@app/Http/Resources/DNSProviderResource.php`:
- Line 26: Update DNSProviderResource’s editable_data construction to check
whether the configured provider handler is available before invoking
provider()->editableData(), using the model-level handler check already
established for storage providers. When the handler is unavailable, return an
empty object; otherwise preserve the existing revealCredentials authorization
and editable-data behavior.

In `@app/Models/StorageProvider.php`:
- Around line 56-61: Update hasProviderHandler() to require the configured
handler to implement or extend App\StorageProviders\StorageProvider by combining
the existing string validation with is_a(..., true), rather than relying only on
class_exists(). Preserve the false result for missing or unrelated handler
classes.

In `@app/Policies/DNSProviderPolicy.php`:
- Around line 48-57: Update app/Policies/DNSProviderPolicy.php lines 48-57 and
app/Policies/StorageProviderPolicy.php lines 39-48 to use the HasRolePolicies
trait, and replace inline token or owner checks in update() and
revealCredentials() with the applicable hasWriteAccess() check against the
provider’s project. Ensure both policies consistently use the project-bound role
policy model.

In `@composer.json.bak`:
- Around line 1-131: Delete composer.json.bak and composer.lock.bak from the
repository, and add *.bak to .gitignore to prevent future Composer backup
artefacts; make no changes to the real Composer manifests unless an intentional
lock update is required.

In `@composer.lock.bak`:
- Around line 11122-11132: Regenerate composer.lock and composer.lock.bak using
Composer so the phpstan/phpstan 2.2.2 entry contains the complete package
metadata, including its source object. Do not manually remove or add fields;
ensure both lock files are synchronized and no longer contain the malformed
entry.

---

Outside diff comments:
In `@app/StorageProviders/FTP.php`:
- Around line 89-100: Update StorageProvider’s connect method to close the FTP
connection whenever the connection resource is truthy, regardless of whether
login succeeds. Keep returning the existing $isConnected result and preserve the
current connection and login flow.

In `@public/api-docs/openapi/user-storage-providers.yaml`:
- Around line 171-182: Update the request schema containing the name and global
properties to declare name in its required list, matching
EditStorageProvider::edit() validation and the deprecated project-scoped
endpoint documentation; leave the existing property definitions unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db16db45-37b7-44da-9fc1-5babdf0b1c87

📥 Commits

Reviewing files that changed from the base of the PR and between 7651bb4 and dffc0c3.

📒 Files selected for processing (32)
  • app/Actions/StorageProvider/CreateStorageProvider.php
  • app/Actions/StorageProvider/EditStorageProvider.php
  • app/Http/Resources/DNSProviderResource.php
  • app/Http/Resources/StorageProviderResource.php
  • app/Models/StorageProvider.php
  • app/Plugins/RegisterStorageProvider.php
  • app/Policies/DNSProviderPolicy.php
  • app/Policies/SitePolicy.php
  • app/Policies/StorageProviderPolicy.php
  • app/StorageProviders/AbstractStorageProvider.php
  • app/StorageProviders/Dropbox.php
  • app/StorageProviders/FTP.php
  • app/StorageProviders/Local.php
  • app/StorageProviders/S3.php
  • app/StorageProviders/SFTP.php
  • app/StorageProviders/StorageProvider.php
  • app/Tables/StorageProviderTable.php
  • composer.json.bak
  • composer.lock.bak
  • public/api-docs/openapi/schemas/DNSProvider.yaml
  • public/api-docs/openapi/schemas/StorageProvider.yaml
  • public/api-docs/openapi/storage-providers.yaml
  • public/api-docs/openapi/user-storage-providers.yaml
  • resources/js/components/ui/dynamic-field.tsx
  • resources/js/pages/storage-providers/components/edit-dialog.tsx
  • resources/js/pages/storage-providers/index.tsx
  • resources/js/types/dynamic-field-config.d.ts
  • resources/js/types/index.d.ts
  • resources/js/types/storage-provider.d.ts
  • tests/Feature/API/StorageProvidersTest.php
  • tests/Feature/StorageProvidersTest.php
  • tests/Unit/StorageProviders/S3Test.php

Comment thread app/Actions/StorageProvider/EditStorageProvider.php
Comment thread app/Http/Resources/DNSProviderResource.php Outdated
Comment thread app/Models/StorageProvider.php
Comment thread app/Policies/DNSProviderPolicy.php
Comment thread composer.json.bak Outdated
Comment thread composer.lock.bak Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/StorageProviders/Dropbox.php (1)

67-69: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add Dropbox edit-field definitions.

Dropbox inherits empty edit-field definitions. Therefore, mergeEditData() ignores app_key, app_secret, and refresh_token. The Action does not reconnect or save submitted Dropbox credentials.

Define the dynamic fields and classify app_key as editable. Classify app_secret and refresh_token as secret fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/StorageProviders/Dropbox.php` around lines 67 - 69, Update the Dropbox
provider class to define its dynamic edit fields, marking app_key as editable
and app_secret and refresh_token as secret fields, so mergeEditData() preserves
and processes submitted credentials. Use the existing edit-field definition
structure and classification symbols used by other storage providers.
app/Actions/StorageProvider/EditStorageProvider.php (1)

63-72: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not convert unexpected provider failures into validation errors.

catch (Throwable $e) also catches Error and TypeError. The verify() method then reports provider defects as invalid credentials instead of letting them reach error handling and monitoring. Catch only expected connection exceptions and rethrow unexpected throwables.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Actions/StorageProvider/EditStorageProvider.php` around lines 63 - 72,
Update the exception handling around provider->connect in verify() to catch only
the expected connection exception type, allowing Error, TypeError, and other
unexpected throwables to propagate to error handling and monitoring. Preserve
the existing logging and connected = false behavior for expected connection
failures.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/Actions/StorageProvider/EditStorageProvider.php`:
- Around line 63-72: Update the exception handling around provider->connect in
verify() to catch only the expected connection exception type, allowing Error,
TypeError, and other unexpected throwables to propagate to error handling and
monitoring. Preserve the existing logging and connected = false behavior for
expected connection failures.

In `@app/StorageProviders/Dropbox.php`:
- Around line 67-69: Update the Dropbox provider class to define its dynamic
edit fields, marking app_key as editable and app_secret and refresh_token as
secret fields, so mergeEditData() preserves and processes submitted credentials.
Use the existing edit-field definition structure and classification symbols used
by other storage providers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 857526eb-d6d3-4cca-95a8-e2bdc0942d76

📥 Commits

Reviewing files that changed from the base of the PR and between dffc0c3 and 4ac9593.

📒 Files selected for processing (10)
  • app/Actions/StorageProvider/EditStorageProvider.php
  • app/Http/Resources/DNSProviderResource.php
  • app/Models/DNSProvider.php
  • app/Models/StorageProvider.php
  • app/StorageProviders/AbstractStorageProvider.php
  • app/StorageProviders/Dropbox.php
  • app/StorageProviders/FTP.php
  • app/StorageProviders/StorageProvider.php
  • public/api-docs/openapi/user-storage-providers.yaml
  • tests/Feature/StorageProvidersTest.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/Feature/StorageProvidersTest.php`:
- Around line 559-562: Add an Http::assertSent() check in the test around the
Dropbox connect() flow to verify a POST request to
https://api.dropboxapi.com/2/check/user includes the fresh-access token returned
by the fake OAuth response, placing the assertion before the provider refresh
operation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 855fa8a1-1a03-4c79-b3b5-4d46eed90017

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac9593 and 14f226e.

📒 Files selected for processing (2)
  • app/StorageProviders/Dropbox.php
  • tests/Feature/StorageProvidersTest.php

Comment thread tests/Feature/StorageProvidersTest.php
@RichardAnderson
RichardAnderson merged commit ee983f9 into vitodeploy:4.x Aug 2, 2026
5 checks passed
@RichardAnderson
RichardAnderson deleted the feat/edit-storage-providers branch August 2, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants