fix: Auto-delete API credentials when uninstalling plugin

Automatically remove plugin provider credentials on uninstall to prevent
orphaned keys from being restored when plugin is reinstalled.

Fixes #27531
This commit is contained in:
Kailun Wang 2025-11-28 18:31:26 -05:00
parent ddad2460f3
commit c1b914ee7c
2 changed files with 57 additions and 0 deletions

View File

@ -826,3 +826,29 @@ class PluginReadmeApi(Resource):
return jsonable_encoder(
{"readme": PluginService.fetch_plugin_readme(tenant_id, args.plugin_unique_identifier, args.language)}
)
class ParserUninstall(BaseModel):
plugin_installation_id: str = Field(..., description="Plugin installation ID")
console_ns.schema_model(
ParserUninstall.__name__, ParserUninstall.model_json_schema(ref_template=DEFAULT_REF_TEMPLATE_SWAGGER_2_0)
)
@console_ns.route("/workspaces/current/plugin/uninstall")
class PluginUninstallApi(Resource):
@console_ns.expect(console_ns.models[ParserUninstall.__name__])
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
_, tenant_id = current_account_with_tenant()
args = ParserUninstall.model_validate(console_ns.payload)
try:
return {"success": PluginService.uninstall(tenant_id, args.plugin_installation_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)

View File

@ -505,7 +505,38 @@ class PluginService:
@staticmethod
def uninstall(tenant_id: str, plugin_installation_id: str) -> bool:
from extensions.ext_database import db
from models.provider import ProviderCredential
from sqlalchemy import select
manager = PluginInstaller()
# Get plugin info before uninstalling to delete associated credentials
try:
plugins = manager.list_plugins(tenant_id)
plugin = next((p for p in plugins if p.installation_id == plugin_installation_id), None)
if plugin:
plugin_id = plugin.plugin_id
logger.info(f"Deleting credentials for plugin: {plugin_id}")
# Delete provider credentials that match this plugin
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()
logger.info(f"Deleted {len(credentials)} credentials for plugin: {plugin_id}")
except Exception as e:
logger.warning(f"Failed to delete credentials: {e}")
# Continue with uninstall even if credential deletion fails
return manager.uninstall(tenant_id, plugin_installation_id)
@staticmethod