fix(api): send invite email when creating Portal user (#145)#149
fix(api): send invite email when creating Portal user (#145)#149b3lz3but wants to merge 3 commits intocaptainpragmatic:masterfrom
Conversation
ca9db81 to
42a30bd
Compare
|
@mostlyvirtual — this PR is ready for review. |
There was a problem hiding this comment.
Pull request overview
Fixes a Portal onboarding gap where newly created customer users were left with unusable passwords and no invite/reset email, by triggering the existing secure welcome-email flow from both the API endpoint and the staff “Create User” view.
Changes:
- Send password-reset invite email after creating a customer user in
customer_users_create(API) andcustomer_create_user(staff view). - Return
invite_email_sentin the API response and show a staff warning message if email sending fails. - Add new customer welcome email templates (HTML + text) and tests covering success/failure scenarios.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| services/platform/apps/api/customers/views.py | Calls secure welcome-email sender and returns invite_email_sent in response. |
| services/platform/apps/customers/user_management_views.py | Calls secure welcome-email sender; warns staff when email fails. |
| services/platform/templates/customers/emails/welcome_email.txt | Adds plaintext welcome/invite template with reset link. |
| services/platform/templates/customers/emails/welcome_email.html | Adds HTML welcome/invite template with reset link/button. |
| services/platform/tests/api/test_customer_api.py | Adds API tests verifying email call + invite_email_sent behavior. |
| services/platform/tests/customers/test_user_management_views.py | Adds staff-view tests verifying email call + warning-on-failure behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ic#145) New users created via the customer_users_create API and the staff customer_create_user view were left with unusable passwords and no email — permanently locked out. Call the existing _send_welcome_email_secure() to send a password-reset invite on creation. Email failure is non-blocking (user still created). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Ciprian Radulescu <craps2003@gmail.com>
42a30bd to
3c010c4
Compare
… request_ip audit logging - Fix welcome email templates: "3 days" → "2 hours" to match PASSWORD_RESET_TIMEOUT=7200s setting - Pass request_ip via get_safe_client_ip(request) to _send_welcome_email_secure for audit trail on both call sites - Update test assertions to verify request_ip is passed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review feedback addressed (68129f2)
|
… request_ip audit logging - Fix welcome email templates: "3 days" → "2 hours" to match PASSWORD_RESET_TIMEOUT=7200s setting - Pass request_ip via get_safe_client_ip(request) to _send_welcome_email_secure for audit trail on both call sites - Update test assertions to verify request_ip is passed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Ciprian Radulescu <craps2003@gmail.com>
68129f2 to
3658348
Compare
mostlyvirtual
left a comment
There was a problem hiding this comment.
Review: REWORK — 3 fixes needed before merge
The core feature is solid — invite emails now go out from both views, email failure is non-blocking, and the prior review feedback (expiry text, request_ip) was addressed. Three issues remain.
Must fix before merge
1. [HIGH] Staff view customer_create_user missing transaction.atomic() + IntegrityError handling
apps/customers/user_management_views.py:116-120 — User creation and membership creation are not wrapped in a transaction. If CustomerMembership.objects.create() fails, the User row is orphaned (committed but no membership, no email, no access). Also, the exists() check at line 112 and create_user() at line 117 are a TOCTOU race — concurrent requests with the same email bypass the check and produce an unhandled IntegrityError (500).
The API view at views.py:1011 correctly wraps both operations in transaction.atomic() and catches IntegrityError. The staff view should match.
Fix: Wrap lines 116-120 in with transaction.atomic(): and add except IntegrityError with a messages.error() redirect.
2. [HIGH] No server-side email format validation in API endpoint
apps/api/customers/views.py:996-1002 — Only checks if not email (empty string). An invalid value like "notanemail" passes, creates a user, and triggers a delivery attempt that bounces. The staff view has client-side type="email" but no server-side enforcement either.
Fix: Add django.core.validators.validate_email(email) before user creation, return 400 on ValidationError.
3. [LOW] Template branding mismatch: "PragmaticHost" vs "PRAHO"
templates/customers/emails/welcome_email.html:5 — says "Welcome to PragmaticHost!" but the system is "PRAHO". This is the first email a new user receives — branding mismatch undermines trust in the invite link.
Fix: Replace "PragmaticHost" with "PRAHO" in both .html and .txt templates.
Non-blocking observations (filed separately)
Additional findings (missing throttle, duplicate service method, empty company_name handling, hardcoded expiry text) have been filed as a separate tracking issue since they are pre-existing and out of scope for this PR.
… for invite flow - Wrap staff customer_create_user in transaction.atomic() with IntegrityError handling to prevent orphaned User rows and fix TOCTOU race on email exists check - Add validate_email() in both staff view and API endpoint to reject invalid addresses before user creation and bounced delivery - Replace "PragmaticHost" with "PRAHO" in welcome email templates Addresses PR captainpragmatic#149 review (3 blocking findings).
|
Addressed all 3 blocking findings in 42ef804:
Targeted tests pass (20/20); mypy clean on both modified files. |
… for invite flow - Wrap staff customer_create_user in transaction.atomic() with IntegrityError handling to prevent orphaned User rows and fix TOCTOU race on email exists check - Add validate_email() in both staff view and API endpoint to reject invalid addresses before user creation and bounced delivery - Replace "PragmaticHost" with "PRAHO" in welcome email templates Addresses PR captainpragmatic#149 review (3 blocking findings). Signed-off-by: Ciprian Radulescu <craps2003@gmail.com>
42ef804 to
f4c29f2
Compare
|
@mostlyvirtual all feedback addressed and CI is green — ready for another look 🙏 |
Summary
customer_users_create(API) andcustomer_create_user(staff view) now call the existing_send_welcome_email_secure()to send a password-reset inviteinvite_email_sent: falsecustomers/emails/welcome_email.html+.txt)Closes #145
Test plan
test_create_user_success— verifies invite email is called andinvite_email_sent: truein responsetest_create_user_invite_email_failure_still_succeeds— user created even if email failstest_create_user_sends_invite_email— staff view triggers emailtest_create_user_warns_on_email_failure— staff view shows warning on failure🤖 Generated with Claude Code