Fix jwt-token

This commit is contained in:
2025-09-24 05:33:09 +02:00
parent a7c8ac2fdf
commit 0cd7328748

View File

@@ -1,8 +1,5 @@
import React, { useState, useEffect, useContext } from 'react'; import React, { useState, useEffect, useContext } from 'react';
import { AuthContext } from '../contexts/AuthContext'; import { AuthContext } from '../contexts/AuthContext';
import { Card, CardHeader, CardTitle, CardContent } from '../components/ui/card';
import { Alert, AlertDescription } from '../components/ui/alert';
import { Badge } from '../components/ui/badge';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
const SecurityLogs = () => { const SecurityLogs = () => {
@@ -23,19 +20,12 @@ const SecurityLogs = () => {
}); });
useEffect(() => { useEffect(() => {
// Only allow admins to view security logs if (user && tenant) {
if (!user || user.role !== 'admin') {
setError('Access denied. Only tenant administrators can view security logs.');
setLoading(false);
return;
}
loadSecurityLogs(); loadSecurityLogs();
}, [filters, pagination.page, user]); }
}, [user, tenant, filters, pagination.page]);
const loadSecurityLogs = async () => { const loadSecurityLogs = async () => {
if (!user || user.role !== 'admin') return;
setLoading(true); setLoading(true);
try { try {
const params = new URLSearchParams({ const params = new URLSearchParams({
@@ -44,10 +34,10 @@ const SecurityLogs = () => {
...filters ...filters
}); });
const response = await fetch(`/api/security-logs?${params}`, { const response = await fetch(`/api/${tenant.slug}/security-logs?${params}`, {
headers: { headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Authorization': `Bearer ${user.token}`,
'X-Tenant-ID': tenant?.id || '' 'Content-Type': 'application/json'
} }
}); });
@@ -71,11 +61,11 @@ const SecurityLogs = () => {
const getLogLevelBadge = (level) => { const getLogLevelBadge = (level) => {
const styles = { const styles = {
'critical': 'bg-red-500 text-white', 'critical': 'bg-red-500 text-white px-2 py-1 rounded text-xs font-semibold',
'high': 'bg-orange-500 text-white', 'high': 'bg-orange-500 text-white px-2 py-1 rounded text-xs font-semibold',
'medium': 'bg-yellow-500 text-black', 'medium': 'bg-yellow-500 text-black px-2 py-1 rounded text-xs font-semibold',
'low': 'bg-blue-500 text-white', 'low': 'bg-blue-500 text-white px-2 py-1 rounded text-xs font-semibold',
'info': 'bg-gray-500 text-white' 'info': 'bg-gray-500 text-white px-2 py-1 rounded text-xs font-semibold'
}; };
return styles[level] || styles.info; return styles[level] || styles.info;
}; };
@@ -100,55 +90,48 @@ const SecurityLogs = () => {
if (metadata.ip_address) items.push(`IP: ${metadata.ip_address}`); if (metadata.ip_address) items.push(`IP: ${metadata.ip_address}`);
if (metadata.country) items.push(`Country: ${metadata.country}`); if (metadata.country) items.push(`Country: ${metadata.country}`);
if (metadata.user_agent) items.push(`Agent: ${metadata.user_agent.substring(0, 50)}...`); if (metadata.user_agent) items.push(`Agent: ${metadata.user_agent.substring(0, 50)}...`);
if (metadata.username) items.push(`User: ${metadata.username}`);
return items.join(' | '); return items.join(' | ');
}; };
// Access control check const totalPages = Math.ceil(pagination.total / pagination.limit);
if (!user || user.role !== 'admin') {
// Don't render if user is not authenticated
if (!user || !tenant) {
return ( return (
<div className="p-6"> <div className="p-6">
<Alert className="border-red-200 bg-red-50"> <div className="text-center py-8 text-gray-500">
<AlertDescription className="text-red-800"> Please log in to view security logs
Access denied. Only tenant administrators can view security logs. </div>
</AlertDescription>
</Alert>
</div> </div>
); );
} }
const totalPages = Math.ceil(pagination.total / pagination.limit);
return ( return (
<div className="p-6"> <div className="p-6">
<div className="mb-6"> <div className="mb-6">
<h1 className="text-3xl font-bold mb-2">Security Logs</h1> <h1 className="text-3xl font-bold mb-2 text-gray-900">Security Logs</h1>
<p className="text-gray-600"> <p className="text-gray-600">Monitor security events for your tenant: {tenant.name}</p>
Security events for {tenant?.name || 'your organization'}
</p>
</div> </div>
{error && ( {error && (
<Alert className="mb-6 border-red-200 bg-red-50"> <div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-md">
<AlertDescription className="text-red-800"> <div className="text-red-800">{error}</div>
{error} </div>
</AlertDescription>
</Alert>
)} )}
{/* Filters */} {/* Filters */}
<Card className="mb-6"> <div className="bg-white rounded-lg shadow mb-6">
<CardHeader> <div className="px-6 py-4 border-b border-gray-200">
<CardTitle>Filters</CardTitle> <h3 className="text-lg font-medium text-gray-900">Filters</h3>
</CardHeader> </div>
<CardContent> <div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div> <div>
<label className="block text-sm font-medium mb-2">Security Level</label> <label className="block text-sm font-medium text-gray-700 mb-2">Security Level</label>
<select <select
value={filters.level} value={filters.level}
onChange={(e) => setFilters(prev => ({ ...prev, level: e.target.value }))} onChange={(e) => setFilters(prev => ({ ...prev, level: e.target.value }))}
className="w-full p-2 border rounded-md" className="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
> >
<option value="all">All Levels</option> <option value="all">All Levels</option>
<option value="critical">Critical</option> <option value="critical">Critical</option>
@@ -159,11 +142,11 @@ const SecurityLogs = () => {
</select> </select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-2">Event Type</label> <label className="block text-sm font-medium text-gray-700 mb-2">Event Type</label>
<select <select
value={filters.eventType} value={filters.eventType}
onChange={(e) => setFilters(prev => ({ ...prev, eventType: e.target.value }))} onChange={(e) => setFilters(prev => ({ ...prev, eventType: e.target.value }))}
className="w-full p-2 border rounded-md" className="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
> >
<option value="all">All Events</option> <option value="all">All Events</option>
<option value="failed_login">Failed Logins</option> <option value="failed_login">Failed Logins</option>
@@ -175,11 +158,11 @@ const SecurityLogs = () => {
</select> </select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-2">Time Range</label> <label className="block text-sm font-medium text-gray-700 mb-2">Time Range</label>
<select <select
value={filters.timeRange} value={filters.timeRange}
onChange={(e) => setFilters(prev => ({ ...prev, timeRange: e.target.value }))} onChange={(e) => setFilters(prev => ({ ...prev, timeRange: e.target.value }))}
className="w-full p-2 border rounded-md" className="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
> >
<option value="1h">Last Hour</option> <option value="1h">Last Hour</option>
<option value="24h">Last 24 Hours</option> <option value="24h">Last 24 Hours</option>
@@ -189,30 +172,30 @@ const SecurityLogs = () => {
</select> </select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-2">Search</label> <label className="block text-sm font-medium text-gray-700 mb-2">Search</label>
<input <input
type="text" type="text"
placeholder="IP, username..." placeholder="IP, username..."
value={filters.search} value={filters.search}
onChange={(e) => setFilters(prev => ({ ...prev, search: e.target.value }))} onChange={(e) => setFilters(prev => ({ ...prev, search: e.target.value }))}
className="w-full p-2 border rounded-md" className="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
/> />
</div> </div>
</div> </div>
</CardContent> </div>
</Card> </div>
{/* Security Logs Table */} {/* Security Logs Table */}
<Card> <div className="bg-white rounded-lg shadow">
<CardHeader> <div className="px-6 py-4 border-b border-gray-200">
<CardTitle className="flex justify-between items-center"> <div className="flex justify-between items-center">
Security Events <h3 className="text-lg font-medium text-gray-900">Security Events</h3>
<span className="text-sm font-normal text-gray-500"> <span className="text-sm text-gray-500">
{pagination.total} total events {pagination.total} total events
</span> </span>
</CardTitle> </div>
</CardHeader> </div>
<CardContent> <div className="p-6">
{loading ? ( {loading ? (
<div className="flex justify-center py-8"> <div className="flex justify-center py-8">
<div className="text-gray-500">Loading security logs...</div> <div className="text-gray-500">Loading security logs...</div>
@@ -225,40 +208,40 @@ const SecurityLogs = () => {
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full"> <table className="w-full">
<thead> <thead>
<tr className="border-b"> <tr className="border-b border-gray-200">
<th className="text-left p-2">Time</th> <th className="text-left p-3 text-sm font-medium text-gray-500 uppercase tracking-wider">Time</th>
<th className="text-left p-2">Level</th> <th className="text-left p-3 text-sm font-medium text-gray-500 uppercase tracking-wider">Level</th>
<th className="text-left p-2">Event</th> <th className="text-left p-3 text-sm font-medium text-gray-500 uppercase tracking-wider">Event</th>
<th className="text-left p-2">Message</th> <th className="text-left p-3 text-sm font-medium text-gray-500 uppercase tracking-wider">Message</th>
<th className="text-left p-2">Details</th> <th className="text-left p-3 text-sm font-medium text-gray-500 uppercase tracking-wider">Details</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{logs.map((log) => ( {logs.map((log) => (
<tr key={log.id} className="border-b hover:bg-gray-50"> <tr key={log.id} className="border-b border-gray-100 hover:bg-gray-50">
<td className="p-2 text-sm"> <td className="p-3 text-sm">
<div>{new Date(log.timestamp).toLocaleString()}</div> <div>{new Date(log.timestamp).toLocaleString()}</div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{formatDistanceToNow(new Date(log.timestamp), { addSuffix: true })} {formatDistanceToNow(new Date(log.timestamp), { addSuffix: true })}
</div> </div>
</td> </td>
<td className="p-2"> <td className="p-3">
<Badge className={getLogLevelBadge(log.level)}> <span className={getLogLevelBadge(log.level)}>
{log.level.toUpperCase()} {log.level.toUpperCase()}
</Badge> </span>
</td> </td>
<td className="p-2"> <td className="p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span>{getEventTypeIcon(log.event_type)}</span> <span>{getEventTypeIcon(log.event_type)}</span>
<span className="text-sm">{log.event_type.replace('_', ' ').toUpperCase()}</span> <span className="text-sm">{log.event_type.replace('_', ' ').toUpperCase()}</span>
</div> </div>
</td> </td>
<td className="p-2 text-sm max-w-md"> <td className="p-3 text-sm max-w-md">
<div className="truncate" title={log.message}> <div className="truncate" title={log.message}>
{log.message} {log.message}
</div> </div>
</td> </td>
<td className="p-2 text-xs text-gray-600 max-w-md"> <td className="p-3 text-xs text-gray-600 max-w-md">
<div className="truncate" title={formatMetadata(log.metadata)}> <div className="truncate" title={formatMetadata(log.metadata)}>
{formatMetadata(log.metadata)} {formatMetadata(log.metadata)}
</div> </div>
@@ -280,22 +263,22 @@ const SecurityLogs = () => {
<button <button
onClick={() => setPagination(prev => ({ ...prev, page: Math.max(1, prev.page - 1) }))} onClick={() => setPagination(prev => ({ ...prev, page: Math.max(1, prev.page - 1) }))}
disabled={pagination.page === 1} disabled={pagination.page === 1}
className="px-3 py-1 text-sm border rounded disabled:opacity-50" className="px-3 py-1 text-sm border border-gray-300 rounded disabled:opacity-50 hover:bg-gray-50"
> >
Previous Previous
</button> </button>
<button <button
onClick={() => setPagination(prev => ({ ...prev, page: Math.min(totalPages, prev.page + 1) }))} onClick={() => setPagination(prev => ({ ...prev, page: Math.min(totalPages, prev.page + 1) }))}
disabled={pagination.page === totalPages} disabled={pagination.page === totalPages}
className="px-3 py-1 text-sm border rounded disabled:opacity-50" className="px-3 py-1 text-sm border border-gray-300 rounded disabled:opacity-50 hover:bg-gray-50"
> >
Next Next
</button> </button>
</div> </div>
</div> </div>
)} )}
</CardContent> </div>
</Card> </div>
</div> </div>
); );
}; };