diff --git a/assets/less/classes/profile/warning.less b/assets/less/classes/profile/warning.less index e7991fab..8bee3022 100644 --- a/assets/less/classes/profile/warning.less +++ b/assets/less/classes/profile/warning.less @@ -1,9 +1,12 @@ .profile__warning { margin: 2px; - padding: 1px; border-radius: 2px; border: 1px solid var(--accent-colour); + &__container { + margin: 2px 0; + } + &--warning { --accent-colour: #666; } @@ -16,31 +19,58 @@ --accent-colour: #c33; } - &__public { + &__background { + background-color: var(--accent-colour); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + &__content { + background-color: var(--background-colour-translucent); display: flex; - flex-wrap: wrap; + padding: 1px; + } + + &__type, + &__created, + &__duration { + display: inline-flex; + align-items: center; + justify-content: center; } &__type { - min-width: 60px; - text-align: center; + min-width: 80px; background-color: var(--accent-colour); border-radius: 1px; padding: 0 4px; } - &__datetime { + &__created, + &__duration { min-width: 100px; padding: 0 4px; } &__note { padding: 1px 4px; + flex: 1 1 auto; } &__private { border-top: 1px solid var(--accent-colour); margin-top: 1px; - padding: 1px 4px; + width: 100%; + opacity: .5; + transition: opacity .2s; + + &:hover, + &:active, + &:focus { + opacity: 1; + } } } diff --git a/assets/less/classes/warning.less b/assets/less/classes/warning.less index 414189ba..12fab2f5 100644 --- a/assets/less/classes/warning.less +++ b/assets/less/classes/warning.less @@ -13,4 +13,15 @@ background-color: rgba(17, 17, 17, .9); padding: 2px 5px; } + + &__link { + color: inherit; + text-decoration: underline dotted; + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } } diff --git a/database/2018_12_24_203231_add_warnings_table.php b/database/2018_12_24_203231_add_warnings_table.php index 66fe7a66..648c745a 100644 --- a/database/2018_12_24_203231_add_warnings_table.php +++ b/database/2018_12_24_203231_add_warnings_table.php @@ -13,13 +13,14 @@ function migrate_up(PDO $conn): void `issuer_id` INT(10) UNSIGNED NULL DEFAULT NULL, `issuer_ip` VARBINARY(16) NOT NULL, `warning_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `warning_duration` TIMESTAMP NULL DEFAULT NULL, `warning_type` TINYINT(3) UNSIGNED NOT NULL, `warning_note` VARCHAR(255) NOT NULL, - `warning_note_private` TEXT NOT NULL, + `warning_note_private` TEXT NULL DEFAULT NULL, PRIMARY KEY (`warning_id`), INDEX `user_warnings_user_foreign` (`user_id`), INDEX `user_warnings_issuer_foreign` (`issuer_id`), - INDEX `user_warnings_indices` (`warning_created`, `warning_type`), + INDEX `user_warnings_indices` (`warning_created`, `warning_type`, `warning_duration`), CONSTRAINT `user_warnings_issuer_foreign` FOREIGN KEY (`issuer_id`) REFERENCES `msz_users` (`user_id`) @@ -32,6 +33,14 @@ function migrate_up(PDO $conn): void ON DELETE CASCADE ) "); + + $conn->exec(" + CREATE TABLE `msz_ip_blacklist` ( + `ip_subnet` VARBINARY(16) NOT NULL, + `ip_mask` TINYINT(3) UNSIGNED NOT NULL, + UNIQUE INDEX `ip_blacklist_unique` (`ip_subnet`, `ip_mask`) + ) + "); } function migrate_down(PDO $conn): void diff --git a/public/auth.php b/public/auth.php index be9df340..39df6d35 100644 --- a/public/auth.php +++ b/public/auth.php @@ -23,6 +23,7 @@ $authEmail = $isSubmission ? ($_POST['auth']['email'] ?? '') : ($_GET['email'] ? $authPassword = $_POST['auth']['password'] ?? ''; $authVerification = $_POST['auth']['verification'] ?? ''; $authRedirect = $_POST['auth']['redirect'] ?? $_GET['redirect'] ?? $_SERVER['HTTP_REFERER'] ?? '/'; +$authRestricted = ip_blacklist_check(ip_remote_address()); tpl_vars([ 'can_create_account' => $canCreateAccount, @@ -31,6 +32,7 @@ tpl_vars([ 'auth_username' => $authUsername, 'auth_email' => $authEmail, 'auth_redirect' => $authRedirect, + 'auth_restricted' => $authRestricted, ]); switch ($authMode) { @@ -300,7 +302,7 @@ MSG; $authRegistrationError = ''; while ($isSubmission) { - if (!$canCreateAccount) { + if (!$canCreateAccount || $authRestricted) { $authRegistrationError = 'You may not create an account right now.'; break; } diff --git a/src/Net/ip.php b/src/Net/ip.php index 02616169..f2080c04 100644 --- a/src/Net/ip.php +++ b/src/Net/ip.php @@ -58,7 +58,7 @@ function ip_get_raw_width(int $version): int return MSZ_IP_SIZES[$version] ?? 0; } -function ip_match_cidr_raw(string $address, string $subnet, int $mask = 0): bool +function ip_match_cidr_raw(string $address, string $subnet, ?int $mask = null): bool { $version = ip_get_raw_version($subnet); $bits = ip_get_raw_width($version) * 8; @@ -72,7 +72,7 @@ function ip_match_cidr_raw(string $address, string $subnet, int $mask = 0): bool } for ($i = 0; $i < ceil($mask / 8); $i++) { - $byteMask = (0xFF00 >> min(8, $mask - ($i * 8))) & 0xFF; + $byteMask = (0xFF00 >> max(0, min(8, $mask - ($i * 8)))) & 0xFF; $addressByte = ord($address[$i]) & $byteMask; $subnetByte = ord($subnet[$i]) & $byteMask; @@ -85,6 +85,15 @@ function ip_match_cidr_raw(string $address, string $subnet, int $mask = 0): bool } function ip_match_cidr(string $address, string $cidr): bool +{ + $address = inet_pton($address); + [$subnet, $mask] = ['', 0]; + extract(ip_cidr_to_raw($cidr)); + + return ip_match_cidr_raw($address, $subnet, $mask); +} + +function ip_cidr_to_raw(string $cidr): array { if (strpos($cidr, '/') !== false) { [$subnet, $mask] = explode('/', $cidr, 2); @@ -92,8 +101,56 @@ function ip_match_cidr(string $address, string $cidr): bool $subnet = $cidr; } - $address = inet_pton($address); $subnet = inet_pton($subnet); + $mask = empty($mask) ? null : $mask; - return ip_match_cidr_raw($address, $subnet, $mask ?? 0); + return compact('subnet', 'mask'); +} + +function ip_blacklist_check(string $address): bool +{ + $checkBlacklist = db_prepare(" + SELECT COUNT(*) > 0 + FROM `msz_ip_blacklist` + WHERE LENGTH(`ip_subnet`) = LENGTH(INET6_ATON(:ip1)) + AND `ip_subnet` & LPAD('', LENGTH(`ip_subnet`), X'FF') << LENGTH(`ip_subnet`) * 8 - `ip_mask` + = INET6_ATON(:ip2) & LPAD('', LENGTH(`ip_subnet`), X'FF') << LENGTH(`ip_subnet`) * 8 - `ip_mask` + "); + $checkBlacklist->bindValue('ip1', $address); + $checkBlacklist->bindValue('ip2', $address); + return (bool)($checkBlacklist->execute() ? $checkBlacklist->fetchColumn() : false); +} + +function ip_blacklist_add_raw(string $subnet, ?int $mask = null): bool +{ + $version = ip_get_raw_version($subnet); + + if ($version === 0) { + return false; + } + + $bits = ip_get_raw_width($version) * 8; + + if (empty($mask)) { + $mask = $bits; + } elseif ($mask < 1 || $mask > $bits) { + return false; + } + + $addBlacklist = db_prepare(' + INSERT INTO `msz_ip_blacklist` + (`ip_subnet`, `ip_mask`) + VALUES + (:subnet, :mask) + '); + $addBlacklist->bindValue('subnet', $subnet); + $addBlacklist->bindValue('mask', $mask); + return $addBlacklist->execute(); +} + +function ip_blacklist_add(string $cidr): bool +{ + [$subnet, $mask] = ['', 0]; + extract(ip_cidr_to_raw($cidr)); + return ip_blacklist_add_raw($subnet, $mask); } diff --git a/src/Users/warning.php b/src/Users/warning.php index 5f0268b5..ecbc9b62 100644 --- a/src/Users/warning.php +++ b/src/Users/warning.php @@ -1,10 +1,11 @@