package integration_test import ( "context" "net/http" "testing" "time" "galaxy/integration/testenv" usermodel "galaxy/model/user" "galaxy/transcoder" ) // TestAdminUserSanctionPermanentBlock verifies that applying the // `permanent_block` sanction through the admin endpoint cascades: // - the user's active session is revoked (subsequent gateway calls // fail Unauthenticated); // - send-email-code on the same email is rejected with the // standard error envelope. func TestAdminUserSanctionPermanentBlock(t *testing.T) { plat := testenv.Bootstrap(t, testenv.BootstrapOptions{}) ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() const email = "pilot+sanction@example.com" sess := testenv.RegisterSession(t, plat, email) gw, err := sess.DialAuthenticated(ctx, plat) if err != nil { t.Fatalf("dial: %v", err) } defer gw.Close() // Sanity: signed call works pre-sanction. payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{}) if err != nil { t.Fatalf("encode payload: %v", err) } if _, err := gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{}); err != nil { t.Fatalf("pre-sanction: %v", err) } userID, err := sess.LookupUserID(ctx, plat) if err != nil { t.Fatalf("resolve user_id: %v", err) } // Admin applies permanent_block. admin := testenv.NewBackendAdminClient(plat.Backend.HTTPURL, plat.Backend.AdminUser, plat.Backend.AdminPassword) body := map[string]any{ "sanction_code": "permanent_block", "scope": "global", "reason_code": "tos_violation", "actor": map[string]any{"type": "admin", "id": plat.Backend.AdminUser}, } raw, resp, err := admin.Do(ctx, http.MethodPost, "/api/v1/admin/users/"+userID+"/sanctions", body) if err != nil || resp.StatusCode/100 != 2 { t.Fatalf("apply sanction: err=%v status=%d body=%s", err, resp.StatusCode, string(raw)) } // Subsequent authenticated calls must fail. deadline := time.Now().Add(2 * time.Second) var lastErr error for time.Now().Before(deadline) { _, lastErr = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{}) if lastErr != nil { break } time.Sleep(100 * time.Millisecond) } if lastErr == nil { t.Fatalf("authenticated call succeeded after permanent_block") } // Gateway maps a revoked session to FailedPrecondition ("device // session is revoked"); a session that vanished from the cache // before the call lands as Unauthenticated. Either is a correct // rejection. if !testenv.IsFailedPrecondition(lastErr) && !testenv.IsUnauthenticated(lastErr) { t.Fatalf("post-sanction status: %v", lastErr) } // New send-email-code on the same email must be rejected. public := testenv.NewPublicRESTClient(plat.Gateway.HTTPURL) _, _, err = public.SendEmailCode(ctx, email, "") if err == nil { t.Fatalf("send-email-code accepted for permanently blocked email") } }