8.8 KiB
Tasks: Provision Approved Access Requests
Review Workload Forecast
| Field | Value |
|---|---|
| Estimated changed lines | 900-1,400 across backend, Console, Admin, docs/tests |
| 400-line budget risk | High |
| Chained PRs recommended | Yes |
| Suggested split | PR 1 backend data/provisioning/email -> PR 2 Admin activation -> PR 3 Console status/resend/docs/tests |
Phase 0: Baseline And Safety
- 0.1 Confirm current
access_requests,users,roles,edge_projects,user_project_access, andsubscriptionsschema in migrations. - 0.2 Confirm roles
admin,platform_admin, andplatform_operatorexist in target environments. - 0.3 Confirm production env can provide
INVITATION_BASE_URL=https://app.omnioil.appandSMTP_*values. - 0.4 Decide implementation location: private helpers in
handlers/access_requests.rsvs newaccess_request_provisioning.rsmodule. - 0.5 Keep existing request submission, review, reject, and login behavior unchanged outside this change.
Phase 1: Backend Schema
- 1.1 Add migration for
access_requests.provisioned_user_id,provisioned_project_id, andprovisioned_at. - 1.2 Add migration for
access_request_invitationswithtoken_hash, email status, expiration, consumed/revoked fields, and indexes. - 1.3 Ensure migration is additive and rollback-safe; do not drop existing request data.
- 1.4 Update backend row/response types for access requests to include provisioning and latest invitation summary fields.
- 1.5 Add query helper to fetch latest invitation summary per request without exposing raw tokens.
Phase 2: Backend Token And Mailer Foundations
- 2.1 Add token generation helper: 32 random bytes, base64url without padding.
- 2.2 Add token hashing helper using SHA-256 or stronger stable digest.
- 2.3 Add tests proving stored hash differs from raw token and token length/encoding is URL-safe.
- 2.4 Add generic SMTP mailer helper using existing
SMTP_HOST,SMTP_USER,SMTP_PASS,SMTP_PORT, andSMTP_FROMenv vars. - 2.5 Add activation email body builder for inactive/pending users.
- 2.6 Add access-ready email body builder for existing active users.
- 2.7 Ensure raw activation tokens are never logged or written to audit metadata.
Phase 3: Backend Approval Provisioning
- 3.1 Refactor
approve_access_requestto run a transaction locking the request row. - 3.2 Reject approval unless request status is
pendingorin_review. - 3.3 Create
edge_projectsfrom deterministic request-derived defaults, including metadata with request id, NIT, contact, phone, and position. - 3.4 Resolve project name conflicts by appending a stable short request-id suffix.
- 3.5 Lookup existing user by normalized email.
- 3.6 For no existing user: create role
adminuser withis_active=falseand an unusable random Argon2 password hash. - 3.7 For existing active customer user: preserve password and active state.
- 3.8 For existing inactive customer user: preserve inactive state and create activation invitation.
- 3.9 For existing provider-role user with same email: fail approval with clear conflict error.
- 3.10 Insert or reactivate
user_project_accesswithproject_role='owner'. - 3.11 Insert
subscriptionsrow withstatus='trial'andtrial_ends_at = now + 7 daysbefore billing reads can create an active default. - 3.12 Update
access_requestswith approved/provisioned fields and reviewer metadata. - 3.13 Create activation invitation for inactive users with 7-day expiry.
- 3.14 For existing active users, decide whether to create an invitation row for email tracking or a separate notification-only record; implement consistently with design.
- 3.15 Commit transaction before SMTP send.
- 3.16 Send activation/access-ready email and update
email_status,email_sent_at, oremail_error. - 3.17 Add audit events for approval, project provisioning, user creation/linking, trial creation, invitation creation, email sent, email failed, and activation.
Phase 4: Backend Activation And Resend Endpoints
- 4.1 Register public
POST /api/auth/invitations/activateroute. - 4.2 Optionally register public
GET /api/auth/invitations/validate?token=...route for frontend preflight. - 4.3 Implement activation token validation: hash lookup, approved request, not expired, not consumed, not revoked, user/project exists.
- 4.4 Implement password validation and Argon2 hashing for activation.
- 4.5 Activate user and mark invitation
consumed_atin one transaction. - 4.6 Register provider-only
POST /api/platform/access-requests/{id}/invitation/resendroute. - 4.7 Resend endpoint revokes all previous unconsumed invitations for the request.
- 4.8 Resend endpoint creates a fresh 7-day invitation and sends email.
- 4.9 Resend endpoint records audit and email status.
Phase 5: Backend Tests
Backend DB integration evidence: services/backend-api/tests/access_request_db_integration.rs calls the real approve_access_request, activate_invitation, and resend_access_request_invitation handlers/routes against a migrated disposable Timescale/PostgreSQL fixture. Evidence command passed against Docker test DB omnioil_access_request_test on port 55432: $env:DATABASE_URL='postgres://omnioil_test:omnioil_test_password@127.0.0.1:55432/omnioil_access_request_test'; cargo test -p backend-api --test access_request_db_integration -- --ignored --nocapture -> 5/5 passed, including non-provider/customer admin denial for the provider resend route without token rotation. Focused unit evidence also covers activation-link construction and platform-role predicates.
- 5.1 Test approving pending request provisions project, customer admin, project access, trial subscription, and invitation.
- 5.2 Test approving in-review request follows same provisioning path.
- 5.3 Test approved/rejected request cannot be re-approved.
- 5.4 Test existing active customer user is linked without password reset or deactivation.
- 5.5 Test existing inactive user gets a fresh activation invitation.
- 5.6 Test provider-role duplicate email blocks approval.
- 5.7 Test SMTP failure leaves request approved/provisioned and marks email failed.
- 5.8 Test raw token is not persisted in invitation rows or audit metadata.
- 5.9 Test expired, consumed, and revoked tokens cannot activate.
- 5.10 Test resend revokes previous unconsumed tokens.
Phase 6: Frontend Admin Activation
- 6.1 Add
/activateroute before protected admin routes. - 6.2 Add
/auth/invitationstoAUTH_BYPASS_ENDPOINTS. - 6.3 Add activation API functions under
features/auth/api. - 6.4 Add activation schema with password and confirmation validation.
- 6.5 Create
ActivatePagewith states: validating token, invalid/expired, password form, success. - 6.6 Ensure activation token is read from URL and not stored in auth store/local storage.
- 6.7 On success, show clear path to
/login; do not auto-login in this change. - 6.8 Add focused tests for valid token form, invalid token, and success state.
Phase 7: Frontend Console Provisioning UX
- 7.1 Extend access-request API types with provisioned user/project and invitation summary fields.
- 7.2 Add resend invitation API function.
- 7.3 Update request detail panel to show provisioned project, customer admin, activation email status, sent time, expiration, and consumed state.
- 7.4 Keep approve button single-click; backend handles automatic project defaults.
- 7.5 Show resend as primary action only when email failed or invitation expired/unconsumed.
- 7.6 Show successful approval toast that distinguishes provisioned + email sent vs provisioned + email failed.
- 7.7 Add focused tests for provisioning status and resend action visibility.
Phase 8: Docs And Deployment
- 8.1 Document
INVITATION_BASE_URL=https://app.omnioil.appin deployment docs. - 8.2 Document required
SMTP_*variables for approval/activation email. - 8.3 Document that initial projects start with 7-day trial and payment enforcement is future billing lifecycle work.
- 8.4 Update
.env.exampleif present; do not commit real secrets. - 8.5 Add operator note: SMTP failure does not rollback approval; use Console resend.
Phase 9: Verification
- 9.1 Run targeted backend tests for access request provisioning and invitation activation.
- 9.2 Run targeted frontend-admin tests for activation page.
- 9.3 Run targeted frontend-console tests for access request detail/resend.
- 9.4 Run lint for touched frontends.
- 9.5 Run targeted
cargo testfor backend modules.cargo fmt -- --checkremains documented as external repo-wide formatting drift outside this feature. - 9.6 Manually verify local happy path with stubbed SMTP if available: submit request -> approve -> activate -> login.