dify icon indicating copy to clipboard operation
dify copied to clipboard

feat: Add option to delete or keep API keys when uninstalling plugin

Open UMDKyle opened this issue 1 month ago • 5 comments

Description

Summary

This PR automatically deletes plugin credentials when uninstalling a plugin, preventing orphaned API keys from persisting in the database.

Updated Implementation (Nov 29, 2025): Following review feedback from @Yeuoly, this PR has been completely refactored to use automatic forced deletion instead of user selection.

"IMO, whether to delete credentials should not be an option to users, I prefer deleting them forcefully." - @Yeuoly


Problem (Issue #27531)

When users deleted a plugin, associated API keys remained in the database:

  • ❌ Credentials silently retained after plugin deletion
  • ❌ No cleanup of unused credentials
  • ❌ API keys automatically restored on plugin reinstall
  • ❌ Users confused about credential persistence

User complaint:

"I deleted the OpenAI plugin, and after reinstalling it, the original API key was automatically restored. I want to clear it."


Solution

Simple automatic deletion:

  • ✅ When plugin is uninstalled, all associated credentials are automatically deleted
  • ✅ No user interaction required
  • ✅ Clean database state after uninstall
  • ✅ Plugin reinstall requires fresh API key configuration

Changes

Backend (api/services/plugin/plugin_service.py)

Modified the uninstall() method to:

  1. Get plugin information before uninstalling
  2. Query all associated ProviderCredential entries (matching plugin_id/% pattern)
  3. Delete all found credentials
  4. Proceed with plugin uninstall

@staticmethod def uninstall(tenant_id: str, plugin_installation_id: str) -> bool: # Get plugin info before uninstalling plugins = manager.list_plugins(tenant_id) plugin = next((p for p in plugins if p.installation_id == plugin_installation_id), None)

if plugin:
    # Delete all credentials matching plugin_id/%
    credentials = db.session.scalars(
        select(ProviderCredential).where(
            ProviderCredential.tenant_id == tenant_id,
            ProviderCredential.provider_name.like(f"{plugin_id}/%"),
        )
    ).all()
    
    for cred in credentials:
        db.session.delete(cred)
    db.session.commit()

return manager.uninstall(tenant_id, plugin_installation_id)**Key Features:**
  • Uses lazy % logging format (Python best practice)
  • Graceful error handling - continues uninstall even if credential deletion fails
  • Logs credential deletion operations for debugging

Code Comparison

Previous approach: +435 −23 lines (user selection UI, multiple components)
Current approach: +57 −0 lines (automatic deletion, backend only)

Simplification:

  • ✅ 87% less code
  • ✅ No frontend changes needed
  • ✅ Simpler to maintain
  • ✅ Follows reviewer recommendation

Testing

Manual Testing:

  1. ✅ Install plugin with API key configuration
  2. ✅ Uninstall plugin → credentials automatically deleted
  3. ✅ Reinstall plugin → requires new API key (old one gone)
  4. ✅ Database queries confirm no orphaned credentials

Tested with Docker deployment:

  • All services running correctly
  • Plugin uninstall flow working as expected
  • No database orphaned records

Database Impact

Affected Tables:

  • provider_credential - Provider-level credentials deleted via pattern match plugin_id/%

Query Pattern: DELETE FROM provider_credential WHERE tenant_id = ? AND provider_name LIKE 'plugin_id/%'---

How This Solves Issue #27531

Before:

  • User deletes OpenAI plugin
  • API key remains in database
  • Plugin reinstall → old API key restored
  • ❌ User frustrated, wants to clear it

After:

  • User deletes OpenAI plugin
  • API key automatically deleted from database
  • Plugin reinstall → requires new API key
  • ✅ Clean state, issue resolved

Related Issue

Fixes #27531


Checklist

  • [x] Code follows project coding standards
  • [x] Self-reviewed the code
  • [x] Added necessary comments
  • [x] Changes generate no new warnings
  • [x] Tested locally with Docker
  • [x] CI checks passing
  • [x] Addressed all review feedback

UMDKyle avatar Nov 13 '25 19:11 UMDKyle

Summary of Changes

Hello @UMDKyle, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the plugin uninstallation experience by providing users with explicit control over the lifecycle of their API keys and credentials. Previously, these sensitive details would remain in the database even after a plugin was removed, leading to potential security concerns and user confusion upon reinstallation. The new implementation introduces a clear, user-friendly mechanism to either securely delete these credentials or intentionally retain them, directly resolving a critical user feedback and improving data hygiene.

Highlights

  • User Control over API Keys: Introduces a user-facing option to either delete or retain associated API keys and credentials when uninstalling a plugin, addressing a long-standing issue where credentials would persist silently.
  • Two-Step Deletion Process: Implements a new workflow where, upon initiating plugin uninstallation, the system first checks for associated credentials and then presents the user with clear choices: delete all credentials (default) or keep them for future reinstallation.
  • New Backend Service for Credential Management: A dedicated PluginCredentialService has been added to handle the retrieval, specific deletion, and bulk deletion of plugin-related credentials from the database.
  • Updated API Endpoints and Frontend UI: New API endpoints facilitate credential checking, and existing uninstall endpoints now accept parameters for credential deletion. The frontend UI has been updated with radio buttons and a list display of credentials to enable user choice.
  • Comprehensive Test Coverage: The feature is backed by 41 new tests, covering both backend logic (26 tests for services and API controllers) and frontend components (15 tests for service functions, state management, and integration flows).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

[^1]: Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot] avatar Nov 13 '25 19:11 gemini-code-assist[bot]

/gemini review

asukaminato0721 avatar Nov 15 '25 18:11 asukaminato0721

Thanks for the PR, please resolve the CI errors :) And you should use existing checkbox component in the project instead.

crazywoola avatar Nov 26 '25 14:11 crazywoola

IMO, whether to delete credentials should not be an option to users, I prefer deleting them forcefully.

How do you think about it? @laipz8200

Yeuoly avatar Nov 26 '25 16:11 Yeuoly

IMO, whether to delete credentials should not be an option to users, I prefer deleting them forcefully.

How do you think about it? @laipz8200

Hi, I updated the pull request and now api keys will be deleted forcefully. I think this is better and sovles the problem with smaller change.

UMDKyle avatar Nov 30 '25 02:11 UMDKyle