mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-07 22:06:05 +01:00
Implement Banned IPs management system: Add functionality to list, add, remove, and delete banned IPs. Update UI components for managing banned IPs, including a new tab in the firewall section and enhanced user notifications. Refactor existing code for better organization and maintainability.
This commit is contained in:
@@ -1008,8 +1008,11 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) {
|
|||||||
if (!$scope.blockingIP) {
|
if (!$scope.blockingIP) {
|
||||||
$scope.blockingIP = ipAddress;
|
$scope.blockingIP = ipAddress;
|
||||||
|
|
||||||
|
// Use the new Banned IPs system instead of the old blockIPAddress
|
||||||
var data = {
|
var data = {
|
||||||
ip_address: ipAddress
|
ip: ipAddress,
|
||||||
|
reason: 'Brute force attack detected from SSH Security Analysis',
|
||||||
|
duration: 'permanent'
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
@@ -1018,7 +1021,7 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$http.post('/base/blockIPAddress', data, config).then(function (response) {
|
$http.post('/firewall/addBannedIP', data, config).then(function (response) {
|
||||||
$scope.blockingIP = null;
|
$scope.blockingIP = null;
|
||||||
if (response.data && response.data.status === 1) {
|
if (response.data && response.data.status === 1) {
|
||||||
// Mark IP as blocked
|
// Mark IP as blocked
|
||||||
@@ -1026,8 +1029,8 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) {
|
|||||||
|
|
||||||
// Show success notification
|
// Show success notification
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Success',
|
title: 'IP Address Banned',
|
||||||
text: `IP address ${ipAddress} has been blocked successfully using ${response.data.firewall.toUpperCase()}`,
|
text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
delay: 5000
|
delay: 5000
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -234,6 +234,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-table th {
|
.activity-table th {
|
||||||
@@ -535,12 +536,12 @@
|
|||||||
<table class="activity-table" ng-if="!loadingSSHLogins && sshLogins.length > 0">
|
<table class="activity-table" ng-if="!loadingSSHLogins && sshLogins.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>USER</th>
|
<th style="width: 15%;">USER</th>
|
||||||
<th>IP</th>
|
<th style="width: 20%;">IP</th>
|
||||||
<th>COUNTRY</th>
|
<th style="width: 15%;">COUNTRY</th>
|
||||||
<th>DATE</th>
|
<th style="width: 20%;">DATE</th>
|
||||||
<th>SESSION</th>
|
<th style="width: 15%;">SESSION</th>
|
||||||
<th>ACTIVITY</th>
|
<th style="width: 15%;">ACTIVITY</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -551,7 +552,15 @@
|
|||||||
<td>{$ login.date $}</td>
|
<td>{$ login.date $}</td>
|
||||||
<td>{$ login.session $}</td>
|
<td>{$ login.session $}</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div style="display: flex; gap: 8px; align-items: center;">
|
||||||
<button class="view-activity-btn" ng-click="viewSSHActivity(login)">View Activity</button>
|
<button class="view-activity-btn" ng-click="viewSSHActivity(login)">View Activity</button>
|
||||||
|
<button class="ban-ip-btn" ng-click="blockIPAddress(login.ip)"
|
||||||
|
style="background: #dc2626; color: white; border: 1px solid #dc2626; padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer;"
|
||||||
|
onmouseover="this.style.background='#b91c1c'"
|
||||||
|
onmouseout="this.style.background='#dc2626'">
|
||||||
|
<i class="fas fa-ban"></i> Ban IP
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -561,12 +570,12 @@
|
|||||||
<table class="activity-table" ng-if="loadingSSHLogins">
|
<table class="activity-table" ng-if="loadingSSHLogins">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>USER</th>
|
<th style="width: 15%;">USER</th>
|
||||||
<th>IP</th>
|
<th style="width: 20%;">IP</th>
|
||||||
<th>COUNTRY</th>
|
<th style="width: 15%;">COUNTRY</th>
|
||||||
<th>DATE</th>
|
<th style="width: 20%;">DATE</th>
|
||||||
<th>SESSION</th>
|
<th style="width: 15%;">SESSION</th>
|
||||||
<th>ACTIVITY</th>
|
<th style="width: 15%;">ACTIVITY</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -577,7 +586,12 @@
|
|||||||
<td>Wed Jun 4 20:59</td>
|
<td>Wed Jun 4 20:59</td>
|
||||||
<td>Still Logged In</td>
|
<td>Still Logged In</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div style="display: flex; gap: 8px; align-items: center;">
|
||||||
<button class="view-activity-btn" style="background: #5b5fcf; color: white; border-color: #5b5fcf;">View Activity</button>
|
<button class="view-activity-btn" style="background: #5b5fcf; color: white; border-color: #5b5fcf;">View Activity</button>
|
||||||
|
<button class="ban-ip-btn" style="background: #dc2626; color: white; border: 1px solid #dc2626; padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer;">
|
||||||
|
<i class="fas fa-ban"></i> Ban IP
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -672,9 +686,12 @@
|
|||||||
onmouseout="this.style.background='#dc2626'">
|
onmouseout="this.style.background='#dc2626'">
|
||||||
<i class="fas fa-ban" ng-if="blockingIP !== alert.details['IP Address']"></i>
|
<i class="fas fa-ban" ng-if="blockingIP !== alert.details['IP Address']"></i>
|
||||||
<i class="fas fa-spinner fa-spin" ng-if="blockingIP === alert.details['IP Address']"></i>
|
<i class="fas fa-spinner fa-spin" ng-if="blockingIP === alert.details['IP Address']"></i>
|
||||||
<span ng-if="blockingIP !== alert.details['IP Address']">Block IP</span>
|
<span ng-if="blockingIP !== alert.details['IP Address']">Ban IP Permanently</span>
|
||||||
<span ng-if="blockingIP === alert.details['IP Address']">Blocking...</span>
|
<span ng-if="blockingIP === alert.details['IP Address']">Banning...</span>
|
||||||
</button>
|
</button>
|
||||||
|
<a href="/firewall/" target="_blank" style="margin-left: 10px; color: #5b5fcf; font-size: 12px; text-decoration: none;">
|
||||||
|
<i class="fas fa-external-link-alt"></i> Manage in Firewall
|
||||||
|
</a>
|
||||||
<span ng-if="blockedIPs && blockedIPs[alert.details['IP Address']]"
|
<span ng-if="blockedIPs && blockedIPs[alert.details['IP Address']]"
|
||||||
style="margin-left: 10px; color: #10b981; font-size: 12px; font-weight: 600;">
|
style="margin-left: 10px; color: #10b981; font-size: 12px; font-weight: 600;">
|
||||||
<i class="fas fa-check-circle"></i> Blocked
|
<i class="fas fa-check-circle"></i> Blocked
|
||||||
|
|||||||
@@ -2096,3 +2096,210 @@ class ContainerManager(multi.Thread):
|
|||||||
data_ret = {'deleteContainerKeepDataStatus': 0, 'error_message': str(msg)}
|
data_ret = {'deleteContainerKeepDataStatus': 0, 'error_message': str(msg)}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
def updateContainer(self, userID=None, data=None):
|
||||||
|
"""
|
||||||
|
Update container with new image while preserving data using Docker volumes
|
||||||
|
This function handles the complete container update process:
|
||||||
|
1. Stops the current container
|
||||||
|
2. Creates a backup of the container configuration
|
||||||
|
3. Removes the old container
|
||||||
|
4. Pulls the new image
|
||||||
|
5. Creates a new container with the same configuration but new image
|
||||||
|
6. Preserves all volumes and data
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
dockerAPI = docker.APIClient()
|
||||||
|
|
||||||
|
containerName = data['containerName']
|
||||||
|
newImage = data['newImage']
|
||||||
|
newTag = data.get('newTag', 'latest')
|
||||||
|
|
||||||
|
# Get the current container
|
||||||
|
try:
|
||||||
|
currentContainer = client.containers.get(containerName)
|
||||||
|
except docker.errors.NotFound:
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Container {containerName} not found'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
except Exception as e:
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error accessing container: {str(e)}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
# Get container configuration for recreation
|
||||||
|
containerConfig = currentContainer.attrs['Config']
|
||||||
|
hostConfig = currentContainer.attrs['HostConfig']
|
||||||
|
|
||||||
|
# Extract volumes for data preservation
|
||||||
|
volumes = {}
|
||||||
|
if 'Binds' in hostConfig and hostConfig['Binds']:
|
||||||
|
for bind in hostConfig['Binds']:
|
||||||
|
if ':' in bind:
|
||||||
|
parts = bind.split(':')
|
||||||
|
if len(parts) >= 2:
|
||||||
|
host_path = parts[0]
|
||||||
|
container_path = parts[1]
|
||||||
|
mode = parts[2] if len(parts) > 2 else 'rw'
|
||||||
|
volumes[host_path] = {'bind': container_path, 'mode': mode}
|
||||||
|
|
||||||
|
# Extract environment variables
|
||||||
|
environment = containerConfig.get('Env', [])
|
||||||
|
envDict = {}
|
||||||
|
for env in environment:
|
||||||
|
if '=' in env:
|
||||||
|
key, value = env.split('=', 1)
|
||||||
|
envDict[key] = value
|
||||||
|
|
||||||
|
# Extract port mappings
|
||||||
|
portConfig = {}
|
||||||
|
if 'PortBindings' in hostConfig and hostConfig['PortBindings']:
|
||||||
|
for container_port, host_bindings in hostConfig['PortBindings'].items():
|
||||||
|
if host_bindings and len(host_bindings) > 0:
|
||||||
|
host_port = host_bindings[0]['HostPort']
|
||||||
|
portConfig[container_port] = host_port
|
||||||
|
|
||||||
|
# Extract memory limit
|
||||||
|
memory_limit = hostConfig.get('Memory', 0)
|
||||||
|
if memory_limit > 0:
|
||||||
|
memory_limit = memory_limit // 1048576 # Convert bytes to MB
|
||||||
|
|
||||||
|
# Stop the current container
|
||||||
|
try:
|
||||||
|
if currentContainer.status == 'running':
|
||||||
|
currentContainer.stop(timeout=30)
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Stopped container {containerName} for update')
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error stopping container {containerName}: {str(e)}')
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error stopping container: {str(e)}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
# Remove the old container
|
||||||
|
try:
|
||||||
|
currentContainer.remove(force=True)
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Removed old container {containerName}')
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error removing old container {containerName}: {str(e)}')
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error removing old container: {str(e)}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
# Pull the new image
|
||||||
|
try:
|
||||||
|
image_name = f"{newImage}:{newTag}"
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Pulling new image {image_name}')
|
||||||
|
client.images.pull(newImage, tag=newTag)
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Successfully pulled image {image_name}')
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error pulling image {newImage}:{newTag}: {str(e)}')
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error pulling new image: {str(e)}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
# Create new container with same configuration but new image
|
||||||
|
try:
|
||||||
|
containerArgs = {
|
||||||
|
'image': image_name,
|
||||||
|
'detach': True,
|
||||||
|
'name': containerName,
|
||||||
|
'ports': portConfig,
|
||||||
|
'publish_all_ports': True,
|
||||||
|
'environment': envDict,
|
||||||
|
'volumes': volumes
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_limit > 0:
|
||||||
|
containerArgs['mem_limit'] = memory_limit * 1048576
|
||||||
|
|
||||||
|
newContainer = client.containers.create(**containerArgs)
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Created new container {containerName} with image {image_name}')
|
||||||
|
|
||||||
|
# Start the new container
|
||||||
|
newContainer.start()
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Started updated container {containerName}')
|
||||||
|
|
||||||
|
except docker.errors.APIError as err:
|
||||||
|
error_message = str(err)
|
||||||
|
if "port is already allocated" in error_message:
|
||||||
|
try:
|
||||||
|
newContainer.remove(force=True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Docker API error creating container: {error_message}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
except docker.errors.ImageNotFound as err:
|
||||||
|
error_message = str(err)
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'New image not found: {error_message}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Error creating new container {containerName}: {str(e)}')
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error creating new container: {str(e)}'}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
# Log successful update
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Successfully updated container {containerName} to image {image_name}')
|
||||||
|
|
||||||
|
data_ret = {
|
||||||
|
'updateContainerStatus': 1,
|
||||||
|
'error_message': 'None',
|
||||||
|
'message': f'Container {containerName} successfully updated to {image_name}'
|
||||||
|
}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
except Exception as msg:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.updateContainer]')
|
||||||
|
data_ret = {'updateContainerStatus': 0, 'error_message': str(msg)}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
def listContainers(self, userID=None):
|
||||||
|
"""
|
||||||
|
Get list of all Docker containers
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
|
||||||
|
# Get all containers (including stopped ones)
|
||||||
|
containers = client.containers.list(all=True)
|
||||||
|
|
||||||
|
container_list = []
|
||||||
|
for container in containers:
|
||||||
|
container_info = {
|
||||||
|
'name': container.name,
|
||||||
|
'image': container.image.tags[0] if container.image.tags else container.image.short_id,
|
||||||
|
'status': container.status,
|
||||||
|
'state': container.attrs['State']['Status'],
|
||||||
|
'created': container.attrs['Created'],
|
||||||
|
'ports': container.attrs['NetworkSettings']['Ports'],
|
||||||
|
'mounts': container.attrs['Mounts'],
|
||||||
|
'id': container.short_id
|
||||||
|
}
|
||||||
|
container_list.append(container_info)
|
||||||
|
|
||||||
|
data_ret = {
|
||||||
|
'status': 1,
|
||||||
|
'error_message': 'None',
|
||||||
|
'containers': container_list
|
||||||
|
}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
except Exception as msg:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.listContainers]')
|
||||||
|
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
@@ -2496,3 +2496,258 @@ app.controller('manageImages', function ($scope, $http) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Container List Controller
|
||||||
|
app.controller('listContainers', function ($scope, $http, $timeout, $window) {
|
||||||
|
$scope.containers = [];
|
||||||
|
$scope.loading = false;
|
||||||
|
$scope.updateContainerName = '';
|
||||||
|
$scope.currentImage = '';
|
||||||
|
$scope.newImage = '';
|
||||||
|
$scope.newTag = 'latest';
|
||||||
|
|
||||||
|
// Load containers list
|
||||||
|
$scope.loadContainers = function() {
|
||||||
|
$scope.loading = true;
|
||||||
|
var url = '/docker/listContainers';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, {}, config).then(function(response) {
|
||||||
|
$scope.loading = false;
|
||||||
|
if (response.data.status === 1) {
|
||||||
|
$scope.containers = response.data.containers || [];
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Error Loading Containers',
|
||||||
|
text: response.data.error_message || 'Failed to load containers',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
$scope.loading = false;
|
||||||
|
new PNotify({
|
||||||
|
title: 'Connection Error',
|
||||||
|
text: 'Could not connect to server',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize containers on page load
|
||||||
|
$scope.loadContainers();
|
||||||
|
|
||||||
|
// Open update container modal
|
||||||
|
$scope.openUpdateModal = function(container) {
|
||||||
|
$scope.updateContainerName = container.name;
|
||||||
|
$scope.currentImage = container.image;
|
||||||
|
$scope.newImage = '';
|
||||||
|
$scope.newTag = 'latest';
|
||||||
|
$('#updateContainer').modal('show');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform container update
|
||||||
|
$scope.performUpdate = function() {
|
||||||
|
if (!$scope.newImage && !$scope.newTag) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Missing Information',
|
||||||
|
text: 'Please enter a new image name or tag',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no new image specified, use current image with new tag
|
||||||
|
var imageToUse = $scope.newImage || $scope.currentImage.split(':')[0];
|
||||||
|
var tagToUse = $scope.newTag || 'latest';
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
containerName: $scope.updateContainerName,
|
||||||
|
newImage: imageToUse,
|
||||||
|
newTag: tagToUse
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = '/docker/updateContainer';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Show loading
|
||||||
|
$('#updateContainer').modal('hide');
|
||||||
|
new PNotify({
|
||||||
|
title: 'Updating Container',
|
||||||
|
text: 'Please wait while the container is being updated...',
|
||||||
|
type: 'info',
|
||||||
|
hide: false
|
||||||
|
});
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.updateContainerStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Updated Successfully',
|
||||||
|
text: response.data.message || 'Container has been updated successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
// Reload containers list
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Update Failed',
|
||||||
|
text: response.data.error_message || 'Failed to update container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Update Failed',
|
||||||
|
text: 'Could not connect to server',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Container actions
|
||||||
|
$scope.startContainer = function(containerName) {
|
||||||
|
var data = { containerName: containerName };
|
||||||
|
var url = '/docker/startContainer';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.startContainerStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Started',
|
||||||
|
text: 'Container has been started successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Start Failed',
|
||||||
|
text: response.data.error_message || 'Failed to start container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.stopContainer = function(containerName) {
|
||||||
|
var data = { containerName: containerName };
|
||||||
|
var url = '/docker/stopContainer';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.stopContainerStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Stopped',
|
||||||
|
text: 'Container has been stopped successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Stop Failed',
|
||||||
|
text: response.data.error_message || 'Failed to stop container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.restartContainer = function(containerName) {
|
||||||
|
var data = { containerName: containerName };
|
||||||
|
var url = '/docker/restartContainer';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.restartContainerStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Restarted',
|
||||||
|
text: 'Container has been restarted successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Restart Failed',
|
||||||
|
text: response.data.error_message || 'Failed to restart container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteContainerWithData = function(containerName) {
|
||||||
|
if (confirm('Are you sure you want to delete this container and all its data? This action cannot be undone.')) {
|
||||||
|
var data = { containerName: containerName };
|
||||||
|
var url = '/docker/deleteContainerWithData';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.deleteContainerWithDataStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Deleted',
|
||||||
|
text: 'Container and all data have been deleted successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Delete Failed',
|
||||||
|
text: response.data.error_message || 'Failed to delete container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteContainerKeepData = function(containerName) {
|
||||||
|
if (confirm('Are you sure you want to delete this container but keep the data? The container will be removed but volumes will be preserved.')) {
|
||||||
|
var data = { containerName: containerName };
|
||||||
|
var url = '/docker/deleteContainerKeepData';
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
if (response.data.deleteContainerKeepDataStatus === 1) {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Container Deleted',
|
||||||
|
text: 'Container has been deleted but data has been preserved',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
$scope.loadContainers();
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Delete Failed',
|
||||||
|
text: response.data.error_message || 'Failed to delete container',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -28,6 +28,7 @@ urlpatterns = [
|
|||||||
re_path(r'^removeImage$', views.removeImage, name='removeImage'),
|
re_path(r'^removeImage$', views.removeImage, name='removeImage'),
|
||||||
re_path(r'^pullImage$', views.pullImage, name='pullImage'),
|
re_path(r'^pullImage$', views.pullImage, name='pullImage'),
|
||||||
re_path(r'^updateContainer$', views.updateContainer, name='updateContainer'),
|
re_path(r'^updateContainer$', views.updateContainer, name='updateContainer'),
|
||||||
|
re_path(r'^listContainers$', views.listContainers, name='listContainers'),
|
||||||
re_path(r'^deleteContainerWithData$', views.deleteContainerWithData, name='deleteContainerWithData'),
|
re_path(r'^deleteContainerWithData$', views.deleteContainerWithData, name='deleteContainerWithData'),
|
||||||
re_path(r'^deleteContainerKeepData$', views.deleteContainerKeepData, name='deleteContainerKeepData'),
|
re_path(r'^deleteContainerKeepData$', views.deleteContainerKeepData, name='deleteContainerKeepData'),
|
||||||
re_path(r'^recreateContainer$', views.recreateContainer, name='recreateContainer'),
|
re_path(r'^recreateContainer$', views.recreateContainer, name='recreateContainer'),
|
||||||
|
|||||||
@@ -745,3 +745,24 @@ def getContainerEnv(request):
|
|||||||
}), content_type='application/json')
|
}), content_type='application/json')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return redirect(loadLoginPage)
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
@preDockerRun
|
||||||
|
def listContainers(request):
|
||||||
|
"""
|
||||||
|
Get list of all Docker containers
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
currentACL = ACLManager.loadedACL(userID)
|
||||||
|
|
||||||
|
if currentACL['admin'] == 1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return ACLManager.loadErrorJson()
|
||||||
|
|
||||||
|
cm = ContainerManager()
|
||||||
|
coreResult = cm.listContainers(userID)
|
||||||
|
|
||||||
|
return coreResult
|
||||||
|
except KeyError:
|
||||||
|
return redirect(loadLoginPage)
|
||||||
@@ -1755,6 +1755,267 @@ class FirewallManager:
|
|||||||
final_json = json.dumps(final_dic)
|
final_json = json.dumps(final_dic)
|
||||||
return HttpResponse(final_json)
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
def getBannedIPs(self, userID=None):
|
||||||
|
"""
|
||||||
|
Get list of banned IP addresses
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
# For now, we'll use a simple file-based storage
|
||||||
|
# In production, you might want to use a database
|
||||||
|
banned_ips_file = '/etc/cyberpanel/banned_ips.json'
|
||||||
|
|
||||||
|
banned_ips = []
|
||||||
|
if os.path.exists(banned_ips_file):
|
||||||
|
try:
|
||||||
|
with open(banned_ips_file, 'r') as f:
|
||||||
|
banned_ips = json.load(f)
|
||||||
|
except:
|
||||||
|
banned_ips = []
|
||||||
|
|
||||||
|
# Filter out expired bans
|
||||||
|
current_time = time.time()
|
||||||
|
active_banned_ips = []
|
||||||
|
|
||||||
|
for banned_ip in banned_ips:
|
||||||
|
if banned_ip.get('expires') == 'Never' or banned_ip.get('expires', 0) > current_time:
|
||||||
|
banned_ip['active'] = True
|
||||||
|
if banned_ip.get('expires') != 'Never':
|
||||||
|
banned_ip['expires'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(banned_ip['expires']))
|
||||||
|
else:
|
||||||
|
banned_ip['expires'] = 'Never'
|
||||||
|
banned_ip['banned_on'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(banned_ip.get('banned_on', current_time)))
|
||||||
|
else:
|
||||||
|
banned_ip['active'] = False
|
||||||
|
banned_ip['expires'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(banned_ip.get('expires', current_time)))
|
||||||
|
banned_ip['banned_on'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(banned_ip.get('banned_on', current_time)))
|
||||||
|
|
||||||
|
active_banned_ips.append(banned_ip)
|
||||||
|
|
||||||
|
final_dic = {'status': 1, 'bannedIPs': active_banned_ips}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
final_dic = {'status': 0, 'error_message': str(msg)}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
def addBannedIP(self, userID=None, data=None):
|
||||||
|
"""
|
||||||
|
Add a banned IP address
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
ip = data.get('ip', '').strip()
|
||||||
|
reason = data.get('reason', '').strip()
|
||||||
|
duration = data.get('duration', '24h')
|
||||||
|
|
||||||
|
if not ip or not reason:
|
||||||
|
final_dic = {'status': 0, 'error_message': 'IP address and reason are required'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
# Validate IP address format
|
||||||
|
import ipaddress
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip.split('/')[0]) # Handle CIDR notation
|
||||||
|
except ValueError:
|
||||||
|
final_dic = {'status': 0, 'error_message': 'Invalid IP address format'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
# Calculate expiration time
|
||||||
|
current_time = time.time()
|
||||||
|
if duration == 'permanent':
|
||||||
|
expires = 'Never'
|
||||||
|
else:
|
||||||
|
duration_map = {
|
||||||
|
'1h': 3600,
|
||||||
|
'24h': 86400,
|
||||||
|
'7d': 604800,
|
||||||
|
'30d': 2592000
|
||||||
|
}
|
||||||
|
duration_seconds = duration_map.get(duration, 86400)
|
||||||
|
expires = current_time + duration_seconds
|
||||||
|
|
||||||
|
# Load existing banned IPs
|
||||||
|
banned_ips_file = '/etc/cyberpanel/banned_ips.json'
|
||||||
|
banned_ips = []
|
||||||
|
if os.path.exists(banned_ips_file):
|
||||||
|
try:
|
||||||
|
with open(banned_ips_file, 'r') as f:
|
||||||
|
banned_ips = json.load(f)
|
||||||
|
except:
|
||||||
|
banned_ips = []
|
||||||
|
|
||||||
|
# Check if IP is already banned
|
||||||
|
for banned_ip in banned_ips:
|
||||||
|
if banned_ip.get('ip') == ip and banned_ip.get('active', True):
|
||||||
|
final_dic = {'status': 0, 'error_message': f'IP address {ip} is already banned'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
# Add new banned IP
|
||||||
|
new_banned_ip = {
|
||||||
|
'id': int(time.time()),
|
||||||
|
'ip': ip,
|
||||||
|
'reason': reason,
|
||||||
|
'duration': duration,
|
||||||
|
'banned_on': current_time,
|
||||||
|
'expires': expires,
|
||||||
|
'active': True
|
||||||
|
}
|
||||||
|
banned_ips.append(new_banned_ip)
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
os.makedirs(os.path.dirname(banned_ips_file), exist_ok=True)
|
||||||
|
|
||||||
|
# Save to file
|
||||||
|
with open(banned_ips_file, 'w') as f:
|
||||||
|
json.dump(banned_ips, f, indent=2)
|
||||||
|
|
||||||
|
# Apply firewall rule to block the IP
|
||||||
|
try:
|
||||||
|
# Add iptables rule to block the IP
|
||||||
|
if '/' in ip:
|
||||||
|
# CIDR notation
|
||||||
|
subprocess.run(['iptables', '-A', 'INPUT', '-s', ip, '-j', 'DROP'], check=True)
|
||||||
|
else:
|
||||||
|
# Single IP
|
||||||
|
subprocess.run(['iptables', '-A', 'INPUT', '-s', ip, '-j', 'DROP'], check=True)
|
||||||
|
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Banned IP {ip} with reason: {reason}')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Failed to add iptables rule for {ip}: {str(e)}')
|
||||||
|
|
||||||
|
final_dic = {'status': 1, 'message': f'IP address {ip} has been banned successfully'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
final_dic = {'status': 0, 'error_message': str(msg)}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
def removeBannedIP(self, userID=None, data=None):
|
||||||
|
"""
|
||||||
|
Remove/unban an IP address
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
banned_ip_id = data.get('id')
|
||||||
|
|
||||||
|
# Load existing banned IPs
|
||||||
|
banned_ips_file = '/etc/cyberpanel/banned_ips.json'
|
||||||
|
banned_ips = []
|
||||||
|
if os.path.exists(banned_ips_file):
|
||||||
|
try:
|
||||||
|
with open(banned_ips_file, 'r') as f:
|
||||||
|
banned_ips = json.load(f)
|
||||||
|
except:
|
||||||
|
banned_ips = []
|
||||||
|
|
||||||
|
# Find and update the banned IP
|
||||||
|
ip_to_unban = None
|
||||||
|
for banned_ip in banned_ips:
|
||||||
|
if banned_ip.get('id') == banned_ip_id:
|
||||||
|
banned_ip['active'] = False
|
||||||
|
banned_ip['unbanned_on'] = time.time()
|
||||||
|
ip_to_unban = banned_ip['ip']
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ip_to_unban:
|
||||||
|
final_dic = {'status': 0, 'error_message': 'Banned IP not found'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
# Save updated banned IPs
|
||||||
|
with open(banned_ips_file, 'w') as f:
|
||||||
|
json.dump(banned_ips, f, indent=2)
|
||||||
|
|
||||||
|
# Remove iptables rule
|
||||||
|
try:
|
||||||
|
# Remove iptables rule to unblock the IP
|
||||||
|
if '/' in ip_to_unban:
|
||||||
|
# CIDR notation
|
||||||
|
subprocess.run(['iptables', '-D', 'INPUT', '-s', ip_to_unban, '-j', 'DROP'], check=False)
|
||||||
|
else:
|
||||||
|
# Single IP
|
||||||
|
subprocess.run(['iptables', '-D', 'INPUT', '-s', ip_to_unban, '-j', 'DROP'], check=False)
|
||||||
|
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Unbanned IP {ip_to_unban}')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Failed to remove iptables rule for {ip_to_unban}: {str(e)}')
|
||||||
|
|
||||||
|
final_dic = {'status': 1, 'message': f'IP address {ip_to_unban} has been unbanned successfully'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
final_dic = {'status': 0, 'error_message': str(msg)}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
def deleteBannedIP(self, userID=None, data=None):
|
||||||
|
"""
|
||||||
|
Permanently delete a banned IP record
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
if admin.acl.adminStatus != 1:
|
||||||
|
return ACLManager.loadError()
|
||||||
|
|
||||||
|
banned_ip_id = data.get('id')
|
||||||
|
|
||||||
|
# Load existing banned IPs
|
||||||
|
banned_ips_file = '/etc/cyberpanel/banned_ips.json'
|
||||||
|
banned_ips = []
|
||||||
|
if os.path.exists(banned_ips_file):
|
||||||
|
try:
|
||||||
|
with open(banned_ips_file, 'r') as f:
|
||||||
|
banned_ips = json.load(f)
|
||||||
|
except:
|
||||||
|
banned_ips = []
|
||||||
|
|
||||||
|
# Find and remove the banned IP
|
||||||
|
ip_to_delete = None
|
||||||
|
updated_banned_ips = []
|
||||||
|
for banned_ip in banned_ips:
|
||||||
|
if banned_ip.get('id') == banned_ip_id:
|
||||||
|
ip_to_delete = banned_ip['ip']
|
||||||
|
else:
|
||||||
|
updated_banned_ips.append(banned_ip)
|
||||||
|
|
||||||
|
if not ip_to_delete:
|
||||||
|
final_dic = {'status': 0, 'error_message': 'Banned IP record not found'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
# Save updated banned IPs
|
||||||
|
with open(banned_ips_file, 'w') as f:
|
||||||
|
json.dump(updated_banned_ips, f, indent=2)
|
||||||
|
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f'Deleted banned IP record for {ip_to_delete}')
|
||||||
|
|
||||||
|
final_dic = {'status': 1, 'message': f'Banned IP record for {ip_to_delete} has been deleted successfully'}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
except BaseException as msg:
|
||||||
|
final_dic = {'status': 0, 'error_message': str(msg)}
|
||||||
|
final_json = json.dumps(final_dic)
|
||||||
|
return HttpResponse(final_json)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,21 @@ app.controller('firewallController', function ($scope, $http) {
|
|||||||
$scope.couldNotConnect = true;
|
$scope.couldNotConnect = true;
|
||||||
$scope.rulesDetails = false;
|
$scope.rulesDetails = false;
|
||||||
|
|
||||||
|
// Banned IPs variables
|
||||||
|
$scope.activeTab = 'rules';
|
||||||
|
$scope.bannedIPs = [];
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
$scope.bannedIPActionFailed = true;
|
||||||
|
$scope.bannedIPActionSuccess = true;
|
||||||
|
$scope.bannedIPCouldNotConnect = true;
|
||||||
|
$scope.banIP = '';
|
||||||
|
$scope.banReason = '';
|
||||||
|
$scope.banDuration = '24h';
|
||||||
|
|
||||||
firewallStatus();
|
firewallStatus();
|
||||||
|
|
||||||
populateCurrentRecords();
|
populateCurrentRecords();
|
||||||
|
populateBannedIPs();
|
||||||
|
|
||||||
$scope.addRule = function () {
|
$scope.addRule = function () {
|
||||||
|
|
||||||
@@ -2393,4 +2405,140 @@ app.controller('litespeed_ent_conf', function ($scope, $http, $timeout, $window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Banned IPs Functions
|
||||||
|
function populateBannedIPs() {
|
||||||
|
$scope.bannedIPsLoading = true;
|
||||||
|
var url = "/firewall/getBannedIPs";
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, {}, config).then(function(response) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
if (response.data.status === 1) {
|
||||||
|
$scope.bannedIPs = response.data.bannedIPs || [];
|
||||||
|
} else {
|
||||||
|
$scope.bannedIPs = [];
|
||||||
|
$scope.bannedIPActionFailed = false;
|
||||||
|
$scope.bannedIPErrorMessage = response.data.error_message;
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
$scope.bannedIPCouldNotConnect = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.addBannedIP = function() {
|
||||||
|
if (!$scope.banIP || !$scope.banReason) {
|
||||||
|
$scope.bannedIPActionFailed = false;
|
||||||
|
$scope.bannedIPErrorMessage = "Please fill in all required fields";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.bannedIPsLoading = true;
|
||||||
|
$scope.bannedIPActionFailed = true;
|
||||||
|
$scope.bannedIPActionSuccess = true;
|
||||||
|
$scope.bannedIPCouldNotConnect = true;
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
ip: $scope.banIP,
|
||||||
|
reason: $scope.banReason,
|
||||||
|
duration: $scope.banDuration
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = "/firewall/addBannedIP";
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
if (response.data.status === 1) {
|
||||||
|
$scope.bannedIPActionSuccess = false;
|
||||||
|
$scope.banIP = '';
|
||||||
|
$scope.banReason = '';
|
||||||
|
$scope.banDuration = '24h';
|
||||||
|
populateBannedIPs(); // Refresh the list
|
||||||
|
} else {
|
||||||
|
$scope.bannedIPActionFailed = false;
|
||||||
|
$scope.bannedIPErrorMessage = response.data.error_message;
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
$scope.bannedIPCouldNotConnect = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeBannedIP = function(id, ip) {
|
||||||
|
if (!confirm('Are you sure you want to unban IP address ' + ip + '?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.bannedIPsLoading = true;
|
||||||
|
$scope.bannedIPActionFailed = true;
|
||||||
|
$scope.bannedIPActionSuccess = true;
|
||||||
|
$scope.bannedIPCouldNotConnect = true;
|
||||||
|
|
||||||
|
var data = { id: id };
|
||||||
|
|
||||||
|
var url = "/firewall/removeBannedIP";
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
if (response.data.status === 1) {
|
||||||
|
$scope.bannedIPActionSuccess = false;
|
||||||
|
populateBannedIPs(); // Refresh the list
|
||||||
|
} else {
|
||||||
|
$scope.bannedIPActionFailed = false;
|
||||||
|
$scope.bannedIPErrorMessage = response.data.error_message;
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
$scope.bannedIPCouldNotConnect = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteBannedIP = function(id, ip) {
|
||||||
|
if (!confirm('Are you sure you want to permanently delete the record for IP address ' + ip + '? This action cannot be undone.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.bannedIPsLoading = true;
|
||||||
|
$scope.bannedIPActionFailed = true;
|
||||||
|
$scope.bannedIPActionSuccess = true;
|
||||||
|
$scope.bannedIPCouldNotConnect = true;
|
||||||
|
|
||||||
|
var data = { id: id };
|
||||||
|
|
||||||
|
var url = "/firewall/deleteBannedIP";
|
||||||
|
var config = {
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.post(url, data, config).then(function(response) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
if (response.data.status === 1) {
|
||||||
|
$scope.bannedIPActionSuccess = false;
|
||||||
|
populateBannedIPs(); // Refresh the list
|
||||||
|
} else {
|
||||||
|
$scope.bannedIPActionFailed = false;
|
||||||
|
$scope.bannedIPErrorMessage = response.data.error_message;
|
||||||
|
}
|
||||||
|
}, function(error) {
|
||||||
|
$scope.bannedIPsLoading = false;
|
||||||
|
$scope.bannedIPCouldNotConnect = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -526,6 +526,265 @@
|
|||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tab Navigation Styles */
|
||||||
|
.tab-navigation {
|
||||||
|
display: flex;
|
||||||
|
background: var(--bg-secondary, white);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
box-shadow: 0 2px 8px var(--shadow-light, rgba(0,0,0,0.1));
|
||||||
|
border: 1px solid var(--border-color, #e8e9ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-secondary, #64748b);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button:hover {
|
||||||
|
background: var(--bg-hover, #f8f9ff);
|
||||||
|
color: var(--accent-color, #5b5fcf);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button.tab-active {
|
||||||
|
background: var(--accent-color, #5b5fcf);
|
||||||
|
color: var(--bg-secondary, white);
|
||||||
|
box-shadow: 0 2px 8px rgba(91, 95, 207, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button i {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Banned IPs Panel Styles */
|
||||||
|
.banned-ips-panel {
|
||||||
|
background: var(--bg-secondary, white);
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px var(--shadow-medium, rgba(0,0,0,0.15));
|
||||||
|
border: 1px solid var(--border-color, #e8e9ff);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-banned-section {
|
||||||
|
padding: 2rem;
|
||||||
|
border-bottom: 1px solid var(--border-light, #f3f4f6);
|
||||||
|
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr auto;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ban {
|
||||||
|
background: var(--danger-color, #ef4444);
|
||||||
|
color: var(--bg-secondary, white);
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ban:hover {
|
||||||
|
background: var(--danger-hover, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-list-section {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
background: var(--bg-secondary, white);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px var(--shadow-light, rgba(0,0,0,0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table th {
|
||||||
|
background: linear-gradient(135deg, var(--bg-hover, #f8f9ff) 0%, var(--bg-gradient, #f0f1ff) 100%);
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary, #1e293b);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 2px solid var(--border-color, #e8e9ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table td {
|
||||||
|
padding: 1rem;
|
||||||
|
color: var(--text-secondary, #64748b);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
border-bottom: 1px solid var(--border-light, #f3f4f6);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table tbody tr {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table tbody tr:hover {
|
||||||
|
background: var(--bg-hover, #f8f9ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ip-address {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary, #1e293b);
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ip-address i {
|
||||||
|
color: var(--accent-color, #5b5fcf);
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-text {
|
||||||
|
background: var(--bg-light, #f8f9fa);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary, #64748b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-date, .expires-date {
|
||||||
|
color: var(--text-secondary, #64748b);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-date i, .expires-date i {
|
||||||
|
color: var(--accent-color, #5b5fcf);
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.status-active {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--danger-color, #ef4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.status-expired {
|
||||||
|
background: rgba(107, 114, 128, 0.1);
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge i {
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unban {
|
||||||
|
background: var(--success-color, #10b981);
|
||||||
|
color: var(--bg-secondary, white);
|
||||||
|
border: none;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unban:hover {
|
||||||
|
background: var(--success-hover, #059669);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete {
|
||||||
|
background: var(--danger-color, #ef4444);
|
||||||
|
color: var(--bg-secondary, white);
|
||||||
|
border: none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete:hover {
|
||||||
|
background: var(--danger-hover, #dc2626);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design for Banned IPs */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.banned-form {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ban {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-list-section {
|
||||||
|
padding: 1rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banned-table {
|
||||||
|
min-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-navigation {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -591,8 +850,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab Navigation -->
|
||||||
|
<div class="tab-navigation">
|
||||||
|
<button type="button"
|
||||||
|
ng-click="activeTab = 'rules'"
|
||||||
|
ng-class="{'tab-active': activeTab === 'rules'}"
|
||||||
|
class="tab-button">
|
||||||
|
<i class="fas fa-list-alt"></i>
|
||||||
|
{% trans "Firewall Rules" %}
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
ng-click="activeTab = 'banned'"
|
||||||
|
ng-class="{'tab-active': activeTab === 'banned'}"
|
||||||
|
class="tab-button">
|
||||||
|
<i class="fas fa-ban"></i>
|
||||||
|
{% trans "Banned IPs" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Rules Panel -->
|
<!-- Rules Panel -->
|
||||||
<div class="rules-panel">
|
<div class="rules-panel" ng-show="activeTab === 'rules'">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<div class="panel-icon">
|
<div class="panel-icon">
|
||||||
@@ -717,6 +994,144 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Banned IPs Panel -->
|
||||||
|
<div class="banned-ips-panel" ng-show="activeTab === 'banned'">
|
||||||
|
<div class="panel-header">
|
||||||
|
<div class="panel-title">
|
||||||
|
<div class="panel-icon">
|
||||||
|
<i class="fas fa-ban"></i>
|
||||||
|
</div>
|
||||||
|
{% trans "Banned IP Addresses" %}
|
||||||
|
</div>
|
||||||
|
<div ng-show="bannedIPsLoading" class="loading-spinner"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Banned IP Section -->
|
||||||
|
<div class="add-banned-section">
|
||||||
|
<form class="banned-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{% trans "IP Address" %}</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="banIP"
|
||||||
|
placeholder="{% trans 'e.g., 192.168.1.100 or 192.168.1.0/24' %}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{% trans "Reason" %}</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="banReason"
|
||||||
|
placeholder="{% trans 'e.g., Suspicious activity, Brute force attack' %}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{% trans "Duration" %}</label>
|
||||||
|
<select ng-model="banDuration" class="form-control select-control">
|
||||||
|
<option value="1h">{% trans "1 Hour" %}</option>
|
||||||
|
<option value="24h">{% trans "24 Hours" %}</option>
|
||||||
|
<option value="7d">{% trans "7 Days" %}</option>
|
||||||
|
<option value="30d">{% trans "30 Days" %}</option>
|
||||||
|
<option value="permanent">{% trans "Permanent" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
ng-click="addBannedIP()"
|
||||||
|
class="btn-ban">
|
||||||
|
<i class="fas fa-ban"></i>
|
||||||
|
{% trans "Ban IP Address" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Banned IPs List -->
|
||||||
|
<div class="banned-list-section">
|
||||||
|
<table class="banned-table" ng-if="bannedIPs.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "IP Address" %}</th>
|
||||||
|
<th>{% trans "Reason" %}</th>
|
||||||
|
<th>{% trans "Banned On" %}</th>
|
||||||
|
<th>{% trans "Expires" %}</th>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th>{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="bannedIP in bannedIPs">
|
||||||
|
<td class="ip-address">
|
||||||
|
<i class="fas fa-globe"></i>
|
||||||
|
{$ bannedIP.ip $}
|
||||||
|
</td>
|
||||||
|
<td class="reason">
|
||||||
|
<span class="reason-text">{$ bannedIP.reason $}</span>
|
||||||
|
</td>
|
||||||
|
<td class="banned-date">
|
||||||
|
<i class="fas fa-calendar"></i>
|
||||||
|
{$ bannedIP.banned_on | date:'MMM dd, yyyy HH:mm' $}
|
||||||
|
</td>
|
||||||
|
<td class="expires-date">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
<span ng-if="bannedIP.expires === 'Never'">{% trans "Never" %}</span>
|
||||||
|
<span ng-if="bannedIP.expires !== 'Never'">{$ bannedIP.expires | date:'MMM dd, yyyy HH:mm' $}</span>
|
||||||
|
</td>
|
||||||
|
<td class="status">
|
||||||
|
<span ng-class="{'status-active': bannedIP.active, 'status-expired': !bannedIP.active}"
|
||||||
|
class="status-badge">
|
||||||
|
<i class="fas fa-circle"></i>
|
||||||
|
{$ bannedIP.active ? 'Active' : 'Expired' $}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="actions">
|
||||||
|
<button type="button"
|
||||||
|
ng-click="removeBannedIP(bannedIP.id, bannedIP.ip)"
|
||||||
|
class="btn-unban"
|
||||||
|
title="{% trans 'Unban IP' %}"
|
||||||
|
ng-if="bannedIP.active">
|
||||||
|
<i class="fas fa-unlock"></i>
|
||||||
|
{% trans "Unban" %}
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
ng-click="deleteBannedIP(bannedIP.id, bannedIP.ip)"
|
||||||
|
class="btn-delete"
|
||||||
|
title="{% trans 'Delete Record' %}">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div ng-if="bannedIPs.length == 0" class="empty-state">
|
||||||
|
<i class="fas fa-shield-check empty-icon"></i>
|
||||||
|
<h3 class="empty-title">{% trans "No Banned IPs" %}</h3>
|
||||||
|
<p class="empty-text">{% trans "All IP addresses are currently allowed. Add banned IPs to block suspicious or malicious traffic." %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Messages -->
|
||||||
|
<div style="padding: 0 2rem 2rem;">
|
||||||
|
<div ng-hide="bannedIPActionFailed" class="alert alert-danger">
|
||||||
|
<i class="fas fa-exclamation-circle alert-icon"></i>
|
||||||
|
<span>{% trans "Action failed. Error message:" %} {$ bannedIPErrorMessage $}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-hide="bannedIPActionSuccess" class="alert alert-success">
|
||||||
|
<i class="fas fa-check-circle alert-icon"></i>
|
||||||
|
<span>{% trans "Action completed successfully." %}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-hide="bannedIPCouldNotConnect" class="alert alert-danger">
|
||||||
|
<i class="fas fa-exclamation-circle alert-icon"></i>
|
||||||
|
<span>{% trans "Could not connect to server. Please refresh this page." %}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -32,6 +32,12 @@ urlpatterns = [
|
|||||||
path('modSecRulesPacks', views.modSecRulesPacks, name='modSecRulesPacks'),
|
path('modSecRulesPacks', views.modSecRulesPacks, name='modSecRulesPacks'),
|
||||||
path('getOWASPAndComodoStatus', views.getOWASPAndComodoStatus, name='getOWASPAndComodoStatus'),
|
path('getOWASPAndComodoStatus', views.getOWASPAndComodoStatus, name='getOWASPAndComodoStatus'),
|
||||||
path('installModSecRulesPack', views.installModSecRulesPack, name='installModSecRulesPack'),
|
path('installModSecRulesPack', views.installModSecRulesPack, name='installModSecRulesPack'),
|
||||||
|
|
||||||
|
# Banned IPs
|
||||||
|
path('getBannedIPs', views.getBannedIPs, name='getBannedIPs'),
|
||||||
|
path('addBannedIP', views.addBannedIP, name='addBannedIP'),
|
||||||
|
path('removeBannedIP', views.removeBannedIP, name='removeBannedIP'),
|
||||||
|
path('deleteBannedIP', views.deleteBannedIP, name='deleteBannedIP'),
|
||||||
path('getRulesFiles', views.getRulesFiles, name='getRulesFiles'),
|
path('getRulesFiles', views.getRulesFiles, name='getRulesFiles'),
|
||||||
path('enableDisableRuleFile', views.enableDisableRuleFile, name='enableDisableRuleFile'),
|
path('enableDisableRuleFile', views.enableDisableRuleFile, name='enableDisableRuleFile'),
|
||||||
|
|
||||||
|
|||||||
@@ -648,3 +648,36 @@ def saveLitespeed_conf(request):
|
|||||||
return fm.saveLitespeed_conf(userID, json.loads(request.body))
|
return fm.saveLitespeed_conf(userID, json.loads(request.body))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return redirect(loadLoginPage)
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
# Banned IPs Views
|
||||||
|
def getBannedIPs(request):
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
fm = FirewallManager()
|
||||||
|
return fm.getBannedIPs(userID)
|
||||||
|
except KeyError:
|
||||||
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
def addBannedIP(request):
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
fm = FirewallManager()
|
||||||
|
return fm.addBannedIP(userID, json.loads(request.body))
|
||||||
|
except KeyError:
|
||||||
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
def removeBannedIP(request):
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
fm = FirewallManager()
|
||||||
|
return fm.removeBannedIP(userID, json.loads(request.body))
|
||||||
|
except KeyError:
|
||||||
|
return redirect(loadLoginPage)
|
||||||
|
|
||||||
|
def deleteBannedIP(request):
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
fm = FirewallManager()
|
||||||
|
return fm.deleteBannedIP(userID, json.loads(request.body))
|
||||||
|
except KeyError:
|
||||||
|
return redirect(loadLoginPage)
|
||||||
Reference in New Issue
Block a user