fix: vá lỗ hổng RBAC + hardcode sàn ở /orders/place, nâng cấp mã hoá và CORS

- orders.place_order: yêu cầu role trader/admin (get_current_trader_user)
  thay vì bất kỳ user đã đăng nhập nào — viewer không còn đặt được lệnh thật
- orders.place_order: resolve exchange theo OrderRequest.exchange thay vì
  hardcode "mexc", fallback về credential active gần nhất nếu không truyền
- security.py: mã hoá API key chuyển AES-256-CBC -> AES-256-GCM (có xác thực
  toàn vẹn), giữ đường giải mã cũ để credential đã lưu trước đây không hỏng
- main_api.py: CORS_ORIGINS rỗng -> deny-all thay vì fallback "*" (kèm
  allow_credentials=True là cấu hình nguy hiểm)
- docker-compose.yml: đồng bộ DB_PASSWORD giữa backend-api/scheduler và db
- frontend: OrderPanel/DashboardPage truyền kèm exchange đang chọn khi đặt lệnh

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Le
2026-07-03 21:27:47 +07:00
parent 88ce9cdd2d
commit 6c1edcda34
7 changed files with 85 additions and 35 deletions
@@ -348,6 +348,7 @@ export default function DashboardPage() {
) : (
<OrderPanel
symbol={symbol || '—'}
exchange={exchange}
lastPrice={lastPrice}
balance={balance}
isConnected={balance !== null}
@@ -4,6 +4,7 @@ import { useAppSelector } from '../../app/hooks';
interface Props {
symbol: string;
exchange: string;
lastPrice: number | null;
balance: {asset: string; free: number; used: number; total: number}[] | null;
isConnected: boolean;
@@ -23,7 +24,7 @@ function formatPrice(price: number): string {
return '$' + price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
export default function OrderPanel({ symbol, lastPrice, balance, isConnected }: Props) {
export default function OrderPanel({ symbol, exchange, lastPrice, balance, isConnected }: Props) {
const { user } = useAppSelector((state) => state.auth);
const defaultTradeSize = user?.preferences?.trade_size || 10;
const [currentTime, setCurrentTime] = useState(new Date());
@@ -97,6 +98,7 @@ export default function OrderPanel({ symbol, lastPrice, balance, isConnected }:
method: 'POST',
body: JSON.stringify({
symbol,
exchange,
side,
order_type: 'market',
amount: amt,