security: Add proper authentication, RBAC, and tenant isolation

- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
This commit is contained in:
Bernt
2026-08-10 12:52:48 +00:00
parent 8921fd1467
commit 78b57273e2
141 changed files with 29192 additions and 180 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+397
View File
@@ -0,0 +1,397 @@
<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BOC Banking Test</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container { max-width: 1200px; margin: 0 auto; }
h1 { color: #1a1a1a; margin-bottom: 24px; }
h2 { color: #333; margin: 32px 0 16px; font-size: 18px; }
.tabs {
display: flex;
gap: 4px;
border-bottom: 2px solid #e0e0e0;
margin-bottom: 24px;
}
.tab {
padding: 12px 20px;
background: none;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #666;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
}
.tab.active {
color: #2563eb;
border-bottom-color: #2563eb;
background: #eff6ff;
}
.card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.bank-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.bank-card {
border-radius: 12px;
padding: 20px;
border: 1px solid #e0e0e0;
}
.bank-card.revolut { background: #eff6ff; border-color: #bfdbfe; }
.bank-card.nordea { background: #fef2f2; border-color: #fecaca; }
.bank-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.bank-icon {
width: 40px;
height: 40px;
border-radius: 10px;
background: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.bank-name { font-weight: 600; color: #1a1a1a; }
.bank-number { font-size: 13px; color: #666; }
.balance {
font-size: 28px;
font-weight: 700;
color: #1a1a1a;
margin-top: 8px;
}
.balance-label {
font-size: 13px;
color: #666;
}
.transaction {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: white;
border-radius: 8px;
margin-bottom: 8px;
border: 1px solid #f0f0f0;
}
.transaction-info { display: flex; align-items: center; gap: 12px; }
.transaction-icon {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.credit { background: #dcfce7; color: #166534; }
.debit { background: #fee2e2; color: #991b1b; }
.transaction-desc { font-weight: 500; color: #1a1a1a; font-size: 14px; }
.transaction-meta { font-size: 12px; color: #666; margin-top: 2px; }
.transaction-amount { font-weight: 600; font-size: 14px; }
.amount-credit { color: #166534; }
.amount-debit { color: #991b1b; }
.btn {
padding: 8px 16px;
border-radius: 8px;
border: none;
font-size: 13px;
font-weight: 500;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
}
.btn-primary { background: #2563eb; color: white; }
.btn-secondary { background: #f3f4f6; color: #374151; }
.upload-area {
border: 2px dashed #d1d5db;
border-radius: 12px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.upload-area:hover {
border-color: #2563eb;
background: #eff6ff;
}
.status-ok { color: #166534; }
.status-warn { color: #92400e; }
.hidden { display: none; }
</style>
</head>
<body>
<div class="container">
<h1>🏦 Accounting - Bankkoppling</h1>
<div class="tabs">
<button class="tab active" onclick="showTab('banks')">🏦 Bankkonton</button>
<button class="tab" onclick="showTab('transactions')">💸 Transaktioner</button>
<button class="tab" onclick="showTab('ledger')">📖 Huvudbok</button>
<button class="tab" onclick="showTab('accounts')">📋 Konton</button>
</div>
<!-- Bankkonton -->
<div id="banks" class="tab-content">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2>Dina bankkonton</h2>
<button class="btn btn-primary" onclick="document.getElementById('upload').classList.toggle('hidden')">
📤 Ladda upp kontoutdrag
</button>
</div>
<div id="upload" class="card hidden">
<h3 style="margin-bottom:16px;">Ladda upp kontoutdrag</h3>
<div class="upload-area" onclick="alert('Filuppladdning skulle öppnas här')">
<div style="font-size:32px; margin-bottom:12px;">📁</div>
<p>Dra och släpp fil här, eller <strong>klicka för att välja</strong></p>
<p style="font-size:12px; color:#666; margin-top:8px;">Stödjer CSV, PDF, XLSX</p>
</div>
</div>
<div class="bank-grid">
<div class="bank-card revolut">
<div class="bank-header">
<div class="bank-icon">🔵</div>
<div>
<div class="bank-name">Revolut Business</div>
<div class="bank-number">1234 5678 9012 3456</div>
<div style="font-size:11px; color:#666;">IBAN: GB29 NWBK 6016 1331 9268 19</div>
</div>
</div>
<div class="balance-label">Saldo</div>
<div class="balance">125 430,50 kr</div>
<div style="margin-top:12px; display:flex; gap:8px;">
<button class="btn btn-secondary" onclick="alert('Kopplar Revolut API...')">🔗 Koppla API</button>
</div>
</div>
<div class="bank-card nordea">
<div class="bank-header">
<div class="bank-icon">🔴</div>
<div>
<div class="bank-name">Nordea Företagskonto</div>
<div class="bank-number">3456 7890 1234 5678</div>
<div style="font-size:11px; color:#666;">IBAN: SE45 5000 0000 0583 9825 7466</div>
</div>
</div>
<div class="balance-label">Saldo</div>
<div class="balance">89 200,00 kr</div>
<div style="margin-top:12px; display:flex; gap:8px;">
<button class="btn btn-secondary" onclick="alert('Kopplar Nordea API...')">🔗 Koppla API</button>
</div>
</div>
<div class="bank-card nordea">
<div class="bank-header">
<div class="bank-icon">🔴</div>
<div>
<div class="bank-name">Nordea Sparkonto</div>
<div class="bank-number">9876 5432 1098 7654</div>
</div>
</div>
<div class="balance-label">Saldo</div>
<div class="balance">250 000,00 kr</div>
<div style="margin-top:12px; display:flex; gap:8px;">
<button class="btn btn-secondary" onclick="alert('Kopplar Nordea API...')">🔗 Koppla API</button>
</div>
</div>
</div>
<h2 style="margin-top:32px;">📄 Importerade kontoutdrag</h2>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon credit">📄</div>
<div>
<div class="transaction-desc">revolut_2026-07.csv</div>
<div class="transaction-meta">2026-07-01 — 2026-07-31 • 45 transaktioner</div>
</div>
</div>
<span class="status-ok">✅ Klart</span>
</div>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon credit">📄</div>
<div>
<div class="transaction-desc">nordea_juli_2026.pdf</div>
<div class="transaction-meta">2026-07-01 — 2026-07-31 • 32 transaktioner</div>
</div>
</div>
<span class="status-ok">✅ Klart</span>
</div>
</div>
<!-- Transaktioner -->
<div id="transactions" class="tab-content hidden">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2>Senaste transaktioner</h2>
<select style="padding:8px 12px; border-radius:8px; border:1px solid #e0e0e0;">
<option>Alla konton</option>
<option>Revolut Business</option>
<option>Nordea Företagskonto</option>
<option>Nordea Sparkonto</option>
</select>
</div>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon debit">↗️</div>
<div>
<div class="transaction-desc">Atlas Capture AB - Månadsavgift</div>
<div class="transaction-meta">2026-08-08 • Atlas Capture AB • Programvara</div>
</div>
</div>
<div class="transaction-amount amount-debit">-299,00 kr</div>
</div>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon credit">↙️</div>
<div>
<div class="transaction-desc">Kundbetalning - Faktura #1001</div>
<div class="transaction-meta">2026-08-07 • Kund AB • Försäljning</div>
</div>
</div>
<div class="transaction-amount amount-credit">+15 000,00 kr</div>
</div>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon debit">↗️</div>
<div>
<div class="transaction-desc">Lön - Johan Berglund</div>
<div class="transaction-meta">2026-08-06 • Johan Berglund • Lönekostnad</div>
</div>
</div>
<div class="transaction-amount amount-debit">-45 000,00 kr</div>
</div>
<div class="transaction">
<div class="transaction-info">
<div class="transaction-icon debit">↗️</div>
<div>
<div class="transaction-desc">Hyra kontor - Wavult Group</div>
<div class="transaction-meta">2026-08-05 • Wavult Group • Lokalhyra</div>
</div>
</div>
<div class="transaction-amount amount-debit">-8 500,00 kr</div>
</div>
</div>
<!-- Huvudbok -->
<div id="ledger" class="tab-content hidden">
<h2>Huvudbok</h2>
<div class="card">
<table style="width:100%; border-collapse:collapse;">
<thead>
<tr style="border-bottom:2px solid #e0e0e0;">
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Ver.nr</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Beskrivning</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Datum</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Period</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Status</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid #f0f0f0;">
<td style="padding:12px; font-weight:500;">1</td>
<td style="padding:12px;">Inbetalning från kund</td>
<td style="padding:12px; color:#666;">2026-08-01</td>
<td style="padding:12px; color:#666;">2026-08</td>
<td style="padding:12px;"><span style="background:#dcfce7; color:#166534; padding:4px 8px; border-radius:4px; font-size:12px;">Bokförd</span></td>
</tr>
<tr style="border-bottom:1px solid #f0f0f0;">
<td style="padding:12px; font-weight:500;">2</td>
<td style="padding:12px;">Leverantörsfaktura</td>
<td style="padding:12px; color:#666;">2026-08-02</td>
<td style="padding:12px; color:#666;">2026-08</td>
<td style="padding:12px;"><span style="background:#dcfce7; color:#166534; padding:4px 8px; border-radius:4px; font-size:12px;">Bokförd</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Konton -->
<div id="accounts" class="tab-content hidden">
<h2>Konton</h2>
<div class="card">
<table style="width:100%; border-collapse:collapse;">
<thead>
<tr style="border-bottom:2px solid #e0e0e0;">
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Kod</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Namn</th>
<th style="text-align:left; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Typ</th>
<th style="text-align:right; padding:12px; font-size:12px; text-transform:uppercase; color:#666;">Saldo</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid #f0f0f0;">
<td style="padding:12px; font-weight:500;">1510</td>
<td style="padding:12px;">Kundfordringar</td>
<td style="padding:12px; color:#666;">Tillgång</td>
<td style="padding:12px; text-align:right; font-weight:500;">25 000,00 kr</td>
</tr>
<tr style="border-bottom:1px solid #f0f0f0;">
<td style="padding:12px; font-weight:500;">1930</td>
<td style="padding:12px;">Företagskonto</td>
<td style="padding:12px; color:#666;">Tillgång</td>
<td style="padding:12px; text-align:right; font-weight:500;">89 200,00 kr</td>
</tr>
<tr style="border-bottom:1px solid #f0f0f0;">
<td style="padding:12px; font-weight:500;">2013</td>
<td style="padding:12px;">Egna insättningar</td>
<td style="padding:12px; color:#666;">Eget kapital</td>
<td style="padding:12px; text-align:right; font-weight:500;">100 000,00 kr</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabId) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
document.querySelectorAll('.tab').forEach(el => el.classList.remove('active'));
// Show selected tab
document.getElementById(tabId).classList.remove('hidden');
event.target.classList.add('active');
}
</script>
</body>
</html>