diff --git a/assets/less/classes/manage/blacklist.less b/assets/less/classes/manage/blacklist.less new file mode 100644 index 00000000..5abc012f --- /dev/null +++ b/assets/less/classes/manage/blacklist.less @@ -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; + } +} diff --git a/assets/less/classes/manage/manage.less b/assets/less/classes/manage/manage.less index 3c931393..6e09c3a1 100644 --- a/assets/less/classes/manage/manage.less +++ b/assets/less/classes/manage/manage.less @@ -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; + } } diff --git a/assets/less/main.less b/assets/less/main.less index ed664507..053308cc 100644 --- a/assets/less/main.less +++ b/assets/less/main.less @@ -114,6 +114,7 @@ html { // Manage @import "classes/manage/manage"; @import "classes/manage/navigation"; +@import "classes/manage/blacklist"; // Profile @import "classes/profile/profile"; diff --git a/public/manage/index.php b/public/manage/index.php index 4dfcf50f..2fa46a18 100644 --- a/public/manage/index.php +++ b/public/manage/index.php @@ -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; } diff --git a/src/Net/ip.php b/src/Net/ip.php index f2080c04..b01c4063 100644 --- a/src/Net/ip.php +++ b/src/Net/ip.php @@ -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 : []; } diff --git a/src/Users/user.php b/src/Users/user.php index 6f7ecea8..48915b37 100644 --- a/src/Users/user.php +++ b/src/Users/user.php @@ -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', diff --git a/src/general.php b/src/general.php index ea7c772c..bd262191 100644 --- a/src/general.php +++ b/src/general.php @@ -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); diff --git a/src/manage.php b/src/manage.php index 1f87b30d..0aaff7c5 100644 --- a/src/manage.php +++ b/src/manage.php @@ -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, - ], ], ], [ diff --git a/templates/manage/general/blacklist.twig b/templates/manage/general/blacklist.twig new file mode 100644 index 00000000..5329ea53 --- /dev/null +++ b/templates/manage/general/blacklist.twig @@ -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 %} +
+ {{ container_title(' IP Blacklist', '', true) }} + +
+ 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. +
+ + {% if notices|length > 0 %} +
+
+ {% for notice in notices %} + {{ notice }} + {% endfor %} +
+
+ {% endif %} + +
+
+ {{ input_csrf('ip_blacklist') }} + + +
+ +
+ {{ input_csrf('ip_blacklist') }} + {{ input_select('blacklist[remove][]', blacklist, null, 'ip_cidr', null, true, 'manage__blacklist__select', { + 'multiple': true, + 'size': 10, + }) }} + +
+
+
+{% endblock %}