PHP WebShell

Текущая директория: /var/www/bitcardoApp/user/security

Просмотр файла: limits.php

<?php
// user/security/limits.php
require_once __DIR__ . '/../../config/bootstrap.php';

if (empty($_SESSION['user_id'])) {
  header("Location: /login.php");
  exit();
}

$userId = (int)$_SESSION['user_id'];

/**
 * ============================================
 * Page Lock: Require password before access
 * ============================================
 * - Stores a short-lived "reauth" stamp in session
 * - TTL default: 10 minutes
 */
$REAUTH_TTL = 10 * 60; // 10 minutes
$REAUTH_KEY = 'reauth_limits_ts';
$lockError  = '';

if (empty($_SESSION['csrf_limits'])) {
  $_SESSION['csrf_limits'] = bin2hex(random_bytes(16));
}

function get_user_password_hash($conn, int $userId): ?string {
  $hash = null;
  if ($st = $conn->prepare("SELECT password_hash FROM users WHERE user_id=? LIMIT 1")) {
    $st->bind_param('i', $userId);
    $st->execute();
    $st->bind_result($hash);
    $st->fetch();
    $st->close();
  }
  return $hash ?: null;
}

function needs_reauth(int $ttl, string $key): bool {
  if (empty($_SESSION[$key])) return true;
  $ts = (int)$_SESSION[$key];
  return ($ts <= 0) || ((time() - $ts) > $ttl);
}

// Handle re-auth submit
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'reauth_limits') {
  $csrf = (string)($_POST['csrf'] ?? '');
  $pwd  = (string)($_POST['password'] ?? '');

  if (!hash_equals((string)($_SESSION['csrf_limits'] ?? ''), $csrf)) {
    $lockError = "Security check failed. Please try again.";
  } else {
    $hash = get_user_password_hash($conn, $userId);
    if (!$hash) {
      $lockError = "Unable to verify your account right now.";
    } elseif (!password_verify($pwd, $hash)) {
      $lockError = "Incorrect password. Please try again.";
    } else {
      // OK
      $_SESSION[$REAUTH_KEY] = time();

      // Redirect back (preserve wallet_id if present)
      $wallet_id = isset($_GET['wallet_id']) ? (int)$_GET['wallet_id'] : 0;
      $redirect = "/user/security/limits.php";
      if ($wallet_id > 0) $redirect .= "?wallet_id=" . $wallet_id;
      header("Location: " . $redirect);
      exit();
    }
  }
}

// Gate: if not recently authenticated, show password prompt and exit
if (needs_reauth($REAUTH_TTL, $REAUTH_KEY)) {
  $wallet_id = isset($_GET['wallet_id']) ? (int)$_GET['wallet_id'] : 0;
  $actionUrl = "/user/security/limits.php" . ($wallet_id > 0 ? "?wallet_id=" . $wallet_id : "");

  include '../common/header.php';
  ?>
  <div class="container mt-3">
    <div class="row">
      <? include '../common/nav.php'; ?>

      <main class="col-md-9 col-lg-10 px-md-5 mb-5">
        <? include '../common/page-header.php'; ?>

        <div class="container my-5 px-md-5 ms-md-4">
          <div class="row justify-content-center">
            <div class="col-12 col-md-7 col-lg-5">
              <div class="card-soft">
                <div class="d-flex align-items-center justify-content-between mb-2">
                  <div>
                    <h5 class="mb-0">Confirm it’s you</h5>
                    <div class="text-muted small">Enter your password to view and manage withdrawal limits.</div>
                  </div>
                  <a href="/user/security/security_privacy.php" class="btn btn-outline-secondary" style="padding:4px 10px;font-size:.80rem;border-radius:10px;">
                    <i class="bi bi-arrow-left"></i> Back
                  </a>
                </div>

                <?php if ($lockError): ?>
                  <div class="alert alert-danger mt-3 mb-0"><?= htmlspecialchars($lockError) ?></div>
                <?php endif; ?>

                <form method="post" action="<?= htmlspecialchars($actionUrl) ?>" class="mt-3">
                  <input type="hidden" name="action" value="reauth_limits">
                  <input type="hidden" name="csrf" value="<?= htmlspecialchars((string)$_SESSION['csrf_limits']) ?>">

                  <label class="form-label">Password</label>
                  <input type="password" name="password" class="form-control" placeholder="Enter your password" required>

                  <button type="submit" class="btn btn-dark w-100 mt-3">
                    Continue
                  </button>

                  <div class="text-muted small mt-2">
                    This step helps keep your account safe.
                  </div>
                </form>
              </div>
            </div>
          </div>
        </div>

      </main>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script>
  <? include '../common/footer.php'; ?>
  <?php
  exit();
}

/**
 * ============================================
 * Normal page code starts here (authenticated)
 * ============================================
 */

$errors = [];
$success = '';

if (!empty($_GET['saved'])) {
  $success = "Withdrawal limits saved successfully.";
}

// ------------------------------------
// Fetch user level caps + level_name
// ------------------------------------
$levelName = 'Starter';
$cap_withdraw_usd = 0.00;
$cap_daily_usd    = 0.00;
$cap_withdraw_ngn = 0.00;
$cap_daily_ngn    = 0.00;

$sqlCaps = "
  SELECT
    COALESCE(ul.level_name, 'Starter') AS level_name,
    COALESCE(ul.withdraw_limit, 0) AS withdraw_limit_usd,
    COALESCE(ul.daily_withdraw_limit, 0) AS daily_withdraw_limit_usd,
    COALESCE(ul.withdraw_limit_ngn, 0) AS withdraw_limit_ngn,
    COALESCE(ul.daily_withdraw_limit_ngn, 0) AS daily_withdraw_limit_ngn
  FROM users u
  LEFT JOIN user_level ul ON ul.level_id = u.level_id
  WHERE u.user_id = ?
  LIMIT 1
";
if ($st = $conn->prepare($sqlCaps)) {
  $st->bind_param('i', $userId);
  $st->execute();
  $st->bind_result($levelName, $cap_withdraw_usd, $cap_daily_usd, $cap_withdraw_ngn, $cap_daily_ngn);
  $st->fetch();
  $st->close();
}

$cap_withdraw_usd = (float)$cap_withdraw_usd;
$cap_daily_usd    = (float)$cap_daily_usd;
$cap_withdraw_ngn = (float)$cap_withdraw_ngn;
$cap_daily_ngn    = (float)$cap_daily_ngn;

// ------------------------------------
// Fetch user's wallets
// ------------------------------------
$wallets = [];
$sqlWallets = "
  SELECT wallet_id, coin, label, wallet_add,
         COALESCE(withdraw_single_limit, 0) AS withdraw_single_limit,
         COALESCE(withdraw_daily_limit, 0) AS withdraw_daily_limit
  FROM user_wallets
  WHERE user_id = ?
  ORDER BY
    CASE WHEN coin='NGN' THEN 0 ELSE 1 END,
    coin ASC,
    wallet_id DESC
";
if ($st = $conn->prepare($sqlWallets)) {
  $st->bind_param('i', $userId);
  $st->execute();
  $res = $st->get_result();
  while ($row = $res->fetch_assoc()) {
    $wallets[] = $row;
  }
  $st->close();
}

// Build summary: all wallets with any limit set
$setLimits = [];
foreach ($wallets as $w) {
  $s = (float)($w['withdraw_single_limit'] ?? 0);
  $d = (float)($w['withdraw_daily_limit'] ?? 0);
  if ($s > 0 || $d > 0) {
    $setLimits[] = $w;
  }
}

// Determine selected wallet (GET only)
$selectedWalletId = 0;
if (isset($_GET['wallet_id']) && is_numeric($_GET['wallet_id'])) {
  $selectedWalletId = (int)$_GET['wallet_id'];
}

// Dropdown: remove ANY wallet where a limit is already set (single>0 OR daily>0)
$dropdownWallets = [];
foreach ($wallets as $w) {
  $s = (float)($w['withdraw_single_limit'] ?? 0);
  $d = (float)($w['withdraw_daily_limit'] ?? 0);
  if (!($s > 0 || $d > 0)) {
    $dropdownWallets[] = $w;
  }
}

// Allowed selected wallet must be from dropdownWallets
$selectedWalletIdAllowed = 0;
if (!empty($dropdownWallets)) {
  $selectedWalletIdAllowed = (int)$dropdownWallets[0]['wallet_id'];

  if ($selectedWalletId > 0) {
    foreach ($dropdownWallets as $w) {
      if ((int)$w['wallet_id'] === $selectedWalletId) {
        $selectedWalletIdAllowed = $selectedWalletId;
        break;
      }
    }
  }
}

// Load selected wallet (only from allowed list)
$sel = null;
if ($selectedWalletIdAllowed > 0) {
  $sqlSel = "
    SELECT wallet_id, coin, label, wallet_add,
           COALESCE(withdraw_single_limit, 0) AS withdraw_single_limit,
           COALESCE(withdraw_daily_limit, 0) AS withdraw_daily_limit
    FROM user_wallets
    WHERE wallet_id = ? AND user_id = ?
    LIMIT 1
  ";
  if ($st = $conn->prepare($sqlSel)) {
    $st->bind_param('ii', $selectedWalletIdAllowed, $userId);
    $st->execute();
    $res = $st->get_result();
    $sel = $res ? $res->fetch_assoc() : null;
    $st->close();
  }
}

// Handle SAVE (POST only)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_limit') {

  $postWalletId = (isset($_POST['wallet_id']) && is_numeric($_POST['wallet_id'])) ? (int)$_POST['wallet_id'] : 0;

  // Must be in dropdownWallets (i.e., no limits set yet)
  $allowed = false;
  foreach ($dropdownWallets as $w) {
    if ((int)$w['wallet_id'] === $postWalletId) {
      $allowed = true;
      break;
    }
  }

  if (!$allowed) {
    $errors[] = "This wallet is not eligible for setting a new limit.";
  } else {

    $selPost = null;
    $sqlSel2 = "SELECT wallet_id, coin FROM user_wallets WHERE wallet_id=? AND user_id=? LIMIT 1";
    if ($st = $conn->prepare($sqlSel2)) {
      $st->bind_param('ii', $postWalletId, $userId);
      $st->execute();
      $res = $st->get_result();
      $selPost = $res ? $res->fetch_assoc() : null;
      $st->close();
    }

    if (!$selPost) {
      $errors[] = "Please select a valid wallet.";
    } else {
      $coin = strtoupper(trim((string)($selPost['coin'] ?? '')));

      $single = isset($_POST['withdraw_single_limit']) ? (float)$_POST['withdraw_single_limit'] : -1;
      $daily  = isset($_POST['withdraw_daily_limit'])  ? (float)$_POST['withdraw_daily_limit']  : -1;

      if ($single < 0) $errors[] = "Single transfer limit must be 0 or higher.";
      if ($daily < 0)  $errors[] = "Daily limit must be 0 or higher.";

      if ($single >= 0 && $daily >= 0 && $daily > 0 && $single > $daily) {
        $errors[] = "Daily limit must be equal to or greater than single transfer limit.";
      }

      if (empty($errors)) {
        if ($coin === 'NGN') {
          if ($single > $cap_withdraw_ngn) $errors[] = "Single transfer limit cannot exceed your level cap (" . number_format($cap_withdraw_ngn, 2) . " NGN).";
          if ($daily  > $cap_daily_ngn)    $errors[] = "Daily limit cannot exceed your level cap (" . number_format($cap_daily_ngn, 2) . " NGN).";
        } else {
          if ($single > $cap_withdraw_usd) $errors[] = "Single transfer limit cannot exceed your level cap (" . number_format($cap_withdraw_usd, 2) . " USD).";
          if ($daily  > $cap_daily_usd)    $errors[] = "Daily limit cannot exceed your level cap (" . number_format($cap_daily_usd, 2) . " USD).";
        }
      }

      if (empty($errors)) {
        $sqlUp = "
          UPDATE user_wallets
          SET withdraw_single_limit = ?, withdraw_daily_limit = ?, updated_at = CURRENT_TIMESTAMP
          WHERE wallet_id = ? AND user_id = ?
          LIMIT 1
        ";
        if ($up = $conn->prepare($sqlUp)) {
          $up->bind_param('ddii', $single, $daily, $postWalletId, $userId);
          if ($up->execute()) {
            header("Location: /user/security/limits.php?saved=1");
            exit();
          } else {
            $errors[] = "Unable to save limits. Please try again.";
          }
          $up->close();
        } else {
          $errors[] = "Unable to save limits right now.";
        }
      }
    }
  }
}

// UI values
$selectedCoin = $sel ? strtoupper(trim((string)($sel['coin'] ?? ''))) : '';
$unit = ($selectedCoin === 'NGN') ? 'NGN' : 'USD';

$capSingle = ($selectedCoin === 'NGN') ? $cap_withdraw_ngn : $cap_withdraw_usd;
$capDaily  = ($selectedCoin === 'NGN') ? $cap_daily_ngn    : $cap_daily_usd;

$currentSingle = $sel ? (float)($sel['withdraw_single_limit'] ?? 0) : 0.00;
$currentDaily  = $sel ? (float)($sel['withdraw_daily_limit'] ?? 0)  : 0.00;

include '../common/header.php';
?>

<style>
  .btn-xxs{
    padding: 1px 6px;
    font-size: .70rem;
    line-height: 1.1;
    border-radius: 999px;
  }
  .limits-card{
    padding: 10px 12px !important;
    border-radius: 14px;
  }
  .limits-card hr{
    margin: 8px 0;
    opacity: .20;
  }
  .limits-title{
    font-size: .92rem;
    font-weight: 700;
  }
  .limit-label{
    font-size: .72rem;
    color: rgba(85, 84, 84, 0.7);
    margin-bottom: 2px;
    display: block;
  }
  .limit-amount{
    font-size: .82rem;
    font-weight: 600;
    line-height: 1.1;
  }
  .back-btn-sm{
    padding: 4px 10px;
    font-size: .80rem;
    border-radius: 10px;
  }
</style>

<div class="container mt-3">
  <div class="row">

    <? include '../common/nav.php'; ?>

    <main class="col-md-9 col-lg-10 px-md-5 mb-5">
      <? include '../common/page-header.php'; ?>

      <div class="container my-5 px-md-5 ms-md-4">

        <div class="d-flex align-items-center justify-content-between mb-3">
          <div>
            <h5 class="mb-0">Withdrawal limit</h5>
            <div class="text-muted small">Set single transfer and daily withdrawal limits per wallet.</div>
          </div>
          <a href="/user/security/security_privacy.php" class="btn btn-sm btn-outline-secondary back-btn-sm">
            <i class="bi bi-arrow-left"></i> Back
          </a>
        </div>

        <?php if ($success): ?>
          <div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
        <?php endif; ?>

        <?php if (!empty($errors)): ?>
          <div class="alert alert-danger">
            <strong>Please fix the following:</strong>
            <ul class="mb-0">
              <?php foreach ($errors as $e): ?><li><?= htmlspecialchars($e) ?></li><?php endforeach; ?>
            </ul>
          </div>
        <?php endif; ?>

        <div class="mb-3 text-muted small">
          <strong>Account level:</strong> <?= htmlspecialchars((string)$levelName) ?> <br>
          <span class="ms-2">
            <strong>USD caps:</strong> Single <?= number_format($cap_withdraw_usd, 2) ?>, Daily <?= number_format($cap_daily_usd, 2) ?>
          </span> <br>
          <span class="ms-2">
            <strong>NGN caps:</strong> Single <?= number_format($cap_withdraw_ngn, 2) ?>, Daily <?= number_format($cap_daily_ngn, 2) ?>
          </span>
        </div>

        <?php if (empty($wallets)): ?>
          <div class="card-soft">
            <div class="text-muted">No wallet found for your account.</div>
          </div>
        <?php else: ?>

          <?php if (!empty($setLimits)): ?>
            <h6 class="section-title">Your set limits</h6>

            <div class="row g-2 mb-3">
              <?php foreach ($setLimits as $w): ?>
                <?php
                  $wid   = (int)$w['wallet_id'];
                  $coin  = strtoupper(trim((string)($w['coin'] ?? '')));
                  $u     = ($coin === 'NGN') ? 'NGN' : 'USD';

                  $single = (float)($w['withdraw_single_limit'] ?? 0);
                  $daily  = (float)($w['withdraw_daily_limit'] ?? 0);
                ?>
                <div class="col-12 col-md-6 col-lg-4">
                  <div class="card-soft limits-card h-100">
                    <div class="d-flex justify-content-between align-items-start">
                      <div class="limits-title"><?= htmlspecialchars($coin) ?> Wallet</div>
                      <a class="btn btn-outline-secondary btn-xxs"
                         href="/user/security/limits.php?wallet_id=<?= $wid ?>">
                        Edit
                      </a>
                    </div>

                    <hr>

                    <div class="d-flex justify-content-between">
                      <div>
                        <span class="limit-label">Single transfer</span>
                        <div class="limit-amount"><?= number_format($single, 2) ?> <?= htmlspecialchars($u) ?></div>
                      </div>
                      <div class="text-end">
                        <span class="limit-label">Daily limit</span>
                        <div class="limit-amount"><?= number_format($daily, 2) ?> <?= htmlspecialchars($u) ?></div>
                      </div>
                    </div>

                  </div>
                </div>
              <?php endforeach; ?>
            </div>
          <?php endif; ?>

          <h6 class="section-title">Set limits</h6>
          <div class="card-soft">

            <div class="mb-3">
              <label class="form-label">Select wallet</label>
              <select class="form-select" onchange="if(this.value){window.location='?wallet_id='+this.value;}">
                <option value="">Choose a wallet</option>
                <?php foreach ($dropdownWallets as $w): ?>
                  <?php
                    $wid  = (int)$w['wallet_id'];
                    $coin = strtoupper((string)($w['coin'] ?? ''));
                    $lab  = trim((string)($w['label'] ?? ''));
                    $addr = trim((string)($w['wallet_add'] ?? ''));
                    $title = $coin . ($lab ? " • " . $lab : "") . ($addr ? " • " . substr($addr, 0, 10) . "..." : "");
                  ?>
                  <option value="<?= $wid ?>" <?= ($selectedWalletIdAllowed === $wid) ? 'selected' : '' ?>>
                    <?= htmlspecialchars($title) ?>
                  </option>
                <?php endforeach; ?>
              </select>

              <div class="form-text">
                NGN wallet uses NGN values. Other wallets use USD values.
              </div>

              <?php if (empty($dropdownWallets)): ?>
                <div class="text-muted small mt-2">
                  All wallets already have withdrawal limits set.
                </div>
              <?php endif; ?>
            </div>

            <?php if (!$sel): ?>
              <div class="text-muted">Select a wallet to set limits.</div>
            <?php else: ?>

              <form method="post" action="">
                <input type="hidden" name="action" value="save_limit">
                <input type="hidden" name="wallet_id" value="<?= (int)$selectedWalletIdAllowed ?>">

                <div class="row g-3">
                  <div class="col-md-6">
                    <label class="form-label">Single transfer limit (<?= htmlspecialchars($unit) ?>)</label>
                    <input
                      type="number"
                      step="0.01"
                      min="0"
                      name="withdraw_single_limit"
                      class="form-control"
                      value="<?= htmlspecialchars(number_format($currentSingle, 2, '.', '')) ?>"
                      placeholder="0.00"
                      required
                    >
                    <div class="form-text">Cap: <?= number_format($capSingle, 2) ?> <?= htmlspecialchars($unit) ?></div>
                  </div>

                  <div class="col-md-6">
                    <label class="form-label">Daily limit (<?= htmlspecialchars($unit) ?>)</label>
                    <input
                      type="number"
                      step="0.01"
                      min="0"
                      name="withdraw_daily_limit"
                      class="form-control"
                      value="<?= htmlspecialchars(number_format($currentDaily, 2, '.', '')) ?>"
                      placeholder="0.00"
                      required
                    >
                    <div class="form-text">Cap: <?= number_format($capDaily, 2) ?> <?= htmlspecialchars($unit) ?></div>
                  </div>

                  <div class="col-12 mt-2">
                    <button type="submit" class="btn btn-dark">
                      <i class="bi bi-save2"></i> Save limits
                    </button>
                  </div>
                </div>
              </form>

            <?php endif; ?>
          </div>

        <?php endif; ?>

      </div>
    </main>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script>

<? include '../common/footer.php'; ?>

Выполнить команду


Для локальной разработки. Не используйте в интернете!