Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 235 additions & 0 deletions FIXES_NEEDED.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# python-libs cloudflare-auth Package Fixes Needed

**Status**: Critical - Package cannot be used as a dependency
**Discovered**: 2025-12-06 during homelab-infra migration
**Reporter**: Claude Code (via ByronWilliams)

---

## Summary

The `byronwilliamscpa-cloudflare-auth` package has broken imports that prevent it from being used
as a dependency in other projects. This violates the core purpose of python-libs as a shared
library repository.

## Issues Found

### 1. ✅ FIXED LOCALLY: Incorrect Import Statements (21+ occurrences)

**Problem**: Module imports use `src.cloudflare_auth` instead of `cloudflare_auth`

**Status**: Fixed locally in this clone (not committed)

**Files Affected**:
- `packages/cloudflare-auth/src/cloudflare_auth/__init__.py`
- `packages/cloudflare-auth/src/cloudflare_auth/middleware.py`
- `packages/cloudflare-auth/src/cloudflare_auth/middleware_enhanced.py`
- `packages/cloudflare-auth/src/cloudflare_auth/validators.py`
- `packages/cloudflare-auth/src/cloudflare_auth/redis_sessions.py`
- And potentially others

**Example**:

```python
# Incorrect (current in repo)
from src.cloudflare_auth.models import CloudflareUser
from src.cloudflare_auth.middleware import CloudflareAuthMiddleware

# Correct (fixed locally)
from cloudflare_auth.models import CloudflareUser
from cloudflare_auth.middleware import CloudflareAuthMiddleware
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

**Fix Applied**:

```bash
cd packages/cloudflare-auth
find src -name "*.py" -exec sed -i 's/from src\.cloudflare_auth/from cloudflare_auth/g' {} \;
find src -name "*.py" -exec sed -i 's/import src\.cloudflare_auth/import cloudflare_auth/g' {} \;
```

---

### 2. ❌ NOT FIXED: Missing CloudflareSettings Module

**Problem**: Multiple files import from non-existent `src.config.settings` module

**Files Affected**:
- `packages/cloudflare-auth/src/cloudflare_auth/validators.py:40`
- `packages/cloudflare-auth/src/cloudflare_auth/middleware.py`
- `packages/cloudflare-auth/src/cloudflare_auth/middleware_enhanced.py`

**Import Statement**:

```python
from src.config.settings import CloudflareSettings, get_cloudflare_settings
```

**Problem**: This module doesn't exist in the cloudflare-auth package

**Options for Resolution**:

#### Option A: Create CloudflareSettings within package (RECOMMENDED)

```python
# packages/cloudflare-auth/src/cloudflare_auth/settings.py
from pydantic_settings import BaseSettings

class CloudflareSettings(BaseSettings):
"""Cloudflare Access configuration settings."""

cloudflare_team_domain: str
cloudflare_audience_tag: str
cloudflare_enabled: bool = True
service_token_enabled: bool = False
# ... other settings

class Config:
env_file = ".env"

def get_cloudflare_settings() -> CloudflareSettings:
"""Get singleton CloudflareSettings instance."""
return CloudflareSettings()
```

Then update imports:

```python
# In validators.py, middleware.py, middleware_enhanced.py
from cloudflare_auth.settings import CloudflareSettings, get_cloudflare_settings
```

#### Option B: Make settings injectable (alternative)

Remove direct settings import and require settings to be passed as parameters:

```python
# In validators.py
class CloudflareJWTValidator:
def __init__(self, team_domain: str, audience_tag: str):
self.team_domain = team_domain
self.audience_tag = audience_tag
```

**Recommendation**: Option A is preferred for backward compatibility and ease of use.

---

## Root Cause

The package appears to have been copied from a different project structure where it was nested under `src/` with a separate `src/config/` module. When moved to the python-libs monorepo structure, imports were not updated to reflect the new package structure.

---

## Impact

**Current State**:
- ❌ Package cannot be imported: `ModuleNotFoundError: No module named 'src.config'`
- ❌ Cannot be used as a dependency in other projects
- ❌ Forces code duplication instead of reuse
- ❌ Violates the stated purpose of python-libs repository

**Blocked Projects**:
- homelab-infra (PR #54) - waiting for these fixes before tests can run

---

## Action Items

### Immediate (Required for PR #54)

1. **Commit local import fixes**:

```bash
cd packages/cloudflare-auth
git add src/cloudflare_auth/*.py
git commit -m "fix: correct all module imports from src.cloudflare_auth to cloudflare_auth"
```

2. **Create CloudflareSettings module**:
- Create `src/cloudflare_auth/settings.py`
- Define `CloudflareSettings` and `get_cloudflare_settings`
- Update imports in affected files
- Test imports work correctly

3. **Add import tests**:

```python
# tests/test_imports.py
def test_public_imports():
"""Verify all public imports work correctly."""
from cloudflare_auth import (
CloudflareAuthMiddleware,
CloudflareUser,
setup_cloudflare_auth_enhanced,
)
assert CloudflareAuthMiddleware is not None
```

4. **Add CI check**:

```yaml
# .github/workflows/ci.yml
- name: Test package imports
run: |
uv run python -c "from cloudflare_auth import CloudflareUser"
```

### Follow-up (Before PyPI Publication)

5. **Publish to PyPI**:
- Once fixes are complete and tests pass
- Follow semantic versioning (v0.1.1 for patch)
- Update homelab-infra to use PyPI package instead of path dependency

6. **Update template**:
- Add import tests to cookiecutter-python-template
- Ensure packages in workspace members have validated imports

---
Comment on lines +139 to +189
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the repository structure
git ls-files | head -20

Repository: ByronWilliamsCPA/python-libs

Length of output: 766


🏁 Script executed:

# Verify the cloudflare-auth package directory structure
if [ -d "packages/cloudflare-auth" ]; then
  echo "✓ cloudflare-auth package exists"
  find packages/cloudflare-auth -type f -name "*.py" | head -20
else
  echo "✗ cloudflare-auth package not found"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 1204


🏁 Script executed:

# Check for settings.py and examine FIXES_NEEDED.md
if [ -f "FIXES_NEEDED.md" ]; then
  echo "✓ FIXES_NEEDED.md exists"
  echo "Checking lines 139-189:"
  sed -n '139,189p' FIXES_NEEDED.md
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 1526


🏁 Script executed:

# Check if docs/secure.md exists and examine it
if [ -f "docs/secure.md" ]; then
  echo "✓ docs/secure.md exists"
  grep -n "CloudflareSettings" docs/secure.md | head -10
else
  echo "✗ docs/secure.md not found"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 481


🏁 Script executed:

# Check the actual package structure
if [ -d "packages/cloudflare-auth/src/cloudflare_auth" ]; then
  echo "Contents of packages/cloudflare-auth/src/cloudflare_auth:"
  ls -la packages/cloudflare-auth/src/cloudflare_auth/
else
  echo "✗ Expected directory not found"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 1194


🏁 Script executed:

# Check what's exported from __init__.py
echo "=== __init__.py contents ==="
cat -n packages/cloudflare-auth/src/cloudflare_auth/__init__.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 3720


🏁 Script executed:

# Check if test_imports.py exists
if [ -f "packages/cloudflare-auth/tests/test_imports.py" ]; then
  echo "✓ test_imports.py exists"
  cat -n packages/cloudflare-auth/tests/test_imports.py
else
  echo "✗ test_imports.py does not exist (action item 3 proposes creating it)"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 141


🏁 Script executed:

# Check settings.py to verify CloudflareSettings and get_cloudflare_settings are defined
echo "=== settings.py first 50 lines ==="
head -50 packages/cloudflare-auth/src/cloudflare_auth/settings.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 1714


🏁 Script executed:

# Check existing CI workflows
if [ -d ".github/workflows" ]; then
  echo "=== CI Workflow files ==="
  ls -la .github/workflows/
else
  echo "✗ .github/workflows not found"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 1379


🏁 Script executed:

# Check if the CI check already exists for imports
if [ -f ".github/workflows/ci.yml" ]; then
  echo "=== Checking for 'Test package imports' in ci.yml ==="
  grep -n "Test package imports" .github/workflows/ci.yml || echo "Not found"
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 135


🏁 Script executed:

# Check ci.yml to understand the current test structure and find where the import test should be added
cat -n .github/workflows/ci.yml | head -100

Repository: ByronWilliamsCPA/python-libs

Length of output: 3968


🏁 Script executed:

# Verify that CloudflareSettings and get_cloudflare_settings are actually exported and importable
grep -n "get_cloudflare_settings" packages/cloudflare-auth/src/cloudflare_auth/settings.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 128


🏁 Script executed:

# Check if CloudflareSettings and get_cloudflare_settings are exported from __init__.py
grep "CloudflareSettings\|get_cloudflare_settings" packages/cloudflare-auth/src/cloudflare_auth/__init__.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 54


🏁 Script executed:

# Verify test_settings.py already exists and what it covers
if [ -f "packages/cloudflare-auth/tests/test_settings.py" ]; then
  echo "✓ test_settings.py exists"
  wc -l packages/cloudflare-auth/tests/test_settings.py
  head -40 packages/cloudflare-auth/tests/test_settings.py
fi

Repository: ByronWilliamsCPA/python-libs

Length of output: 1422


🏁 Script executed:

# Check docs/secure.md to see what the intended migration path is for CloudflareSettings
echo "=== docs/secure.md sections about CloudflareSettings ==="
grep -B2 -A2 "CloudflareSettings" docs/secure.md | head -50

Repository: ByronWilliamsCPA/python-libs

Length of output: 859


🏁 Script executed:

# Check if settings should be in __all__ by looking at the full settings.py
tail -20 packages/cloudflare-auth/src/cloudflare_auth/settings.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 705


🏁 Script executed:

# Verify the actual line count of FIXES_NEEDED.md to understand the context
wc -l FIXES_NEEDED.md

Repository: ByronWilliamsCPA/python-libs

Length of output: 91


🏁 Script executed:

# Check the markdown line length guideline compliance for FIXES_NEEDED.md around lines 139-189
sed -n '139,189p' FIXES_NEEDED.md | awk '{print length, $0}' | sort -rn | head -10

Repository: ByronWilliamsCPA/python-libs

Length of output: 723


🏁 Script executed:

# Check if the proposed test imports would actually work - verify CloudflareSettings is NOT exported
echo "=== Checking __all__ exports from __init__.py ==="
grep -A 50 "__all__" packages/cloudflare-auth/src/cloudflare_auth/__init__.py

Repository: ByronWilliamsCPA/python-libs

Length of output: 899


🏁 Script executed:

# Try to verify if importing the proposed items would work
python3 << 'EOF'
import sys
import os

# Simulate what the test would do
try:
    # This should work
    from cloudflare_auth import CloudflareUser
    print("✓ Can import CloudflareUser")
except ImportError as e:
    print(f"✗ Cannot import CloudflareUser: {e}")

try:
    # This should work
    from cloudflare_auth import CloudflareAuthMiddleware
    print("✓ Can import CloudflareAuthMiddleware")
except ImportError as e:
    print(f"✗ Cannot import CloudflareAuthMiddleware: {e}")

try:
    # This should work
    from cloudflare_auth import setup_cloudflare_auth_enhanced
    print("✓ Can import setup_cloudflare_auth_enhanced")
except ImportError as e:
    print(f"✗ Cannot import setup_cloudflare_auth_enhanced: {e}")

try:
    # This might NOT work - check if CloudflareSettings is exported
    from cloudflare_auth import CloudflareSettings
    print("✓ Can import CloudflareSettings from cloudflare_auth")
except ImportError as e:
    print(f"✗ Cannot import CloudflareSettings from cloudflare_auth: {e}")

try:
    # This will work
    from cloudflare_auth.settings import CloudflareSettings, get_cloudflare_settings
    print("✓ Can import CloudflareSettings from cloudflare_auth.settings")
except ImportError as e:
    print(f"✗ Cannot import from cloudflare_auth.settings: {e}")
EOF

Repository: ByronWilliamsCPA/python-libs

Length of output: 158


🏁 Script executed:

# Look more carefully at the CI section to see if there's any import testing
grep -n "import\|Import\|test.*import" .github/workflows/ci.yml

Repository: ByronWilliamsCPA/python-libs

Length of output: 54


🏁 Script executed:

# Check if there are any remaining "src.cloudflare_auth" imports that should be "cloudflare_auth"
grep -r "from src\.cloudflare_auth\|from src\.config" packages/cloudflare-auth/src/ 2>/dev/null | grep -v ".pyc" || echo "No src. imports found"

Repository: ByronWilliamsCPA/python-libs

Length of output: 93


🏁 Script executed:

# Check middleware.py and middleware_enhanced.py for import issues
head -30 packages/cloudflare-auth/src/cloudflare_auth/middleware.py | grep "^from\|^import"

Repository: ByronWilliamsCPA/python-libs

Length of output: 54


Action items 1–2 appear complete; items 3–4 (test_imports.py and CI check) remain pending.

The steps correctly reference the cloudflare-auth package structure and align with docs/secure.md. Import fixes appear done (no src.cloudflare_auth imports remain), and CloudflareSettings module exists in packages/cloudflare-auth/src/cloudflare_auth/settings.py.

However:

  • test_imports.py (item 3) does not yet exist; the proposed test is valid—all three imports are exported in __all__
  • CI check (item 4) is not present in .github/workflows/ci.yml; the proposed command is correct

All referenced file paths and import statements match the actual module layout. Sequencing is sound and ready to implement.

🤖 Prompt for AI Agents
In @FIXES_NEEDED.md around lines 139 - 189, Add the missing import test and CI
check: create tests/test_imports.py that imports CloudflareAuthMiddleware,
CloudflareUser, and setup_cloudflare_auth_enhanced from the cloudflare_auth
package and asserts the main symbol(s) are not None (to validate public
exports), and update .github/workflows/ci.yml to add a step (e.g., "Test package
imports") that runs a one-liner import command (python -c "from cloudflare_auth
import CloudflareUser") to catch import-time errors in CI.


## Files Changed Locally (Not Committed)

All files in `packages/cloudflare-auth/src/cloudflare_auth/`:
- `__init__.py` - Fixed imports and docstring
- `middleware.py` - Fixed imports
- `middleware_enhanced.py` - Fixed imports
- `validators.py` - Fixed imports
- `security_helpers.py` - Fixed imports
- `sessions.py` - Fixed imports
- `utils.py` - Fixed imports
- `whitelist.py` - Fixed imports
- `models.py` - Fixed imports
- `csrf.py` - Fixed imports
- `rate_limiter.py` - Fixed imports
- `redis_sessions.py` - Fixed imports

---

## Testing Verification

After fixes are applied, verify with:

```bash
# 1. Clean install test
cd /tmp
uv venv test-env
source test-env/bin/activate
uv pip install /path/to/python-libs/packages/cloudflare-auth

# 2. Import test
python -c "from cloudflare_auth import CloudflareUser, setup_cloudflare_auth_enhanced"

# 3. Run package tests
cd /path/to/python-libs/packages/cloudflare-auth
uv run pytest tests/ -v
```

---

## Contact

For questions or to coordinate fixes:
- **Project**: homelab-infra (blocked PR #54)
- **Reporter**: Claude Code
- **Related**: [homelab-infra PR #54](https://github.com/ByronWilliamsCPA/homelab-infra/pull/54)
30 changes: 15 additions & 15 deletions docs/secure.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# python-libs Publishing Handoff Document

> **Repository:** [ByronWilliamsCPA/python-libs](https://github.com/ByronWilliamsCPA/python-libs)
> **Last Updated:** 2025-12-04
> **Repository:** [ByronWilliamsCPA/python-libs](https://github.com/ByronWilliamsCPA/python-libs)
> **Last Updated:** 2025-12-04
> **Status:** Ready for Implementation

## Overview
Expand Down Expand Up @@ -144,7 +144,7 @@ jobs:
run: |
TAG="${{ github.ref_name }}"
echo "Processing tag: $TAG"

if [[ "$TAG" == cloudflare-auth-v* ]]; then
echo "package_dir=packages/cloudflare-auth" >> $GITHUB_OUTPUT
echo "package_name=byronwilliamscpa-cloudflare-auth" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -240,60 +240,60 @@ from pydantic_settings import BaseSettings, SettingsConfigDict

class CloudflareSettings(BaseSettings):
"""Configuration for Cloudflare Access authentication."""

model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
case_sensitive=False,
)

# Required
cloudflare_team_domain: str = Field(default="", alias="CLOUDFLARE_TEAM_DOMAIN")
cloudflare_audience_tag: str = Field(default="", alias="CLOUDFLARE_AUDIENCE_TAG")
cloudflare_enabled: bool = Field(default=True, alias="CLOUDFLARE_ENABLED")

# Headers
jwt_header_name: str = Field(default="Cf-Access-Jwt-Assertion", alias="CF_JWT_HEADER")
email_header_name: str = Field(default="Cf-Access-Authenticated-User-Email", alias="CF_EMAIL_HEADER")

# Security
require_email_verification: bool = Field(default=True, alias="CF_REQUIRE_EMAIL_VERIFICATION")
log_auth_failures: bool = Field(default=True, alias="CF_LOG_AUTH_FAILURES")
require_cloudflare_headers: bool = Field(default=True, alias="CF_REQUIRE_CLOUDFLARE_HEADERS")

# Access control
allowed_email_domains: list[str] = Field(default_factory=list, alias="CF_ALLOWED_EMAIL_DOMAINS")
allowed_tunnel_ips: list[str] = Field(default_factory=list, alias="CF_ALLOWED_TUNNEL_IPS")

# Cookies
cookie_domain: Optional[str] = Field(default=None, alias="CF_COOKIE_DOMAIN")
cookie_path: str = Field(default="/", alias="CF_COOKIE_PATH")
cookie_secure: bool = Field(default=True, alias="CF_COOKIE_SECURE")
cookie_samesite: str = Field(default="lax", alias="CF_COOKIE_SAMESITE")

# JWT
jwt_algorithm: str = Field(default="RS256", alias="CF_JWT_ALGORITHM")
jwt_cache_max_keys: int = Field(default=16, alias="CF_JWT_CACHE_MAX_KEYS")

@field_validator("allowed_email_domains", "allowed_tunnel_ips", mode="before")
@classmethod
def parse_comma_separated(cls, v):
if isinstance(v, str):
return [item.strip() for item in v.split(",") if item.strip()] if v.strip() else []
return v or []

Comment on lines 279 to +285
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Ensure field validator is robust against edge cases.

The parse_comma_separated validator handles comma-separated strings and strips whitespace. Verify:

  1. Empty strings and None values are handled safely (currently checks if v.strip())
  2. Behavior matches expectations when fields are passed as lists directly (currently returns v or [])
  3. The validator applies to both allowed_email_domains and allowed_tunnel_ips as intended

Consider adding a docstring to clarify the expected input formats and return behavior.

@property
def issuer(self) -> str:
if not self.cloudflare_team_domain:
return ""
domain = self.cloudflare_team_domain.rstrip("/")
return f"https://{domain}" if not domain.startswith("https://") else domain

@property
def certs_url(self) -> str:
return f"{self.issuer}/cdn-cgi/access/certs" if self.issuer else ""

def is_email_allowed(self, email: str) -> bool:
if not self.allowed_email_domains:
return True
Expand Down Expand Up @@ -537,4 +537,4 @@ elif [[ "$TAG" == new-package-v* ]]; then

- **Infrastructure Questions:** Byron Williams
- **Infisical Issues:** Check https://secrets.byronwilliamscpa.com status
- **GCP Issues:** Check `assured-oss-457903` project in GCP Console
- **GCP Issues:** Check `assured-oss-457903` project in GCP Console
52 changes: 52 additions & 0 deletions packages/gemini-image/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - 2026-01-09

### Added

- OpenSSF-required files: LICENSE, SECURITY.md, CONTRIBUTING.md, CHANGELOG.md
- `exceptions.py` module with custom exception hierarchy
- `client.py` module with `GeminiClient` class and retry logic using tenacity
- `io.py` module with magic byte format detection
- `registry.py` module for PROMPTS.md documentation
- `response_parser.py` module for API response handling
- Structured logging with structlog (replaces print statements)
- `--no-document` CLI flag to disable PROMPTS.md logging
- `--resume` flag for story sequence generation (skips existing parts)
- JSON sidecar metadata files for generated images
- Python 3.10 compatibility (replaced `datetime.UTC` with `timezone.utc`)

### Changed

- Refactored `generator.py` into smaller, focused modules
- Improved error handling with specific exception types
- Output paths now honor absolute paths (no forced `output/` prefix)
- API key loading now uses python-dotenv for robust .env handling

### Fixed

- JPEG/PNG format mismatch from Gemini API (magic byte detection)
- Story mode now resumes from last completed part instead of regenerating

## [0.1.0] - 2024-12-15

### Added

- Initial release
- `generate_image()` function for text-to-image generation
- `generate_story_sequence()` function for multi-part stories
- `finalize_draft()` function for draft-to-final workflow
- CLI (`gemini-image` command)
- Support for Gemini 2.5 Flash and Gemini 3 Pro models
- Aspect ratio and resolution configuration
- Reference image support for editing
- Thinking mode with intermediate image visualization
- Draft mode for cost-effective iteration

[0.2.0]: https://github.com/ByronWilliamsCPA/python-libs/compare/gemini-image-v0.1.0...gemini-image-v0.2.0
[0.1.0]: https://github.com/ByronWilliamsCPA/python-libs/releases/tag/gemini-image-v0.1.0
Loading
Loading