feat: implement graceful fallback to config.json for OTP secret storage when the keyring module is unavailable.
This commit is contained in:
parent
d52743d69c
commit
252a7ed239
1 changed files with 59 additions and 36 deletions
35
wrapper.py
35
wrapper.py
|
|
@ -13,7 +13,11 @@ import hashlib
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import configparser
|
import configparser
|
||||||
|
try:
|
||||||
import keyring
|
import keyring
|
||||||
|
HAS_KEYRING = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_KEYRING = False
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
@ -172,14 +176,15 @@ def detect_launch_command():
|
||||||
def get_secret(config, config_path):
|
def get_secret(config, config_path):
|
||||||
"""
|
"""
|
||||||
Retrieves the OTP secret in the following priority:
|
Retrieves the OTP secret in the following priority:
|
||||||
1. System Keyring (SERVICE_NAME, USERNAME)
|
1. System Keyring (SERVICE_NAME, USERNAME) - if available
|
||||||
2. config.json (Legacy) - if found, migrates to Keyring and removes from config.
|
2. config.json - if Keyring unavailable or secret found there (and Keyring unavailable for migration)
|
||||||
3. User Input - prompts user and saves to Keyring.
|
3. User Input - prompts user and saves to Keyring (if available) or config.json.
|
||||||
"""
|
"""
|
||||||
SERVICE_NAME = "XIVLauncherWrapper"
|
SERVICE_NAME = "XIVLauncherWrapper"
|
||||||
USERNAME = "OTP_SECRET"
|
USERNAME = "OTP_SECRET"
|
||||||
|
|
||||||
# 1. Try Keyring
|
# 1. Try Keyring
|
||||||
|
if HAS_KEYRING:
|
||||||
try:
|
try:
|
||||||
secret = keyring.get_password(SERVICE_NAME, USERNAME)
|
secret = keyring.get_password(SERVICE_NAME, USERNAME)
|
||||||
if secret:
|
if secret:
|
||||||
|
|
@ -187,10 +192,11 @@ def get_secret(config, config_path):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Failed to access keyring: {e}")
|
print(f"Warning: Failed to access keyring: {e}")
|
||||||
|
|
||||||
# 2. Try Config (Legacy/Migration)
|
# 2. Try Config
|
||||||
if config and "secret" in config:
|
if config and "secret" in config:
|
||||||
secret = config["secret"]
|
secret = config["secret"]
|
||||||
if secret and secret != "YOUR_BASE32_SECRET_HERE":
|
if secret and secret != "YOUR_BASE32_SECRET_HERE":
|
||||||
|
if HAS_KEYRING:
|
||||||
print("Migrating secret from config.json to system keyring...")
|
print("Migrating secret from config.json to system keyring...")
|
||||||
try:
|
try:
|
||||||
keyring.set_password(SERVICE_NAME, USERNAME, secret)
|
keyring.set_password(SERVICE_NAME, USERNAME, secret)
|
||||||
|
|
@ -205,9 +211,16 @@ def get_secret(config, config_path):
|
||||||
print(f"Error migrating to keyring: {e}")
|
print(f"Error migrating to keyring: {e}")
|
||||||
print("Continuing with secret from config...")
|
print("Continuing with secret from config...")
|
||||||
return secret
|
return secret
|
||||||
|
else:
|
||||||
|
# No keyring, just use config
|
||||||
|
return secret
|
||||||
|
|
||||||
# 3. Prompt User
|
# 3. Prompt User
|
||||||
|
if HAS_KEYRING:
|
||||||
print("\nOTP Secret not found in keyring.")
|
print("\nOTP Secret not found in keyring.")
|
||||||
|
else:
|
||||||
|
print("\nOTP Secret not found in config.json and keyring module is missing.")
|
||||||
|
|
||||||
print("Please enter your TOTP Secret (base32) or otpauth:// URL.")
|
print("Please enter your TOTP Secret (base32) or otpauth:// URL.")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|
@ -222,16 +235,26 @@ def get_secret(config, config_path):
|
||||||
print("Invalid secret format. Please try again.")
|
print("Invalid secret format. Please try again.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if HAS_KEYRING:
|
||||||
try:
|
try:
|
||||||
keyring.set_password(SERVICE_NAME, USERNAME, parsed)
|
keyring.set_password(SERVICE_NAME, USERNAME, parsed)
|
||||||
print("Secret saved to system keyring.")
|
print("Secret saved to system keyring.")
|
||||||
return parsed
|
return parsed
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error saving to keyring: {e}")
|
print(f"Error saving to keyring: {e}")
|
||||||
# If we can't save to keyring, we might just return it in memory for this session
|
|
||||||
# But typically we want persistence.
|
|
||||||
print("Proceeding with in-memory secret for this session.")
|
print("Proceeding with in-memory secret for this session.")
|
||||||
return parsed
|
return parsed
|
||||||
|
else:
|
||||||
|
# Save to config.json
|
||||||
|
try:
|
||||||
|
config['secret'] = parsed
|
||||||
|
with open(config_path, 'w') as f:
|
||||||
|
json.dump(config, f, indent=4)
|
||||||
|
print(f"Secret saved to {config_path}")
|
||||||
|
return parsed
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving to config.json: {e}")
|
||||||
|
return parsed
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nOperation cancelled.")
|
print("\nOperation cancelled.")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue