From b4eda7aca3a49cf2ed957383179f7f240e560137 Mon Sep 17 00:00:00 2001 From: Gouri Phadnis Date: Fri, 10 Oct 2025 14:34:13 +0530 Subject: [PATCH] Add token refresher daemon --- refresh_token_daemon.py | 172 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 refresh_token_daemon.py diff --git a/refresh_token_daemon.py b/refresh_token_daemon.py new file mode 100644 index 0000000000..990a9e6613 --- /dev/null +++ b/refresh_token_daemon.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +""" +refresh_token_daemon.py + +Periodically fetch an auth token and write to a file (used by sqlmap --eval). +Configure via env vars or edit the defaults below. +""" + +import os +import sys +import time +import json +import tempfile +import traceback +from datetime import datetime +from typing import Optional + +import requests + +# === CONFIG (edit here or set env vars) === +AUTH_URL = os.getenv("AUTH_URL", "https://target.example.com/auth") +AUTH_BODY_JSON = os.getenv("AUTH_BODY_JSON") # example: '{"username":"me","password":"pwd"}' +if AUTH_BODY_JSON is None: + AUTH_BODY = {"username": "youruser", "password": "yourpass"} +else: + try: + AUTH_BODY = json.loads(AUTH_BODY_JSON) + except Exception: + print("ERROR: AUTH_BODY_JSON not valid JSON", file=sys.stderr) + AUTH_BODY = {} + +DEFAULT_OUT_UNIX = "/tmp/current_token.txt" +DEFAULT_OUT_WIN = r"C:\temp\current_token.txt" +TOKEN_OUT = os.getenv("TOKEN_OUT", DEFAULT_OUT_UNIX if os.name != "nt" else DEFAULT_OUT_WIN) + +TOKEN_JSON_KEY = os.getenv("TOKEN_JSON_KEY", "access_token") # set to "" to use raw body +REFRESH_TTL = int(os.getenv("REFRESH_TTL", "50")) # seconds token considered fresh +REFRESH_FREQ = int(os.getenv("REFRESH_FREQ", "10")) # daemon wake frequency (s) + +REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "15")) +MAX_RETRIES = int(os.getenv("MAX_RETRIES", "3")) +RETRY_BACKOFF = float(os.getenv("RETRY_BACKOFF", "2.0")) + +def log(msg: str, err: bool = False): + ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + out = sys.stderr if err else sys.stdout + print(f"[{ts}] {msg}", file=out) + out.flush() + +# Cross-platform file lock +if os.name == "nt": + import msvcrt + class FileLock: + def __init__(self, path): self.fp = open(path, "a+b") + def acquire(self): + try: + msvcrt.locking(self.fp.fileno(), msvcrt.LK_NBLCK, 1); return True + except OSError: return False + def release(self): + try: self.fp.seek(0); msvcrt.locking(self.fp.fileno(), msvcrt.LK_UNLCK, 1) + except Exception: pass + def close(self): + try: self.fp.close() + except Exception: pass +else: + import fcntl + class FileLock: + def __init__(self, path): self.fp = open(path, "a+") + def acquire(self): + try: + fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB); return True + except IOError: return False + def release(self): + try: fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN) + except Exception: pass + def close(self): + try: self.fp.close() + except Exception: pass + +def ensure_dir_for_file(path: str): + d = os.path.dirname(path) + if d and not os.path.exists(d): + os.makedirs(d, exist_ok=True) + +def atomic_write(path: str, data: str): + ensure_dir_for_file(path) + dirpath = os.path.dirname(path) or "." + fd, tmp_path = tempfile.mkstemp(dir=dirpath) + try: + with os.fdopen(fd, "w") as tmpf: + tmpf.write(data); tmpf.flush(); os.fsync(tmpf.fileno()) + os.replace(tmp_path, path) + except Exception: + log("ERROR during atomic write: " + traceback.format_exc(), err=True) + try: + if os.path.exists(tmp_path): os.remove(tmp_path) + except Exception: pass + +def read_token_file_if_recent(path: str, ttl: int) -> Optional[str]: + try: + if os.path.exists(path): + mtime = os.path.getmtime(path) + age = time.time() - mtime + if age <= ttl: + with open(path, "r") as f: return f.read().strip() + except Exception: pass + return None + +def fetch_token_from_api() -> Optional[str]: + attempt = 0; delay = 1.0 + while attempt < MAX_RETRIES: + attempt += 1 + try: + log(f"Attempting auth request (attempt {attempt}) to {AUTH_URL}") + resp = requests.post(AUTH_URL, json=AUTH_BODY, timeout=REQUEST_TIMEOUT) + resp.raise_for_status() + if TOKEN_JSON_KEY: + try: + obj = resp.json(); token = obj.get(TOKEN_JSON_KEY) + if token: return token.strip() + else: log("Token JSON key not found; falling back to raw body", err=True) + except ValueError: + log("Response not JSON; falling back to raw body", err=True) + text = resp.text.strip() + return text if text else None + except Exception as e: + log(f"Auth request failed: {e}", err=True) + if attempt < MAX_RETRIES: + time.sleep(delay); delay *= RETRY_BACKOFF + return None + +def main(): + lockfile_path = TOKEN_OUT + ".lock" + lock = FileLock(lockfile_path) + log("Starting refresh_token_daemon") + log(f"AUTH_URL: {AUTH_URL}") + log(f"TOKEN_OUT: {TOKEN_OUT}") + log(f"TOKEN_JSON_KEY: {TOKEN_JSON_KEY!r}") + log(f"REFRESH_TTL: {REFRESH_TTL}s REFRESH_FREQ: {REFRESH_FREQ}s") + ensure_dir_for_file(TOKEN_OUT) + try: + while True: + existing = read_token_file_if_recent(TOKEN_OUT, REFRESH_TTL) + if existing: + log(f"Token is fresh (<= {REFRESH_TTL}s). Sleeping {REFRESH_FREQ}s.") + time.sleep(REFRESH_FREQ); continue + got_lock = lock.acquire() + if not got_lock: + log("Another process refreshing token; waiting."); time.sleep(REFRESH_FREQ); continue + try: + existing = read_token_file_if_recent(TOKEN_OUT, REFRESH_TTL) + if existing: + log("Token written by another process while waiting for lock; skipping fetch."); continue + token = fetch_token_from_api() + if token: + atomic_write(TOKEN_OUT, token) + log("Token refreshed and written successfully.") + else: + log("Failed to fetch token after retries; will retry later.", err=True) + finally: + lock.release() + time.sleep(REFRESH_FREQ) + except KeyboardInterrupt: + log("Interrupted by user; exiting.") + except Exception: + log("Unexpected error: " + traceback.format_exc(), err=True) + finally: + try: lock.close() + except Exception: pass + +if __name__ == "__main__": + main()