08bb0d51b2
Add README.md with decision matrix, step-by-step provisioning flows, security and compliance checklist, and rollback plan. Add helper scripts: - oauth_token_exchange.py: obtains and outputs a refresh token - gmail_health_check.py: validates API or IMAP connectivity Co-Authored-By: Paperclip <noreply@paperclip.ing>
116 lines
3.9 KiB
Python
116 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Connectivity health check for Gmail via OAuth (Gmail API) or IMAP (App Password).
|
|
Usage:
|
|
# OAuth mode
|
|
export GMAIL_REFRESH_TOKEN="1//..."
|
|
export GMAIL_CLIENT_SECRET_PATH=/path/to/client_secret.json
|
|
python3 gmail_health_check.py --mode oauth
|
|
|
|
# IMAP mode
|
|
export GMAIL_APP_PASSWORD="xxxx xxxx xxxx xxxx"
|
|
python3 gmail_health_check.py --mode imap --user jonathan@cloudinit.dev
|
|
|
|
Exit codes:
|
|
0 — mailbox reachable
|
|
1 — missing/unreadable credentials or bad args
|
|
2 — network/authentication error
|
|
3 — mailbox reachable but unexpected structure (review needed)
|
|
"""
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
|
|
def check_oauth():
|
|
refresh_token = os.getenv("GMAIL_REFRESH_TOKEN")
|
|
secret_path = os.getenv("GMAIL_CLIENT_SECRET_PATH")
|
|
if not refresh_token or not secret_path:
|
|
print("Error: GMAIL_REFRESH_TOKEN and GMAIL_CLIENT_SECRET_PATH must be set.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import google.auth
|
|
from google.oauth2.credentials import Credentials
|
|
from google.auth.transport.requests import Request
|
|
from googleapiclient.discovery import build
|
|
except ImportError as e:
|
|
print(f"Error: google-api-python-client and google-auth are required ({e}).", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import json
|
|
with open(secret_path, "r") as f:
|
|
client_config = json.load(f)["installed"]
|
|
creds = Credentials(
|
|
None,
|
|
refresh_token=refresh_token,
|
|
token_uri=client_config["token_uri"],
|
|
client_id=client_config["client_id"],
|
|
client_secret=client_config["client_secret"],
|
|
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
|
|
)
|
|
creds.refresh(Request())
|
|
service = build("gmail", "v1", credentials=creds, cache_discovery=False)
|
|
profile = service.users().getProfile(userId="me").execute()
|
|
labels = service.users().labels().list(userId="me").execute()
|
|
except Exception as e:
|
|
print(f"Error: Authentication or API call failed ({e}).", file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
expected_labels = {"INBOX", "SENT"}
|
|
found = {lbl["id"] for lbl in labels.get("labels", [])}
|
|
missing = expected_labels - found
|
|
if missing:
|
|
print(f"Warning: Expected labels missing: {missing}", file=sys.stderr)
|
|
print(f"Profile: {profile}")
|
|
sys.exit(3)
|
|
|
|
print(f"OK — OAuth connectivity verified. Profile: {profile.get('emailAddress')}")
|
|
sys.exit(0)
|
|
|
|
|
|
def check_imap(user: str):
|
|
password = os.getenv("GMAIL_APP_PASSWORD")
|
|
if not password:
|
|
print("Error: GMAIL_APP_PASSWORD must be set for IMAP mode.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import imaplib
|
|
except ImportError:
|
|
print("Error: imaplib is part of the standard library; if this fails your Python build is incomplete.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
mail = imaplib.IMAP4_SSL("imap.gmail.com", 993)
|
|
mail.login(user, password)
|
|
status, _ = mail.select("INBOX")
|
|
if status != "OK":
|
|
print("Warning: INBOX not selectable. Review folder structure.", file=sys.stderr)
|
|
mail.logout()
|
|
sys.exit(3)
|
|
mail.logout()
|
|
except Exception as e:
|
|
print(f"Error: IMAP connection or authentication failed ({e}).", file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
print("OK — IMAP connectivity verified.")
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Gmail connectivity health check")
|
|
parser.add_argument("--mode", required=True, choices=["oauth", "imap"], help="Which path to test")
|
|
parser.add_argument("--user", default="jonathan@cloudinit.dev", help="Gmail address for IMAP mode")
|
|
args = parser.parse_args()
|
|
|
|
if args.mode == "oauth":
|
|
check_oauth()
|
|
elif args.mode == "imap":
|
|
check_imap(args.user)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|