server: gate suspended users out of profile and synckit billing mutations
AuthUser refreshes user.suspended on every request but most handlers
that read it are mutation paths; these five were missed:
- users/profile.rs update_profile — display name / bio edits
- users/profile.rs disconnect_stripe — payout settings
- synckit/billing.rs setup — Stripe customer creation
- synckit/billing.rs activate — subscription creation
- synckit/billing.rs patch — subscription re-pricing
Each now calls user.check_not_suspended()? after extraction.
Deliberately NOT gating synckit/billing.rs cancel — letting a
suspended developer wind down their app's subscription so they
stop being charged is humane and doesn't expose new risk.
2 files changed,
+5 insertions,
-0 deletions
| 46 |
46 |
|
AuthUser(user): AuthUser,
|
| 47 |
47 |
|
Form(req): Form<UpdateProfileRequest>,
|
| 48 |
48 |
|
) -> Result<Response> {
|
|
49 |
+ |
user.check_not_suspended()?;
|
| 49 |
50 |
|
// Validate input
|
| 50 |
51 |
|
if let Some(ref name) = req.display_name {
|
| 51 |
52 |
|
validation::validate_display_name(name)?;
|
| 314 |
315 |
|
State(state): State<AppState>,
|
| 315 |
316 |
|
AuthUser(user): AuthUser,
|
| 316 |
317 |
|
) -> Result<impl IntoResponse> {
|
|
318 |
+ |
user.check_not_suspended()?;
|
| 317 |
319 |
|
db::users::disconnect_user_stripe(&state.db, user.id).await?;
|
| 318 |
320 |
|
Ok(StatusCode::NO_CONTENT)
|
| 319 |
321 |
|
}
|
| 40 |
40 |
|
Path(app_id): Path<SyncAppId>,
|
| 41 |
41 |
|
) -> Result<impl IntoResponse> {
|
| 42 |
42 |
|
user.check_not_sandbox()?;
|
|
43 |
+ |
user.check_not_suspended()?;
|
| 43 |
44 |
|
|
| 44 |
45 |
|
let app = db::synckit_billing::get_app_with_billing(&state.db, app_id)
|
| 45 |
46 |
|
.await?
|
| 97 |
98 |
|
Json(req): Json<BillingActivateRequest>,
|
| 98 |
99 |
|
) -> Result<impl IntoResponse> {
|
| 99 |
100 |
|
user.check_not_sandbox()?;
|
|
101 |
+ |
user.check_not_suspended()?;
|
| 100 |
102 |
|
validate_knobs(&req.enforcement_mode, req.storage_gb_cap, req.key_cap, req.gb_per_key)?;
|
| 101 |
103 |
|
|
| 102 |
104 |
|
let app = db::synckit_billing::get_app_with_billing(&state.db, app_id)
|
| 168 |
170 |
|
Json(req): Json<BillingPatchRequest>,
|
| 169 |
171 |
|
) -> Result<impl IntoResponse> {
|
| 170 |
172 |
|
user.check_not_sandbox()?;
|
|
173 |
+ |
user.check_not_suspended()?;
|
| 171 |
174 |
|
validate_knobs(&req.enforcement_mode, req.storage_gb_cap, req.key_cap, req.gb_per_key)?;
|
| 172 |
175 |
|
|
| 173 |
176 |
|
let app = db::synckit_billing::get_app_with_billing(&state.db, app_id)
|