Fix jwt-token

This commit is contained in:
2025-09-13 22:30:56 +02:00
parent e4d557192d
commit 9a25bb26b1
2 changed files with 174 additions and 20 deletions

View File

@@ -10,7 +10,9 @@ import {
GlobeAltIcon, GlobeAltIcon,
KeyIcon, KeyIcon,
EyeIcon, EyeIcon,
EyeSlashIcon EyeSlashIcon,
TrashIcon,
PencilIcon
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { hasPermission, canAccessSettings } from '../utils/rbac'; import { hasPermission, canAccessSettings } from '../utils/rbac';
@@ -1099,6 +1101,8 @@ const UsersSettings = ({ tenantConfig, onRefresh }) => {
const [showCreateUser, setShowCreateUser] = useState(false); const [showCreateUser, setShowCreateUser] = useState(false);
const [showEditUser, setShowEditUser] = useState(false); const [showEditUser, setShowEditUser] = useState(false);
const [editingUser, setEditingUser] = useState(null); const [editingUser, setEditingUser] = useState(null);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [deletingUser, setDeletingUser] = useState(null);
const authProvider = tenantConfig?.auth_provider; const authProvider = tenantConfig?.auth_provider;
const isLocalAuth = authProvider === 'local'; const isLocalAuth = authProvider === 'local';
@@ -1260,25 +1264,41 @@ const UsersSettings = ({ tenantConfig, onRefresh }) => {
</td> </td>
{(canEditUsers || canDeleteUsers) && ( {(canEditUsers || canDeleteUsers) && (
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium"> <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
{canEditUsers && ( <div className="flex items-center space-x-2">
<button {canEditUsers && (
onClick={() => handleEditUser(user)} <button
className="text-primary-600 hover:text-primary-900 mr-4" onClick={() => handleEditUser(user)}
> className="inline-flex items-center px-2 py-1 border border-transparent text-xs font-medium rounded text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
Edit title="Edit user"
</button> >
)} <PencilIcon className="h-3 w-3 mr-1" />
{canDeleteUsers && ( Edit
<button </button>
onClick={() => handleToggleUserStatus(user)} )}
className={user.is_active {canDeleteUsers && (
? 'text-red-600 hover:text-red-900' <button
: 'text-green-600 hover:text-green-900' onClick={() => handleToggleUserStatus(user)}
} className={`inline-flex items-center px-2 py-1 border border-transparent text-xs font-medium rounded focus:outline-none focus:ring-2 focus:ring-offset-2 ${
> user.is_active
{user.is_active ? 'Deactivate' : 'Activate'} ? 'text-orange-700 bg-orange-100 hover:bg-orange-200 focus:ring-orange-500'
</button> : 'text-green-700 bg-green-100 hover:bg-green-200 focus:ring-green-500'
)} }`}
title={user.is_active ? 'Deactivate user' : 'Activate user'}
>
{user.is_active ? 'Deactivate' : 'Activate'}
</button>
)}
{canDeleteUsers && (
<button
onClick={() => handleDeleteUser(user)}
className="inline-flex items-center px-2 py-1 border border-transparent text-xs font-medium rounded text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
title="Delete user permanently"
>
<TrashIcon className="h-3 w-3 mr-1" />
Delete
</button>
)}
</div>
</td> </td>
)} )}
</tr> </tr>
@@ -1349,6 +1369,43 @@ const UsersSettings = ({ tenantConfig, onRefresh }) => {
}} }}
/> />
)} )}
{/* Delete Confirmation Modal */}
{showDeleteConfirm && deletingUser && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div className="mt-3 text-center">
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
<TrashIcon className="h-6 w-6 text-red-600" />
</div>
<h3 className="text-lg font-medium text-gray-900 mt-4">Delete User</h3>
<div className="mt-2 px-7 py-3">
<p className="text-sm text-gray-500">
Are you sure you want to delete <strong>{deletingUser.username}</strong>?
This action cannot be undone and will permanently remove the user and all associated data.
</p>
</div>
<div className="flex justify-center space-x-4 mt-4">
<button
onClick={() => {
setShowDeleteConfirm(false);
setDeletingUser(null);
}}
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500"
>
Cancel
</button>
<button
onClick={confirmDeleteUser}
className="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500"
>
Delete User
</button>
</div>
</div>
</div>
</div>
)}
</div> </div>
); );
@@ -1369,6 +1426,25 @@ const UsersSettings = ({ tenantConfig, onRefresh }) => {
toast.error('Failed to update user status'); toast.error('Failed to update user status');
} }
}; };
const handleDeleteUser = (user) => {
setDeletingUser(user);
setShowDeleteConfirm(true);
};
const confirmDeleteUser = async () => {
if (!deletingUser) return;
try {
await api.delete(`/tenant/users/${deletingUser.id}`);
toast.success('User deleted successfully');
fetchUsers();
setShowDeleteConfirm(false);
setDeletingUser(null);
} catch (error) {
toast.error('Failed to delete user');
}
};
}; };
// Create User Modal Component (for local auth only) // Create User Modal Component (for local auth only)

View File

@@ -673,6 +673,84 @@ router.put('/users/:userId', authenticateToken, requirePermissions(['users.edit'
} }
}); });
/**
* DELETE /tenant/users/:userId
* Delete user permanently (user admin or higher, local auth only)
*/
router.delete('/users/:userId', authenticateToken, requirePermissions(['users.delete']), async (req, res) => {
try {
// Determine tenant from request
const tenantId = await multiAuth.determineTenant(req);
if (!tenantId) {
return res.status(400).json({
success: false,
message: 'Unable to determine tenant'
});
}
const tenant = await Tenant.findOne({ where: { slug: tenantId } });
if (!tenant) {
return res.status(404).json({
success: false,
message: 'Tenant not found'
});
}
// Check if tenant uses local authentication
if (tenant.auth_provider !== 'local') {
return res.status(400).json({
success: false,
message: `User management is only available for local authentication. This tenant uses ${tenant.auth_provider}.`
});
}
const { userId } = req.params;
// Find user in this tenant
const user = await User.findOne({
where: {
id: userId,
tenant_id: tenant.id
}
});
if (!user) {
return res.status(404).json({
success: false,
message: 'User not found in this tenant'
});
}
// Prevent self-deletion
if (user.id === req.user.id) {
return res.status(400).json({
success: false,
message: 'Cannot delete your own user account'
});
}
// Store username for logging before deletion
const deletedUsername = user.username;
// Delete the user permanently
await user.destroy();
console.log(`⚠️ User "${deletedUsername}" permanently deleted from tenant "${tenantId}" by admin "${req.user.username}"`);
res.json({
success: true,
message: 'User deleted successfully'
});
} catch (error) {
console.error('Error deleting user:', error);
res.status(500).json({
success: false,
message: 'Failed to delete user'
});
}
});
/** /**
* GET /tenant/auth * GET /tenant/auth
* Get authentication configuration (auth admins or higher) * Get authentication configuration (auth admins or higher)