feat: auto Keycloak account creation + email-mismatch fix + script names fix #94
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api!94
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "93-feat-auto-keycloak-account-creation-on-r"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Three tightly coupled fixes for the registration-to-account-creation pipeline. When a parent completes paid registration, their Keycloak account is now created automatically with correct firstName/lastName and role. The email-mismatch bug that created duplicate parent records is fixed by using token-based lookup. The batch script now includes names in the payload to prevent VERIFY_PROFILE prompts.
Changes
src/basketball_api/services/keycloak.py(new): Shared Keycloak Admin API service module extracted from the batch script. All functions acceptbase_urlparameter. Includescreate_account_for_parent()high-level entry point with full error handling (never raises).src/basketball_api/services/password.py(new): Canonical location forgenerate_passwordandextract_first_name. Moved from scripts.src/basketball_api/routes/register.py: Token-based parent lookup whenform_tokenis present (fixes email-mismatch bug). Auto Keycloak account creation afterdb.commit()for paid registrations. Credentials section on confirmation page.src/basketball_api/config.py: Addedkeycloak_base_urlandkeycloak_admin_passwordsettings.scripts/create_keycloak_accounts.py: Refactored to import from sharedservices/keycloakmodule. Passesfirst_name/last_nametocreate_keycloak_user().scripts/password_gen.py: Re-exports frombasketball_api.services.passwordfor backward compatibility.tests/conftest.py: Default emptyBASKETBALL_KEYCLOAK_ADMIN_PASSWORDenv var.tests/test_keycloak_integration.py(new): 15 tests covering all acceptance criteria.Test Plan
pytest tests/ -v)ruff formatandruff checkpass cleanReview Checklist
Related
plan-wkq-- Phase 11 (Girls Tryout, March 21)Self-Review
Reviewed the full diff (8 files, +919 -177 lines). No issues found.
Correctness
Parent.registration_token == form_tokenis checked first, email fallback only for walk-up flow. Parent email is updated to the form value after token match, which is the intended behavior.db.commit()so parent data is safe regardless of Keycloak outcome.create_account_for_parent()catches all exceptions and returns None on failure. Guard condition (is_paid and settings.keycloak_admin_password) prevents calls when password is not configured.firstName/lastNamenow included in payload. Parent name parsed into first/last correctly.Architecture
services/keycloak.py) used by both route and script -- no duplication.services/password.pywith backward-compatible re-export fromscripts/password_gen.py.base_urlparameter (not hardcoded), enabling testing and multi-environment deployment.Test Coverage
No Issues Found
escape()for XSS preventionPR #94 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Pydantic / httpx. Keycloak Admin API integration.
Architecture assessment: Well-structured extraction of Keycloak logic into a shared service module (
services/keycloak.py). The batch script and registration route now share one codepath for user creation, role assignment, and password generation. Thecreate_account_for_parent()high-level entry point correctly wraps everything in try/except and never raises -- a Keycloak failure cannot block registration. This is the right design.Token-based lookup (email-mismatch fix): The registration route at line 833 now checks
form_tokenfirst and matches byregistration_tokencolumn, falling back to email only for walk-up (no-token) flows. This correctly addresses the duplicate-parent bug where Stripe checkout email differs from form email.PEP compliance:
services/keycloak.pyandservices/password.py.OWASP / Security:
escape()before rendering in confirmation pages (lines 311-312, 401, etc.). Good.escape(token, quote=True)(line 141). Good.keycloak_admin_passwordconfig defaults to empty string and is loaded via env var (BASKETBALL_prefix). No credentials in code. Good.conftest.pyexplicitly setsBASKETBALL_KEYCLOAK_ADMIN_PASSWORDto empty to prevent test-time Keycloak calls. Good.SQLAlchemy patterns:
db.commit()is called before Keycloak account creation (line 980), ensuring parent/player data is persisted regardless of Keycloak outcome. Correct session management.Parent.registration_tokenwith tenant scoping. Good.httpx usage:
timeout=30.0. Good.raise_for_status()called after each request. Good.BLOCKERS
None.
Test coverage: 15 new tests in
test_keycloak_integration.pycovering:scripts/password_gen.pyAll acceptance criteria have corresponding tests. No BLOCKER issues.
Input validation: Token is validated against the database (line 571, line 835). Form fields validated for required values. File uploads validated for extension, content type, and size. No unvalidated user input reaches Keycloak API calls.
Secrets: No credentials committed. Admin password loaded from environment. Default is empty string (disables feature). Config uses
BASKETBALL_env prefix via pydantic-settings.NITS
randomvssecretsfor password generation (services/password.pyline 10):random.randintis not cryptographically secure. For password generation,secrets.randbelow(90) + 10would be more appropriate. The practical risk is low given the password pattern isWestside-{Name}-{2digits}(only 90 possible suffixes per name), but usingsecretssignals intent correctly. This is a pre-existing pattern from the batch script, so not introduced by this PR.Duplicated name-parsing logic (
services/keycloak.pylines 219-221 andscripts/create_keycloak_accounts.pylines 148-150): Theparent_name.strip().split(None, 1)pattern for extracting first/last name is duplicated between the shared service and the batch script. Consider extracting aparse_parent_name(name: str) -> tuple[str, str]helper into the service module so both callers use one implementation. Minor DRY concern -- not in an auth/security path, so not a blocker.Hardcoded
KEYCLOAK_BASE_URLin batch script (scripts/create_keycloak_accounts.pyline 52): The script hasKEYCLOAK_BASE_URL = "https://keycloak.tail5b443a.ts.net"hardcoded, while the FastAPI app reads it fromsettings.keycloak_base_url(which defaults to the same value but is overridable via env). Consider reading fromBASKETBALL_KEYCLOAK_BASE_URLenv var with the same default, for consistency.Test overlap with
test_account_creation.py: The newTestKeycloakServiceModuleandTestPasswordModuleclasses intest_keycloak_integration.pypartially overlap with existing tests intest_account_creation.py(e.g.,determine_roletests,generate_passwordpattern test,extract_first_nametests). Not harmful, but worth noting for future deduplication.ADMIN_EMAILSre-export (scripts/create_keycloak_accounts.pyline 35): TheADMIN_EMAILSimport is kept with a# noqa: F401comment "re-exported for test_account_creation.py". Now thatADMIN_EMAILSlives canonically inservices/keycloak.py,test_account_creation.pycould import directly from there. Low priority.SOP COMPLIANCE
93-feat-auto-keycloak-account-creation-on-r)plan-wkqPROCESS OBSERVATIONS
settings.keycloak_admin_passwordmust be non-empty). Tests default it to empty, and the code path is designed to never block registration on Keycloak failure. The token-based lookup is a strict superset of the old behavior (falls back to email lookup when no token present).VERDICT: APPROVED