PHP WebShell

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

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

<?php
// backyard/user/security/2fa.php
include '../common/header.php';
if (!isset($conn)) { include_once '../../config/db_config.php'; }
require_once '../../models/security/2fa.php';
date_default_timezone_set('Africa/Lagos');

function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }

$filters = [
  'q'         => $_GET['q'] ?? '',
  'status'    => $_GET['status'] ?? 'all',
  'only_totp' => isset($_GET['only_totp']) ? 1 : 0,
  'page'      => (int)($_GET['page'] ?? 1),
  'per_page'  => (int)($_GET['per_page'] ?? 25),
];
$list = fa_list_users($conn, $filters);
function qurl(array $add=[]){ $qs = array_merge($_GET,$add); return '?'.http_build_query($qs); }
?>
<style>
  .code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; }
  @media (max-width: 767.98px){
    .stack-sm>*{ margin-bottom:.5rem; }
    .nk-block-head .nk-block-between-md { gap:.75rem; }
  }
</style>

<div class="nk-content nk-content-fluid mt-5">
  <div class="container-xl wide-lg">
    <div class="nk-content-body">

      <div class="nk-block-head">
        <div class="nk-block-between-md g-3">
          <div class="nk-block-head-content">
            <h4 class="nk-block-title fw-normal mb-1">2FA Admin</h4>
            <p class="text-soft mb-0">See who has TOTP enabled, enable/disable, reset, and regenerate backup codes.</p>
          </div>
          <div class="nk-block-head-content">
            <a href="../dashboard/index.php" class="btn btn-outline-secondary btn-sm">Back</a>
          </div>
        </div>
      </div>

      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <form class="row g-2 mb-2" method="get">
              <div class="col-12 col-md-4">
                <label class="form-label small">Search user (name / email / phone)</label>
                <input type="text" class="form-control" name="q" value="<?= h($filters['q']); ?>">
              </div>
              <div class="col-6 col-md-2">
                <label class="form-label small">Status</label>
                <select class="form-select" name="status">
                  <option value="all"  <?= $filters['status']==='all'?'selected':''; ?>>All</option>
                  <option value="active"  <?= $filters['status']==='active'?'selected':''; ?>>Active</option>
                  <option value="inactive"  <?= $filters['status']==='inactive'?'selected':''; ?>>Inactive</option>
                </select>
              </div>
              <div class="col-6 col-md-2 d-flex align-items-end">
                <div class="form-check">
                  <input type="checkbox" class="form-check-input" id="only_totp" name="only_totp" <?= $filters['only_totp']?'checked':''; ?>>
                  <label class="form-check-label small" for="only_totp">Only with TOTP</label>
                </div>
              </div>
              <div class="col-6 col-md-2">
                <label class="form-label small">Per Page</label>
                <select class="form-select" name="per_page" onchange="this.form.submit()">
                  <?php foreach([25,50,100] as $pp): ?>
                    <option value="<?= $pp; ?>" <?= $filters['per_page']==$pp?'selected':''; ?>><?= $pp; ?></option>
                  <?php endforeach; ?>
                </select>
              </div>
              <div class="col-6 col-md-2 d-flex align-items-end justify-content-end">
                <button class="btn btn-primary w-100 w-md-auto">Apply</button>
              </div>
            </form>

            <div class="d-flex justify-content-between align-items-center mb-2">
              <h6 class="mb-0">Users</h6>
              <div class="small text-soft"><?= number_format($list['total']); ?> total</div>
            </div>

            <!-- Mobile list -->
            <div class="d-md-none">
              <?php if(empty($list['rows'])): ?>
                <div class="alert alert-light border mb-0">No users found.</div>
              <?php else: foreach($list['rows'] as $u):
                $name = trim(($u['first_name']??'').' '.($u['last_name']??''));
                $enabled = (int)($u['totp_enabled'] ?? 0) === 1;
              ?>
                <div class="border rounded-3 p-3 mb-2">
                  <div class="d-flex justify-content-between">
                    <div>
                      <div class="fw-semibold"><?= h($name ?: '—'); ?></div>
                      <div class="small text-soft"><?= h($u['email'] ?: $u['phone'] ?: ''); ?></div>
                    </div>
                    <span class="badge <?= $enabled?'bg-success':'bg-secondary'; ?>"><?= $enabled?'TOTP On':'TOTP Off'; ?></span>
                  </div>
                  <div class="d-flex gap-2 mt-2">
                    <?php if($enabled): ?>
                      <button class="btn btn-sm btn-outline-warning" onclick="faAction(<?= (int)$u['user_id'];?>,'disable')">Disable</button>
                      <button class="btn btn-sm btn-outline-danger" onclick="faAction(<?= (int)$u['user_id'];?>,'reset')">Reset TOTP</button>
                    <?php else: ?>
                      <button class="btn btn-sm btn-outline-success" onclick="faAction(<?= (int)$u['user_id'];?>,'enable')">Enable</button>
                    <?php endif; ?>
                    <button class="btn btn-sm btn-outline-primary" onclick="regenCodes(<?= (int)$u['user_id'];?>)">Regenerate Codes</button>
                  </div>
                </div>
              <?php endforeach; endif; ?>
            </div>

            <!-- Desktop table -->
            <div class="table-responsive d-none d-md-block">
              <table class="table table-striped align-middle">
                <thead class="small text-soft">
                  <tr>
                    <th style="width:80px;">User ID</th>
                    <th>Name</th>
                    <th>Contact</th>
                    <th>Status</th>
                    <th>TOTP</th>
                    <th class="text-end">Actions</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if(empty($list['rows'])): ?>
                    <tr><td colspan="6" class="text-center text-muted py-4">No users found.</td></tr>
                  <?php else: foreach($list['rows'] as $u):
                    $name = trim(($u['first_name']??'').' '.($u['last_name']??''));
                    $enabled = (int)($u['totp_enabled'] ?? 0) === 1;
                  ?>
                    <tr>
                      <td class="code"><?= (int)$u['user_id']; ?></td>
                      <td><?= h($name ?: '—'); ?></td>
                      <td class="small text-soft"><?= h($u['email'] ?: $u['phone'] ?: '—'); ?></td>
                      <td>
                        <?php if (strtolower((string)$u['user_status'])==='active'): ?>
                          <span class="badge bg-success">Active</span>
                        <?php else: ?>
                          <span class="badge bg-danger">Inactive</span>
                        <?php endif; ?>
                      </td>
                      <td><span class="badge <?= $enabled?'bg-success':'bg-secondary'; ?>"><?= $enabled?'Enabled':'Disabled'; ?></span></td>
                      <td class="text-end">
                        <?php if($enabled): ?>
                          <button class="btn btn-xs btn-outline-warning me-1" onclick="faAction(<?= (int)$u['user_id'];?>,'disable')">Disable</button>
                          <button class="btn btn-xs btn-outline-danger me-1" onclick="faAction(<?= (int)$u['user_id'];?>,'reset')">Reset</button>
                        <?php else: ?>
                          <button class="btn btn-xs btn-outline-success me-1" onclick="faAction(<?= (int)$u['user_id'];?>,'enable')">Enable</button>
                        <?php endif; ?>
                        <button class="btn btn-xs btn-outline-primary" onclick="regenCodes(<?= (int)$u['user_id'];?>)">Regen Codes</button>
                      </td>
                    </tr>
                  <?php endforeach; endif; ?>
                </tbody>
              </table>
            </div>

            <?php if($list['pages']>1):
              $p=$list['page']; $pages=$list['pages']; $prev=max(1,$p-1); $next=min($pages,$p+1); ?>
              <div class="d-flex justify-content-between align-items-center mt-2">
                <div class="small text-soft">Page <?= $p; ?> of <?= $pages; ?></div>
                <ul class="pagination pagination-sm mb-0">
                  <li class="page-item <?= $p<=1?'disabled':''; ?>"><a class="page-link" href="<?= qurl(['page'=>1]); ?>">« First</a></li>
                  <li class="page-item <?= $p<=1?'disabled':''; ?>"><a class="page-link" href="<?= qurl(['page'=>$prev]); ?>">‹ Prev</a></li>
                  <li class="page-item disabled"><span class="page-link"><?= $p; ?></span></li>
                  <li class="page-item <?= $p>=$pages?'disabled':''; ?>"><a class="page-link" href="<?= qurl(['page'=>$next]); ?>">Next ›</a></li>
                  <li class="page-item <?= $p>=$pages?'disabled':''; ?>"><a class="page-link" href="<?= qurl(['page'=>$pages]); ?>">Last »</a></li>
                </ul>
              </div>
            <?php endif; ?>

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

      <!-- Codes Modal -->
      <div class="modal fade" id="codesModal" tabindex="-1" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered">
          <div class="modal-content">
            <div class="modal-header">
              <h6 class="modal-title">Backup Codes (displayed once)</h6>
              <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body">
              <div id="codesBox" class="code"></div>
              <div class="small text-soft mt-2">Store these codes securely. They will not be shown again.</div>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
            </div>
          </div>
        </div>
      </div>

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

<script>
async function faAction(user_id, action){
  const labels = {enable:'Enable TOTP for this user?', disable:'Disable TOTP for this user?', reset:'Reset (wipe) TOTP for this user?'};
  if(!confirm(labels[action] || 'Proceed?')) return;
  try{
    const resp = await fetch('../../models/security/2fa_actions.php', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify({action, user_id})
    });
    const txt = await resp.text();
    let data; try{ data = JSON.parse(txt); }catch(e){ throw new Error('Bad JSON: '+txt.substring(0,160)); }
    if(!data.ok){ alert('Failed: ' + (data.error||'Unknown')); return; }
    location.reload();
  }catch(err){ alert(err.message||'Network error'); }
}

async function regenCodes(user_id){
  if(!confirm('Regenerate backup codes for this user? Old codes will be invalidated.')) return;
  try{
    const resp = await fetch('../../models/security/2fa_actions.php', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify({action:'regen_codes', user_id})
    });
    const txt = await resp.text();
    let data; try{ data = JSON.parse(txt); }catch(e){ throw new Error('Bad JSON: '+txt.substring(0,160)); }
    if(!data.ok){ alert('Failed: ' + (data.error||'Unknown')); return; }
    const box = document.getElementById('codesBox');
    box.innerHTML = (data.codes||[]).map(c => `<div>${c}</div>`).join('');
    bootstrap.Modal.getOrCreateInstance(document.getElementById('codesModal')).show();
  }catch(err){ alert(err.message||'Network error'); }
}
</script>

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

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


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