Refactor test utilities and enhance ASGI settings

- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -4,19 +4,17 @@ GitHub OAuth Device Flow Authentication for ThrillWiki CI/CD
This script implements GitHub's device flow to securely obtain access tokens.
"""
import os
import sys
import json
import time
import requests
import argparse
from pathlib import Path
from urllib.parse import urlencode
# GitHub OAuth App Configuration
CLIENT_ID = "Iv23liOX5Hp75AxhUvIe"
TOKEN_FILE = ".github-token"
def parse_response(response):
"""Parse HTTP response and handle errors."""
if response.status_code in [200, 201]:
@@ -28,37 +26,40 @@ def parse_response(response):
print(f"HTTP {response.status_code}: {response.text}")
sys.exit(1)
def request_device_code():
"""Request a device code from GitHub."""
url = "https://github.com/login/device/code"
data = {"client_id": CLIENT_ID}
headers = {"Accept": "application/json"}
response = requests.post(url, data=data, headers=headers)
return parse_response(response)
def request_token(device_code):
"""Request an access token using the device code."""
url = "https://github.com/login/oauth/access_token"
data = {
"client_id": CLIENT_ID,
"device_code": device_code,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code"
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
}
headers = {"Accept": "application/json"}
response = requests.post(url, data=data, headers=headers)
return parse_response(response)
def poll_for_token(device_code, interval):
"""Poll GitHub for the access token after user authorization."""
print("Waiting for authorization...")
while True:
response = request_token(device_code)
error = response.get("error")
access_token = response.get("access_token")
if error:
if error == "authorization_pending":
# User hasn't entered the code yet
@@ -78,124 +79,136 @@ def poll_for_token(device_code, interval):
else:
print(f"\nError: {response}")
sys.exit(1)
# Success! Save the token
token_path = Path(TOKEN_FILE)
token_path.write_text(access_token)
token_path.chmod(0o600) # Read/write for owner only
print(f"\nToken saved to {TOKEN_FILE}")
break
def login():
"""Initiate the GitHub OAuth device flow login process."""
print("Starting GitHub authentication...")
device_response = request_device_code()
verification_uri = device_response["verification_uri"]
user_code = device_response["user_code"]
device_code = device_response["device_code"]
interval = device_response["interval"]
print(f"\nPlease visit: {verification_uri}")
print(f"and enter code: {user_code}")
print("\nWaiting for you to complete authorization in your browser...")
poll_for_token(device_code, interval)
print("Successfully authenticated!")
return True
def whoami():
"""Display information about the authenticated user."""
token_path = Path(TOKEN_FILE)
if not token_path.exists():
print("You are not authorized. Run the `login` command.")
sys.exit(1)
try:
token = token_path.read_text().strip()
except Exception as e:
print(f"Error reading token: {e}")
print("You may need to run the `login` command again.")
sys.exit(1)
url = "https://api.github.com/user"
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}"
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers)
user_data = parse_response(response)
print(f"You are authenticated as: {user_data['login']}")
print(f"Name: {user_data.get('name', 'Not set')}")
print(f"Email: {user_data.get('email', 'Not public')}")
return user_data
def get_token():
"""Get the current access token if available."""
token_path = Path(TOKEN_FILE)
if not token_path.exists():
return None
try:
return token_path.read_text().strip()
except Exception:
return None
def validate_token():
"""Validate that the current token is still valid."""
token = get_token()
if not token:
return False
url = "https://api.github.com/user"
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}"
"Authorization": f"Bearer {token}",
}
try:
response = requests.get(url, headers=headers)
return response.status_code == 200
except Exception:
return False
def ensure_authenticated():
"""Ensure user is authenticated, prompting login if necessary."""
if validate_token():
return get_token()
print("GitHub authentication required.")
login()
return get_token()
def logout():
"""Remove the stored access token."""
token_path = Path(TOKEN_FILE)
if token_path.exists():
token_path.unlink()
print("Successfully logged out.")
else:
print("You are not currently logged in.")
def main():
"""Main CLI interface."""
parser = argparse.ArgumentParser(description="GitHub OAuth authentication for ThrillWiki CI/CD")
parser.add_argument("command", choices=["login", "logout", "whoami", "token", "validate"],
help="Command to execute")
parser = argparse.ArgumentParser(
description="GitHub OAuth authentication for ThrillWiki CI/CD"
)
parser.add_argument(
"command",
choices=["login", "logout", "whoami", "token", "validate"],
help="Command to execute",
)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
if args.command == "login":
login()
elif args.command == "logout":
@@ -216,5 +229,6 @@ def main():
print("Token is invalid or missing.")
sys.exit(1)
if __name__ == "__main__":
main()
main()