updates for keyring.alt and fallbacks on RUnPod or environments where

keyring is not available
This commit is contained in:
adamhaley
2025-08-18 17:07:06 -07:00
parent 005b8bd051
commit 53f8ded658
2 changed files with 131 additions and 19 deletions
+109 -15
View File
@@ -6,16 +6,109 @@ from keyring.errors import KeyringError
DROPBOX_KEYRING_SERVICE = "comfyui_dropbox"
def setup_keyring_backend():
"""Setup the best available keyring backend for the environment"""
try:
# Try to use the default backend first
keyring.get_keyring()
test_service = "test_keyring_availability"
keyring.set_password(test_service, "test", "test")
keyring.delete_password(test_service, "test")
print("[DropboxAuthManager] Using system keyring backend")
return True
except Exception as e:
print(f"[DropboxAuthManager] System keyring not available: {e}")
# Try keyrings.alt file-based backends
try:
import keyrings.alt.file
import keyrings.alt.cryptfile
# Try encrypted file keyring first (more secure)
try:
crypto_keyring = keyrings.alt.cryptfile.CryptFileKeyring()
# Set a simple password for the keyring file
crypto_keyring.keyring_key = "comfyui_dropbox_keyring_2024"
# Set custom file location in the plugin directory
import os
plugin_dir = os.path.dirname(os.path.abspath(__file__))
crypto_keyring.filename = os.path.join(plugin_dir, ".dropbox_keyring.cfg")
keyring.set_keyring(crypto_keyring)
# Test it works
test_service = "test_crypto_keyring"
keyring.set_password(test_service, "test", "test")
keyring.delete_password(test_service, "test")
print(f"[DropboxAuthManager] Using encrypted file keyring: {crypto_keyring.filename}")
return True
except Exception as crypto_error:
print(f"[DropboxAuthManager] Encrypted file keyring failed: {crypto_error}")
# Fall back to plaintext file keyring
try:
file_keyring = keyrings.alt.file.PlaintextKeyring()
# Set custom file location in the plugin directory
plugin_dir = os.path.dirname(os.path.abspath(__file__))
file_keyring.filename = os.path.join(plugin_dir, ".dropbox_keyring_plaintext.cfg")
keyring.set_keyring(file_keyring)
# Test it works
test_service = "test_file_keyring"
keyring.set_password(test_service, "test", "test")
keyring.delete_password(test_service, "test")
print(f"[DropboxAuthManager] Using plaintext file keyring: {file_keyring.filename}")
print(f"[DropboxAuthManager] WARNING: Credentials will be stored in plaintext")
return True
except Exception as file_error:
print(f"[DropboxAuthManager] File keyring also failed: {file_error}")
return False
except ImportError:
print("[DropboxAuthManager] keyrings.alt not available - install with: pip install keyrings.alt")
return False
except Exception as e:
print(f"[DropboxAuthManager] All keyring backends failed: {e}")
return False
class DropboxAuthManager:
def __init__(self, app_key=None, app_secret=None):
self.app_key = app_key if app_key is not None else keyring.get_password(DROPBOX_KEYRING_SERVICE, "app_key")
self.app_secret = app_secret if app_secret is not None else keyring.get_password(DROPBOX_KEYRING_SERVICE, "app_secret")
self.refresh_token = keyring.get_password(DROPBOX_KEYRING_SERVICE, "refresh_token")
# Setup the best available keyring backend first
self.keyring_available = setup_keyring_backend()
# Try to get from keyring, but handle backend unavailability gracefully
if self.keyring_available:
try:
self.app_key = app_key if app_key is not None else keyring.get_password(DROPBOX_KEYRING_SERVICE, "app_key")
self.app_secret = app_secret if app_secret is not None else keyring.get_password(DROPBOX_KEYRING_SERVICE, "app_secret")
self.refresh_token = keyring.get_password(DROPBOX_KEYRING_SERVICE, "refresh_token")
except Exception as e:
print(f"[DropboxAuthManager] Error reading from keyring: {e}")
self.app_key = app_key
self.app_secret = app_secret
self.refresh_token = None
self.keyring_available = False
else:
print(f"[DropboxAuthManager] No keyring backend available - using provided credentials only")
self.app_key = app_key
self.app_secret = app_secret
self.refresh_token = None
def is_connected(self):
return bool(self.app_key and self.app_secret and self.refresh_token)
def store_tokens(self, app_key, app_secret, refresh_token):
if not self.keyring_available:
raise RuntimeError("Keyring not available. Please use 'env_file' or 'display_only' storage method instead.")
try:
keyring.set_password(DROPBOX_KEYRING_SERVICE, "app_key", app_key)
keyring.set_password(DROPBOX_KEYRING_SERVICE, "app_secret", app_secret)
@@ -126,18 +219,19 @@ class DropboxAuthManager:
def reset(self):
"""Clear stored tokens"""
try:
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "app_key")
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "app_secret")
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "refresh_token")
# Clear instance variables
self.app_key = None
self.app_secret = None
self.refresh_token = None
except KeyringError:
# If passwords don't exist, that's fine
pass
if self.keyring_available:
try:
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "app_key")
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "app_secret")
keyring.delete_password(DROPBOX_KEYRING_SERVICE, "refresh_token")
except KeyringError:
# If passwords don't exist, that's fine
pass
# Clear instance variables regardless of keyring availability
self.app_key = None
self.app_secret = None
self.refresh_token = None
def get_oauth_url(self, redirect_uri=None, state=None):
"""Generate OAuth URL for initial authorization"""
+22 -4
View File
@@ -18,6 +18,10 @@ class DropboxSetupNode:
auth_manager = DropboxAuthManager()
is_connected = auth_manager.is_connected()
# If keyring is not available, we know keyring storage won't work
if not auth_manager.keyring_available:
print(f"[DropboxSetup] Keyring not available in this environment")
# Also check for .env file credentials
if not is_connected:
node_dir = os.path.dirname(__file__)
@@ -80,9 +84,17 @@ class DropboxSetupNode:
"label": "Automatic OAuth (no manual auth code needed)",
"default": True
})
inputs["optional"]["storage_method"] = (["keyring", "env_file", "display_only"], {
# Smart default based on keyring availability
if auth_manager.keyring_available:
default_storage = "keyring"
storage_options = ["keyring", "env_file", "display_only"]
else:
default_storage = "display_only" # Better for RunPod/Docker
storage_options = ["display_only", "env_file", "keyring"]
inputs["optional"]["storage_method"] = (storage_options, {
"label": "Credential Storage Method",
"default": "keyring"
"default": default_storage
})
return inputs
@@ -284,8 +296,14 @@ class DropboxSetupNode:
# Handle different storage methods
if storage_method == "keyring":
# Store in system keyring (original behavior)
auth_manager_setup.store_tokens(app_key_clean, app_secret_clean, refresh_token)
message = "✅ Dropbox connected successfully! Credentials stored securely in system keyring."
try:
auth_manager_setup.store_tokens(app_key_clean, app_secret_clean, refresh_token)
message = "✅ Dropbox connected successfully! Credentials stored securely in system keyring."
except RuntimeError as e:
if "Keyring not available" in str(e):
message = f"❌ All keyring backends failed in this environment.\n\n💡 Please use 'env_file' or 'display_only' storage method instead.\n\n🔧 To enable file-based keyring, ensure 'keyrings.alt' is installed.\n\nTechnical details: {e}"
else:
raise e
elif storage_method == "env_file":
# Store in .env file