From 75127394c76192bade28ab2caedfaf2238db5132 Mon Sep 17 00:00:00 2001 From: Kobe Date: Mon, 2 Jun 2025 11:46:42 +0200 Subject: [PATCH] fix settings page csrf --- routes/__pycache__/main.cpython-313.pyc | Bin 72779 -> 75120 bytes routes/main.py | 70 +++++- static/js/events.js | 220 +++++++++++++------ static/js/settings.js | 4 +- templates/settings/settings.html | 4 +- templates/settings/tabs/company_info.html | 4 +- templates/settings/tabs/email_templates.html | 38 ++-- templates/settings/tabs/events.html | 134 ----------- templates/settings/tabs/mails.html | 7 +- 9 files changed, 250 insertions(+), 231 deletions(-) diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 53c4394476a9334553c05ff8df58714fe8411179..ebfcdb2b2c7a5f47e9ff341ff22aa47e17dea2fc 100644 GIT binary patch delta 8143 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 delta 7126 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 start_date) + if user_id: + query = query.filter_by(user_id=user_id) + + # Get total count for pagination + total_events = query.count() + total_pages = (total_events + per_page - 1) // per_page + + # Get paginated events + events = query.order_by(Event.timestamp.desc()).paginate(page=page, per_page=per_page) + + return jsonify({ + 'events': [{ + 'id': event.id, + 'event_type': event.event_type, + 'timestamp': event.timestamp.isoformat(), + 'user': { + 'id': event.user.id, + 'username': event.user.username, + 'last_name': event.user.last_name + } if event.user else None, + 'ip_address': event.ip_address, + 'details': event.details + } for event in events.items], + 'current_page': page, + 'total_pages': total_pages + }) @main_bp.route('/api/events/') @login_required @@ -1194,7 +1254,7 @@ def init_routes(main_bp): template_id=template_id, users=users, email_templates=email_templates, - csrf_token=session.get('csrf_token')) + csrf_token=generate_csrf()) # For full page requests, render the full settings page site_settings = SiteSettings.get_settings() @@ -1215,7 +1275,7 @@ def init_routes(main_bp): users=users, email_templates=email_templates, form=company_form, - csrf_token=session.get('csrf_token')) + csrf_token=generate_csrf()) @main_bp.route('/settings/mails/') @login_required diff --git a/static/js/events.js b/static/js/events.js index 46acca6..453f527 100644 --- a/static/js/events.js +++ b/static/js/events.js @@ -1,6 +1,6 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize variables - let currentPage = 1; + let currentPage = parseInt(document.getElementById('currentPage').textContent) || 1; let totalPages = parseInt(document.getElementById('totalPages').textContent) || 1; let isFetching = false; @@ -32,107 +32,191 @@ document.addEventListener('DOMContentLoaded', function() { window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`); } + // Function to update pagination UI + function updatePaginationUI(page, total) { + currentPage = page; + totalPages = total; + currentPageSpan.textContent = currentPage; + totalPagesSpan.textContent = totalPages; + prevPageBtn.disabled = currentPage === 1; + nextPageBtn.disabled = currentPage === totalPages; + } + // Function to fetch filtered events function fetchEvents() { if (isFetching) return; isFetching = true; // Show loading state - eventsTableBody.innerHTML = 'Loading...'; + if (eventsTableBody) { + eventsTableBody.innerHTML = 'Loading...'; + } const params = new URLSearchParams({ - tab: 'events', - page: currentPage, event_type: eventTypeFilter.value, date_range: dateRangeFilter.value, user_id: userFilter.value, - ajax: 'true' + page: currentPage }); - fetch(`${window.location.pathname}?${params.toString()}`, { + const csrfToken = document.querySelector('meta[name="csrf-token"]').content; + fetch(`/api/events?${params.toString()}`, { headers: { - 'X-Requested-With': 'XMLHttpRequest' + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': csrfToken } }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } - return response.text(); + return response.json(); }) - .then(html => { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - const newTableBody = doc.getElementById('eventsTableBody'); + .then(data => { + console.log('Received events data:', data); - if (newTableBody) { - eventsTableBody.innerHTML = newTableBody.innerHTML; - - // Update pagination - const newCurrentPage = parseInt(doc.getElementById('currentPage').textContent) || 1; - const newTotalPages = parseInt(doc.getElementById('totalPages').textContent) || 1; - currentPage = newCurrentPage; - totalPages = newTotalPages; - currentPageSpan.textContent = currentPage; - totalPagesSpan.textContent = totalPages; - - // Update pagination buttons - prevPageBtn.disabled = currentPage <= 1; - nextPageBtn.disabled = currentPage >= totalPages; - - // Update URL - updateURL(); - } else { - console.error('Could not find events table in response'); - eventsTableBody.innerHTML = 'Error loading events'; + if (!eventsTableBody) { + console.error('Could not find events table body element'); + return; } + + // Update table content + let tableHtml = ''; + if (data.events && data.events.length > 0) { + data.events.forEach(event => { + tableHtml += ` + + ${new Date(event.timestamp).toLocaleString()} + + + ${formatEventType(event.event_type)} + + + ${event.user ? `${event.user.username} ${event.user.last_name}` : 'Unknown'} + + + + ${event.ip_address || '-'} + + `; + }); + } else { + tableHtml = 'No events found'; + } + + // Update the table body + eventsTableBody.innerHTML = tableHtml; + console.log('Updated table content with', data.events.length, 'events'); + + // Update pagination + updatePaginationUI(data.current_page, data.total_pages); + + // Update URL + updateURL(); }) .catch(error => { console.error('Error fetching events:', error); - eventsTableBody.innerHTML = 'Error loading events'; + if (eventsTableBody) { + eventsTableBody.innerHTML = 'Error loading events'; + } }) .finally(() => { isFetching = false; }); } + // Helper function to get badge class based on event type + function getEventBadgeClass(eventType) { + const badgeClasses = { + 'user_login': 'bg-info', + 'user_logout': 'bg-info', + 'user_create': 'bg-success', + 'user_delete': 'bg-danger', + 'user_update': 'bg-warning', + 'file_upload': 'bg-success', + 'file_delete': 'bg-danger', + 'file_download': 'bg-info', + 'file_preview': 'bg-info', + 'file_restore': 'bg-warning', + 'file_move': 'bg-warning', + 'file_rename': 'bg-warning', + 'file_star': 'bg-warning', + 'file_unstar': 'bg-warning', + 'file_delete_permanent': 'bg-danger', + 'folder_create': 'bg-success', + 'room_create': 'bg-success', + 'room_delete': 'bg-danger', + 'room_update': 'bg-warning', + 'room_open': 'bg-info', + 'room_member_add': 'bg-success', + 'room_member_remove': 'bg-danger', + 'room_member_permissions_update': 'bg-warning', + 'room_permission_update': 'bg-warning', + 'conversation_create': 'bg-success', + 'conversation_update': 'bg-warning', + 'conversation_delete': 'bg-danger', + 'conversation_open': 'bg-info', + 'message_create': 'bg-success', + 'attachment_download': 'bg-info' + }; + return badgeClasses[eventType] || 'bg-secondary'; + } + + // Helper function to format event type for display + function formatEventType(eventType) { + return eventType.split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + // Function to load event details function loadEventDetails(eventId) { console.log('Loading details for event:', eventId); - fetch(`/api/events/${eventId}`) - .then(response => { - console.log('Response status:', response.status); - return response.json(); - }) - .then(data => { - console.log('Received event data:', data); - - // Format the details for display - const formattedDetails = {}; - - // Handle details separately - if (data.details) { - if (typeof data.details === 'object') { - formattedDetails['Details'] = JSON.stringify(data.details, null, 2); - } else { - formattedDetails['Details'] = data.details; - } + const csrfToken = document.querySelector('meta[name="csrf-token"]').content; + fetch(`/api/events/${eventId}`, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': csrfToken + } + }) + .then(response => { + console.log('Response status:', response.status); + return response.json(); + }) + .then(data => { + console.log('Received event data:', data); + + // Format the details for display + const formattedDetails = {}; + + // Handle details separately + if (data.details) { + if (typeof data.details === 'object') { + formattedDetails['Details'] = JSON.stringify(data.details, null, 2); } else { - formattedDetails['Details'] = 'No additional details'; + formattedDetails['Details'] = data.details; } - - // Convert to formatted string - const detailsText = Object.entries(formattedDetails) - .map(([key, value]) => `${key}: ${value}`) - .join('\n\n'); - - console.log('Formatted details:', detailsText); - eventDetailsContent.textContent = detailsText; - }) - .catch(error => { - console.error('Error loading event details:', error); - eventDetailsContent.textContent = 'Error loading event details. Please try again.'; - }); + } else { + formattedDetails['Details'] = 'No additional details'; + } + + // Convert to formatted string + const detailsText = Object.entries(formattedDetails) + .map(([key, value]) => `${key}: ${value}`) + .join('\n\n'); + + console.log('Formatted details:', detailsText); + eventDetailsContent.textContent = detailsText; + }) + .catch(error => { + console.error('Error loading event details:', error); + eventDetailsContent.textContent = 'Error loading event details. Please try again.'; + }); } // Add event listeners for filters with debounce @@ -187,8 +271,6 @@ document.addEventListener('DOMContentLoaded', function() { userFilter.value = urlParams.get('user_id') || ''; currentPage = parseInt(urlParams.get('page')) || 1; - // Initial fetch if filters are set - if (eventTypeFilter.value || dateRangeFilter.value !== '24h' || userFilter.value) { - fetchEvents(); - } + // Initial fetch to ensure pagination is correct + fetchEvents(); }); \ No newline at end of file diff --git a/static/js/settings.js b/static/js/settings.js index a3c748a..3d1b0d3 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -341,10 +341,12 @@ document.addEventListener('DOMContentLoaded', function() { const formData = new FormData(companyInfoForm); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); - formData.append('csrf_token', csrfToken); fetch(companyInfoForm.action, { method: 'POST', + headers: { + 'X-CSRF-Token': csrfToken + }, body: formData }) .then(response => { diff --git a/templates/settings/settings.html b/templates/settings/settings.html index b2c0f09..c57a6e7 100644 --- a/templates/settings/settings.html +++ b/templates/settings/settings.html @@ -75,12 +75,12 @@
- {{ company_info_tab(site_settings, form) }} + {{ company_info_tab(site_settings, form, csrf_token) }}
- {{ email_templates_tab(email_templates) }} + {{ email_templates_tab(email_templates, csrf_token) }}
diff --git a/templates/settings/tabs/company_info.html b/templates/settings/tabs/company_info.html index c468131..dd9b609 100644 --- a/templates/settings/tabs/company_info.html +++ b/templates/settings/tabs/company_info.html @@ -1,11 +1,11 @@ -{% macro company_info_tab(site_settings, form) %} +{% macro company_info_tab(site_settings, form, csrf_token) %}
- {{ form.csrf_token }} +
diff --git a/templates/settings/tabs/email_templates.html b/templates/settings/tabs/email_templates.html index 4cfc7b5..4ccefd1 100644 --- a/templates/settings/tabs/email_templates.html +++ b/templates/settings/tabs/email_templates.html @@ -1,4 +1,4 @@ -{% macro email_templates_tab(templates) %} +{% macro email_templates_tab(templates, csrf_token) %}
@@ -33,24 +33,27 @@
-
+
@@ -241,7 +244,8 @@ document.addEventListener('DOMContentLoaded', function() { } // Handle template save - $('#saveTemplate').on('click', function() { + $('#templateForm').on('submit', function(event) { + event.preventDefault(); const templateId = $('#templateSelect').val(); const subject = $('#templateSubject').val(); const body = $('#templateBody').summernote('code'); @@ -252,7 +256,7 @@ document.addEventListener('DOMContentLoaded', function() { } // Show loading state - const saveButton = this; + const saveButton = this.querySelector('button[type="submit"]'); const originalText = saveButton.innerHTML; saveButton.disabled = true; saveButton.innerHTML = 'Saving...'; diff --git a/templates/settings/tabs/events.html b/templates/settings/tabs/events.html index e6418c1..d1f0a6c 100644 --- a/templates/settings/tabs/events.html +++ b/templates/settings/tabs/events.html @@ -196,140 +196,6 @@
- - {% endmacro %} {% block content %} diff --git a/templates/settings/tabs/mails.html b/templates/settings/tabs/mails.html index be013ae..59c816a 100644 --- a/templates/settings/tabs/mails.html +++ b/templates/settings/tabs/mails.html @@ -173,7 +173,12 @@