mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
231 lines
7.9 KiB
Python
231 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
restore_gdrive.py — Restore Google Drive access and re-ingest into ENE.
|
|
|
|
Steps:
|
|
1. Test current rclone Gdrive: remote
|
|
2. If token is dead, print re-auth instructions
|
|
3. If token works, extract credentials and re-ingest into ENE substrate
|
|
4. Verify ENE can decrypt the new credentials
|
|
|
|
Usage:
|
|
# If gdrive token is dead, re-auth first:
|
|
rclone config reconnect Gdrive:
|
|
|
|
# Then run:
|
|
python3 5-Applications/scripts/restore_gdrive.py
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# Load .env
|
|
project_root = Path(__file__).parent.parent.parent
|
|
try:
|
|
from dotenv import load_dotenv
|
|
if (project_root / ".env").exists():
|
|
load_dotenv(project_root / ".env")
|
|
except ImportError:
|
|
pass
|
|
|
|
sys.path.insert(0, str(project_root))
|
|
sys.path.insert(0, str(project_root / "4-Infrastructure" / "infra"))
|
|
|
|
from infra.ene_api import ENEAPIHook, AccessLevel
|
|
|
|
RCLONE_CONF = Path.home() / ".config" / "rclone" / "rclone.conf"
|
|
|
|
|
|
def test_rclone_gdrive() -> dict:
|
|
"""Test if rclone can list Gdrive root."""
|
|
result = subprocess.run(
|
|
["rclone", "ls", "Gdrive:", "--max-depth", "1"],
|
|
capture_output=True, text=True, timeout=60
|
|
)
|
|
return {
|
|
"ok": result.returncode == 0,
|
|
"stdout": result.stdout.strip(),
|
|
"stderr": result.stderr.strip(),
|
|
"returncode": result.returncode
|
|
}
|
|
|
|
|
|
def parse_rclone_token() -> dict:
|
|
"""Extract token JSON from rclone config."""
|
|
if not RCLONE_CONF.exists():
|
|
return {"error": f"rclone.conf not found at {RCLONE_CONF}"}
|
|
|
|
in_gdrive = False
|
|
token_line = None
|
|
with open(RCLONE_CONF) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line.startswith("[Gdrive]"):
|
|
in_gdrive = True
|
|
elif line.startswith("[") and line.endswith("]"):
|
|
in_gdrive = False
|
|
elif in_gdrive and line.startswith("token = "):
|
|
token_line = line[len("token = "):]
|
|
break
|
|
|
|
if not token_line:
|
|
return {"error": "No token found in [Gdrive] section"}
|
|
|
|
try:
|
|
token = json.loads(token_line)
|
|
return {"ok": True, "token": token}
|
|
except json.JSONDecodeError as e:
|
|
return {"error": f"Invalid token JSON: {e}"}
|
|
|
|
|
|
def update_rclone_config(new_token: dict) -> bool:
|
|
"""Update rclone.conf with new token JSON."""
|
|
if not RCLONE_CONF.exists():
|
|
return False
|
|
|
|
lines = []
|
|
in_gdrive = False
|
|
replaced = False
|
|
with open(RCLONE_CONF) as f:
|
|
for line in f:
|
|
stripped = line.strip()
|
|
if stripped.startswith("[Gdrive]"):
|
|
in_gdrive = True
|
|
elif stripped.startswith("[") and stripped.endswith("]"):
|
|
in_gdrive = False
|
|
|
|
if in_gdrive and stripped.startswith("token = ") and not replaced:
|
|
lines.append(f"token = {json.dumps(new_token)}\n")
|
|
replaced = True
|
|
continue
|
|
lines.append(line)
|
|
|
|
with open(RCLONE_CONF, "w") as f:
|
|
f.writelines(lines)
|
|
|
|
return replaced
|
|
|
|
|
|
def ingest_gdrive_to_ene(token: dict, client_id: str, client_secret: str) -> dict:
|
|
"""Store gdrive credentials in ENE substrate."""
|
|
api = ENEAPIHook()
|
|
|
|
# Store the refresh token as the primary credential
|
|
payload = json.dumps({
|
|
"provider": "gdrive",
|
|
"remote_name": "Gdrive",
|
|
"client_id": client_id,
|
|
"client_secret": client_secret,
|
|
"refresh_token": token.get("refresh_token", ""),
|
|
"token_type": token.get("token_type", "Bearer"),
|
|
"updated_at": datetime.utcnow().isoformat(),
|
|
})
|
|
|
|
result = api.store_sensitive_data(
|
|
pkg="credentials/gdrive",
|
|
payload=payload,
|
|
classification=AccessLevel.SECRET
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
def main():
|
|
print("=" * 70)
|
|
print("GDRIVE RESTORE")
|
|
print("=" * 70)
|
|
|
|
# 1. Test rclone
|
|
print("\n[1] Testing rclone Gdrive: access...")
|
|
test = test_rclone_gdrive()
|
|
if test["ok"]:
|
|
print(" ✅ Gdrive is accessible")
|
|
print(f" Listing: {test['stdout'][:200]}...")
|
|
else:
|
|
print(" ❌ Gdrive access failed")
|
|
print(f" Error: {test['stderr'][:200]}")
|
|
|
|
if "invalid_grant" in test["stderr"] or "token expired" in test["stderr"]:
|
|
print("\n The refresh token is revoked or expired.")
|
|
print(" You need to re-authenticate:")
|
|
print("\n ┌─────────────────────────────────────────────────────────────┐")
|
|
print(" │ Run this command in your terminal: │")
|
|
print(" │ │")
|
|
print(" │ rclone config reconnect Gdrive: │")
|
|
print(" │ │")
|
|
print(" │ Then follow the browser prompt to authorize. │")
|
|
print(" │ After that, run this script again. │")
|
|
print(" └─────────────────────────────────────────────────────────────┘")
|
|
sys.exit(1)
|
|
|
|
# 2. Parse token
|
|
print("\n[2] Reading rclone token...")
|
|
token_info = parse_rclone_token()
|
|
if "error" in token_info:
|
|
print(f" ❌ {token_info['error']}")
|
|
sys.exit(1)
|
|
|
|
token = token_info["token"]
|
|
refresh_token = token.get("refresh_token", "")
|
|
if not refresh_token:
|
|
print(" ❌ No refresh_token in rclone config. Re-authenticate with:")
|
|
print(" rclone config reconnect Gdrive:")
|
|
sys.exit(1)
|
|
|
|
print(f" ✅ Refresh token present ({len(refresh_token)} chars)")
|
|
|
|
# 3. Read client_id/client_secret from rclone config
|
|
client_id = None
|
|
client_secret = None
|
|
with open(RCLONE_CONF) as f:
|
|
in_gdrive = False
|
|
for line in f:
|
|
line = line.strip()
|
|
if line.startswith("[Gdrive]"):
|
|
in_gdrive = True
|
|
elif line.startswith("[") and line.endswith("]"):
|
|
in_gdrive = False
|
|
elif in_gdrive and line.startswith("client_id = "):
|
|
client_id = line[len("client_id = "):]
|
|
elif in_gdrive and line.startswith("client_secret = "):
|
|
client_secret = line[len("client_secret = "):]
|
|
|
|
# 4. Ingest into ENE
|
|
print("\n[3] Ingesting gdrive credentials into ENE substrate...")
|
|
ene_result = ingest_gdrive_to_ene(token, client_id, client_secret)
|
|
if ene_result.get("success"):
|
|
print(f" ✅ Stored in ENE. ID: {ene_result['id']}")
|
|
else:
|
|
print(f" ❌ ENE storage failed: {ene_result.get('error')}")
|
|
sys.exit(1)
|
|
|
|
# 5. Verify decryption
|
|
print("\n[4] Verifying ENE decryption...")
|
|
api = ENEAPIHook()
|
|
verify = api.retrieve_sensitive_data("credentials/gdrive", AccessLevel.SECRET)
|
|
if verify.get("success"):
|
|
print(" ✅ Credentials decryptable")
|
|
parsed = json.loads(verify["payload"])
|
|
print(f" Provider: {parsed.get('provider')}")
|
|
print(f" Remote: {parsed.get('remote_name')}")
|
|
print(f" Refresh token length: {len(parsed.get('refresh_token', ''))}")
|
|
else:
|
|
print(f" ❌ Decryption failed: {verify.get('error')}")
|
|
sys.exit(1)
|
|
|
|
print("\n" + "=" * 70)
|
|
print("GDRIVE RESTORED")
|
|
print("=" * 70)
|
|
print("\nNext steps:")
|
|
print(" - ENE substrate now holds the gdrive credential")
|
|
print(" - rclone Gdrive: remote is working")
|
|
print(" - Run 'rclone mount Gdrive: ~/Gdrive' to mount if desired")
|
|
print(" - Run bridge/dump scripts to push gdrive metadata into Neo4j")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|