Added a UI for managing the IP blacklist.
This commit is contained in:
parent
3f12ff7d8d
commit
38cb13aefe
9 changed files with 191 additions and 17 deletions
31
assets/less/classes/manage/blacklist.less
Normal file
31
assets/less/classes/manage/blacklist.less
Normal file
|
@ -0,0 +1,31 @@
|
|||
.manage__blacklist {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
@media (max-width: @site-mobile-width) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__form {
|
||||
margin: 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__select,
|
||||
&__textarea {
|
||||
margin: 0;
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
|
@ -17,4 +17,11 @@
|
|||
&__content {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: .9em;
|
||||
margin: 1px 2px;
|
||||
border-bottom: 1px solid var(--accent-colour);
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ html {
|
|||
// Manage
|
||||
@import "classes/manage/manage";
|
||||
@import "classes/manage/navigation";
|
||||
@import "classes/manage/blacklist";
|
||||
|
||||
// Profile
|
||||
@import "classes/profile/profile";
|
||||
|
|
|
@ -92,4 +92,52 @@ switch ($_GET['v'] ?? null) {
|
|||
|
||||
echo tpl_render('manage.general.settings');
|
||||
break;
|
||||
|
||||
case 'blacklist':
|
||||
if (!perms_check($generalPerms, MSZ_PERM_GENERAL_MANAGE_BLACKLIST)) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
$notices = [];
|
||||
|
||||
while (!empty($_POST)) {
|
||||
if (!csrf_verify('ip_blacklist', $_POST['csrf'] ?? '')) {
|
||||
$notices[] = 'Verification failed.';
|
||||
break;
|
||||
}
|
||||
|
||||
header(csrf_http_header('ip_blacklist'));
|
||||
|
||||
if (!empty($_POST['blacklist']['remove']) && is_array($_POST['blacklist']['remove'])) {
|
||||
foreach ($_POST['blacklist']['remove'] as $cidr) {
|
||||
if (!ip_blacklist_remove($cidr)) {
|
||||
$notices[] = sprintf('Failed to remove "%s" from the blacklist.', $cidr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_POST['blacklist']['add']) && is_string($_POST['blacklist']['add'])) {
|
||||
$cidrs = explode("\n", $_POST['blacklist']['add']);
|
||||
|
||||
foreach ($cidrs as $cidr) {
|
||||
$cidr = trim($cidr);
|
||||
|
||||
if (empty($cidr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ip_blacklist_add($cidr)) {
|
||||
$notices[] = sprintf('Failed to add "%s" to the blacklist.', $cidr);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
echo tpl_render('manage.general.blacklist', [
|
||||
'notices' => $notices,
|
||||
'blacklist' => ip_blacklist_list(),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ function ip_match_cidr(string $address, string $cidr): bool
|
|||
return ip_match_cidr_raw($address, $subnet, $mask);
|
||||
}
|
||||
|
||||
function ip_cidr_to_raw(string $cidr): array
|
||||
function ip_cidr_to_raw(string $cidr): ?array
|
||||
{
|
||||
if (strpos($cidr, '/') !== false) {
|
||||
[$subnet, $mask] = explode('/', $cidr, 2);
|
||||
|
@ -101,8 +101,13 @@ function ip_cidr_to_raw(string $cidr): array
|
|||
$subnet = $cidr;
|
||||
}
|
||||
|
||||
$subnet = inet_pton($subnet);
|
||||
$mask = empty($mask) ? null : $mask;
|
||||
try {
|
||||
$subnet = inet_pton($subnet);
|
||||
} catch (Exception $ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$mask = empty($mask) ? null : (int)$mask;
|
||||
|
||||
return compact('subnet', 'mask');
|
||||
}
|
||||
|
@ -138,7 +143,7 @@ function ip_blacklist_add_raw(string $subnet, ?int $mask = null): bool
|
|||
}
|
||||
|
||||
$addBlacklist = db_prepare('
|
||||
INSERT INTO `msz_ip_blacklist`
|
||||
REPLACE INTO `msz_ip_blacklist`
|
||||
(`ip_subnet`, `ip_mask`)
|
||||
VALUES
|
||||
(:subnet, :mask)
|
||||
|
@ -150,7 +155,48 @@ function ip_blacklist_add_raw(string $subnet, ?int $mask = null): bool
|
|||
|
||||
function ip_blacklist_add(string $cidr): bool
|
||||
{
|
||||
[$subnet, $mask] = ['', 0];
|
||||
extract(ip_cidr_to_raw($cidr));
|
||||
return ip_blacklist_add_raw($subnet, $mask);
|
||||
$raw = ip_cidr_to_raw($cidr);
|
||||
|
||||
if (empty($raw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ip_blacklist_add_raw($raw['subnet'], $raw['mask']);
|
||||
}
|
||||
|
||||
function ip_blacklist_remove_raw(string $subnet, ?int $mask = null): bool
|
||||
{
|
||||
$removeBlacklist = db_prepare('
|
||||
DELETE FROM `msz_ip_blacklist`
|
||||
WHERE `ip_subnet` = :subnet
|
||||
AND `ip_mask` = :mask
|
||||
');
|
||||
$removeBlacklist->bindValue('subnet', $subnet);
|
||||
$removeBlacklist->bindValue('mask', $mask);
|
||||
return $removeBlacklist->execute();
|
||||
}
|
||||
|
||||
function ip_blacklist_remove(string $cidr): bool
|
||||
{
|
||||
$raw = ip_cidr_to_raw($cidr);
|
||||
|
||||
if (empty($raw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ip_blacklist_remove_raw($raw['subnet'], $raw['mask']);
|
||||
}
|
||||
|
||||
function ip_blacklist_list(): array
|
||||
{
|
||||
$getBlacklist = db_query("
|
||||
SELECT
|
||||
INET6_NTOA(`ip_subnet`) AS `ip_subnet`,
|
||||
`ip_mask`,
|
||||
LENGTH(`ip_subnet`) AS `ip_bytes`,
|
||||
CONCAT(INET6_NTOA(`ip_subnet`), '/', `ip_mask`) as `ip_cidr`
|
||||
FROM `msz_ip_blacklist`
|
||||
");
|
||||
$blacklist = $getBlacklist->execute() ? $getBlacklist->fetchAll(PDO::FETCH_ASSOC) : false;
|
||||
return $blacklist ? $blacklist : [];
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ define('MSZ_PERM_USER_MANAGE_ROLES', 1 << 21);
|
|||
define('MSZ_PERM_USER_MANAGE_PERMS', 1 << 22);
|
||||
define('MSZ_PERM_USER_MANAGE_REPORTS', 1 << 23);
|
||||
define('MSZ_PERM_USER_MANAGE_RESTRICTIONS', 1 << 24);
|
||||
define('MSZ_PERM_USER_MANAGE_BLACKLISTS', 1 << 25);
|
||||
//define('MSZ_PERM_USER_MANAGE_BLACKLISTS', 1 << 25); // Replaced with MSZ_PERM_GENERAL_MANAGE_BLACKLIST
|
||||
|
||||
define(
|
||||
'MSZ_USERS_PASSWORD_HASH_ALGO',
|
||||
|
|
|
@ -4,3 +4,4 @@ define('MSZ_PERM_GENERAL_VIEW_LOGS', 1 << 1);
|
|||
define('MSZ_PERM_GENERAL_MANAGE_EMOTICONS', 1 << 2);
|
||||
define('MSZ_PERM_GENERAL_MANAGE_SETTINGS', 1 << 3);
|
||||
define('MSZ_PERM_GENERAL_TESTER', 1 << 4);
|
||||
define('MSZ_PERM_GENERAL_MANAGE_BLACKLIST', 1 << 5);
|
||||
|
|
|
@ -27,6 +27,10 @@ function manage_get_menu(int $userId): array
|
|||
$menu['General']['Settings'] = '/manage/index.php?v=settings';
|
||||
}
|
||||
|
||||
if (perms_check($perms['general'], MSZ_PERM_GENERAL_MANAGE_BLACKLIST)) {
|
||||
$menu['General']['IP Blacklist'] = '/manage/index.php?v=blacklist';
|
||||
}
|
||||
|
||||
if (perms_check($perms['user'], MSZ_PERM_USER_MANAGE_USERS | MSZ_PERM_USER_MANAGE_PERMS)) {
|
||||
$menu['Users']['Listing'] = '/manage/users.php?v=listing';
|
||||
}
|
||||
|
@ -43,10 +47,6 @@ function manage_get_menu(int $userId): array
|
|||
$menu['Users']['Restrictions'] = '/manage/users.php?v=restrictions';
|
||||
}
|
||||
|
||||
if (perms_check($perms['user'], MSZ_PERM_USER_MANAGE_BLACKLISTS)) {
|
||||
$menu['Users']['Blacklisting'] = '/manage/users.php?v=blacklisting';
|
||||
}
|
||||
|
||||
if (perms_check($perms['news'], MSZ_PERM_NEWS_MANAGE_POSTS)) {
|
||||
$menu['News']['Posts'] = '/manage/news.php?v=posts';
|
||||
}
|
||||
|
@ -199,6 +199,11 @@ function manage_perms_list(array $rawPerms): array
|
|||
'title' => 'Can use experimental features.',
|
||||
'perm' => MSZ_PERM_GENERAL_TESTER,
|
||||
],
|
||||
[
|
||||
'section' => 'manage-blacklist',
|
||||
'title' => 'Can manage blacklistings.',
|
||||
'perm' => MSZ_PERM_GENERAL_MANAGE_BLACKLIST,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -250,11 +255,6 @@ function manage_perms_list(array $rawPerms): array
|
|||
'title' => 'Can manage restrictions.',
|
||||
'perm' => MSZ_PERM_USER_MANAGE_RESTRICTIONS,
|
||||
],
|
||||
[
|
||||
'section' => 'manage-blacklistings',
|
||||
'title' => 'Can manage blacklistings.',
|
||||
'perm' => MSZ_PERM_USER_MANAGE_BLACKLISTS,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
|
40
templates/manage/general/blacklist.twig
Normal file
40
templates/manage/general/blacklist.twig
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends 'manage/general/master.twig' %}
|
||||
{% from 'macros.twig' import container_title, pagination %}
|
||||
{% from '_layout/input.twig' import input_csrf, input_text, input_checkbox, input_file, input_select %}
|
||||
|
||||
{% block manage_content %}
|
||||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-shield-alt"></i> IP Blacklist', '', true) }}
|
||||
|
||||
<div class="manage__description">
|
||||
Here you can add or remove CIDR ranges to the IP Blacklist, these ranges are allowed to log into the site but cannot create accounts.
|
||||
</div>
|
||||
|
||||
{% if notices|length > 0 %}
|
||||
<div class="warning">
|
||||
<div class="warning__content">
|
||||
{% for notice in notices %}
|
||||
{{ notice }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="manage__blacklist">
|
||||
<form action="" method="post" class="manage__blacklist__form">
|
||||
{{ input_csrf('ip_blacklist') }}
|
||||
<textarea name="blacklist[add]" class="input__textarea manage__blacklist__textarea" placeholder="Enter CIDR (subnet/mask), each line will be processed. Addresses without a mask will just be blacklisted alone."></textarea>
|
||||
<button class="input__button input__button--save manage__blacklist__button">Add</button>
|
||||
</form>
|
||||
|
||||
<form action="" method="post" class="manage__blacklist__form">
|
||||
{{ input_csrf('ip_blacklist') }}
|
||||
{{ input_select('blacklist[remove][]', blacklist, null, 'ip_cidr', null, true, 'manage__blacklist__select', {
|
||||
'multiple': true,
|
||||
'size': 10,
|
||||
}) }}
|
||||
<button class="input__button input__button--destroy manage__blacklist__button">Remove</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Reference in a new issue