From eef2adf12aae7c79f1eb525df67b18b234902961 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:07:41 -0500 Subject: [PATCH 01/15] allows admins to view user ips for easy blocking --- migrations/Version20240808230919.php | 29 ++++++++++++++++++++++++++++ src/Entity/User.php | 2 ++ src/Security/GithubAuthenticator.php | 3 +++ src/Security/UserChecker.php | 9 ++++++++- src/Service/EntryCommentManager.php | 4 ++++ src/Service/EntryManager.php | 8 ++++++++ src/Service/PostCommentManager.php | 4 ++++ src/Service/PostManager.php | 8 ++++++++ templates/admin/users.html.twig | 6 ++++++ 9 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 migrations/Version20240808230919.php diff --git a/migrations/Version20240808230919.php b/migrations/Version20240808230919.php new file mode 100644 index 000000000..c360f952a --- /dev/null +++ b/migrations/Version20240808230919.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE "user" ADD ip VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE "user" DROP ip'); + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index dcf2d5af9..51e983fc7 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -93,6 +93,8 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Visibil public ?Image $cover = null; #[Column(type: 'string', unique: true, nullable: false)] public string $email; + #[Column(type: 'string', nullable: true)] + public ?string $ip = null; #[Column(type: 'string', unique: true, nullable: false)] public string $username; #[Column(type: 'json', nullable: false, options: ['jsonb' => true])] diff --git a/src/Security/GithubAuthenticator.php b/src/Security/GithubAuthenticator.php index 476fd6c18..97f5e60c0 100644 --- a/src/Security/GithubAuthenticator.php +++ b/src/Security/GithubAuthenticator.php @@ -6,6 +6,7 @@ use App\DTO\UserDto; use App\Entity\User; +use App\Service\IpResolver; use App\Service\SettingsManager; use App\Service\UserManager; use App\Utils\Slugger; @@ -31,6 +32,7 @@ public function __construct( private readonly RouterInterface $router, private readonly EntityManagerInterface $entityManager, private readonly UserManager $userManager, + private readonly IpResolver $ipResolver, private readonly Slugger $slugger, private readonly SettingsManager $settingsManager ) { @@ -84,6 +86,7 @@ public function authenticate(Request $request): Passport ); $dto->plainPassword = bin2hex(random_bytes(20)); + $dto->ip = $this->ipResolver->resolve(); $user = $this->userManager->create($dto, false); $user->oauthGithubId = \strval($githubUser->getId()); diff --git a/src/Security/UserChecker.php b/src/Security/UserChecker.php index f265ff910..040848dd9 100644 --- a/src/Security/UserChecker.php +++ b/src/Security/UserChecker.php @@ -5,6 +5,8 @@ namespace App\Security; use App\Entity\User as AppUser; +use App\Service\IpResolver; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; @@ -16,7 +18,9 @@ class UserChecker implements UserCheckerInterface { public function __construct( private readonly TranslatorInterface $translator, - private readonly UrlGeneratorInterface $urlGenerator + private readonly UrlGeneratorInterface $urlGenerator, + private readonly EntityManagerInterface $entityManager, + private readonly IpResolver $ipResolver ) { } @@ -45,5 +49,8 @@ public function checkPostAuth(UserInterface $user): void if (!$user instanceof AppUser) { return; } + + $user->ip = $this->ipResolver->resolve(); + $this->entityManager->flush(); } } diff --git a/src/Service/EntryCommentManager.php b/src/Service/EntryCommentManager.php index adaa6a4ed..7a75e6664 100644 --- a/src/Service/EntryCommentManager.php +++ b/src/Service/EntryCommentManager.php @@ -48,6 +48,10 @@ public function __construct( public function create(EntryCommentDto $dto, User $user, $rateLimit = true): EntryComment { + if (!$user->apId) { + $user->ip = $dto->ip; + } + if ($rateLimit) { $limiter = $this->entryCommentLimiter->create($dto->ip); if ($limiter && false === $limiter->consume()->isAccepted()) { diff --git a/src/Service/EntryManager.php b/src/Service/EntryManager.php index 0d600a1f9..c3cba3e7a 100644 --- a/src/Service/EntryManager.php +++ b/src/Service/EntryManager.php @@ -75,6 +75,10 @@ public function __construct( */ public function create(EntryDto $dto, User $user, bool $rateLimit = true, bool $stickyIt = false): Entry { + if (!$user->apId) { + $user->ip = $dto->ip; + } + if ($rateLimit) { $limiter = $this->entryLimiter->create($dto->ip); if (false === $limiter->consume()->isAccepted()) { @@ -182,6 +186,10 @@ public function canUserEditEntry(Entry $entry, User $user): bool public function edit(Entry $entry, EntryDto $dto, User $editedBy): Entry { + if (!$editedBy->apId) { + $editedBy->ip = $dto->ip; + } + Assert::same($entry->magazine->getId(), $dto->magazine->getId()); $entry->title = $dto->title; diff --git a/src/Service/PostCommentManager.php b/src/Service/PostCommentManager.php index 2b57336fb..2c9f9b6f5 100644 --- a/src/Service/PostCommentManager.php +++ b/src/Service/PostCommentManager.php @@ -54,6 +54,10 @@ public function __construct( */ public function create(PostCommentDto $dto, User $user, $rateLimit = true): PostComment { + if (!$user->apId) { + $user->ip = $dto->ip; + } + if ($rateLimit) { $limiter = $this->postCommentLimiter->create($dto->ip); if ($limiter && false === $limiter->consume()->isAccepted()) { diff --git a/src/Service/PostManager.php b/src/Service/PostManager.php index 9e3b7dccf..1c9464ddc 100644 --- a/src/Service/PostManager.php +++ b/src/Service/PostManager.php @@ -66,6 +66,10 @@ public function __construct( */ public function create(PostDto $dto, User $user, $rateLimit = true, bool $stickyIt = false): Post { + if (!$user->apId) { + $user->ip = $dto->ip; + } + if ($rateLimit) { $limiter = $this->postLimiter->create($dto->ip); if ($limiter && false === $limiter->consume()->isAccepted()) { @@ -133,6 +137,10 @@ public function canUserEditPost(Post $post, User $user): bool public function edit(Post $post, PostDto $dto, ?User $editedBy = null): Post { + if (null !== $editedBy && !$editedBy->apId) { + $editedBy->ip = $dto->ip; + } + Assert::same($post->magazine->getId(), $dto->magazine->getId()); $post->body = $dto->body; diff --git a/templates/admin/users.html.twig b/templates/admin/users.html.twig index 9949452bd..8a8cb62ad 100644 --- a/templates/admin/users.html.twig +++ b/templates/admin/users.html.twig @@ -71,6 +71,9 @@ {{ 'username'|trans }} {{ 'email'|trans }} + {% if withFederated is not defined or withFederated is same as false %} + {{ 'IP' }} + {% endif %} {{ 'created_at'|trans }} {{ 'last_active'|trans }} @@ -80,6 +83,9 @@ {{ component('user_inline', {user: user}) }} {{ user.apId ? '-' : user.email }} + {% if withFederated is not defined or withFederated is same as false %} + {{ user.ip }} + {% endif %} {{ component('date', {date: user.createdAt}) }} {{ component('date', {date: user.lastActive}) }} From 28ace2aa7c5c991d8ab3bdea537aeffe743ac3a1 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:22:59 -0500 Subject: [PATCH 02/15] fix lint --- src/Service/EntryCommentManager.php | 2 +- src/Service/PostCommentManager.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service/EntryCommentManager.php b/src/Service/EntryCommentManager.php index 7a75e6664..78acc80c4 100644 --- a/src/Service/EntryCommentManager.php +++ b/src/Service/EntryCommentManager.php @@ -51,7 +51,7 @@ public function create(EntryCommentDto $dto, User $user, $rateLimit = true): Ent if (!$user->apId) { $user->ip = $dto->ip; } - + if ($rateLimit) { $limiter = $this->entryCommentLimiter->create($dto->ip); if ($limiter && false === $limiter->consume()->isAccepted()) { diff --git a/src/Service/PostCommentManager.php b/src/Service/PostCommentManager.php index 2c9f9b6f5..2f5e4f2e5 100644 --- a/src/Service/PostCommentManager.php +++ b/src/Service/PostCommentManager.php @@ -57,7 +57,7 @@ public function create(PostCommentDto $dto, User $user, $rateLimit = true): Post if (!$user->apId) { $user->ip = $dto->ip; } - + if ($rateLimit) { $limiter = $this->postCommentLimiter->create($dto->ip); if ($limiter && false === $limiter->consume()->isAccepted()) { From 639fc2dba3ce2eae66e7840e527fdf10c7e2ffd4 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:47:15 -0500 Subject: [PATCH 03/15] add trusted proxy config --- .env.example | 5 +++++ .env.example_docker | 5 +++++ config/packages/framework.yaml | 2 ++ 3 files changed, 12 insertions(+) diff --git a/.env.example b/.env.example index c76c6ccd8..1123cd94a 100644 --- a/.env.example +++ b/.env.example @@ -32,6 +32,11 @@ KBIN_HEADER_LOGO=false KBIN_FEDERATION_PAGE_ENABLED=true MBIN_DEFAULT_THEME=default +# If you are running Mbin behind a reverse proxy be sure to adjust the proxy address/range below +# to your server's IP address if it does not already fall within the private IP spaces specified. +#TRUSTED_PROXIES=::1,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 +TRUSTED_PROXIES= + # Max image filesize (in bytes) # This should be set to <= `upload_max_filesize` and `post_max_size` in the server's php.ini file MAX_IMAGE_BYTES=6000000 diff --git a/.env.example_docker b/.env.example_docker index 36f0b811b..0bd7cf320 100644 --- a/.env.example_docker +++ b/.env.example_docker @@ -32,6 +32,11 @@ KBIN_HEADER_LOGO=false KBIN_FEDERATION_PAGE_ENABLED=true MBIN_DEFAULT_THEME=default +# If you are running Mbin behind a reverse proxy be sure to adjust the proxy address/range below +# to your server's IP address if it does not already fall within the private IP spaces specified. +#TRUSTED_PROXIES=::1,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 +TRUSTED_PROXIES= + # Max image filesize (in bytes) # This should be set to <= `upload_max_filesize` and `post_max_size` in the server's php.ini file MAX_IMAGE_BYTES=6000000 diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 6a4cbd2b9..391606515 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -4,6 +4,8 @@ framework: annotations: false #no longer supported http_method_override: false handle_all_throwables: true + trusted_proxies: '%env(string:default::TRUSTED_PROXIES)%' + trusted_headers: ['x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix'] # Enables session support. Note that the session will ONLY be started if you read or write from it. # Remove or comment this section to explicitly disable session support. From 72d0c85c57550260f216c08b966cfddd35ec4698 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:53:46 -0500 Subject: [PATCH 04/15] adjust env comment wording --- .env.example | 2 +- .env.example_docker | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 1123cd94a..7aa3d6a92 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ KBIN_HEADER_LOGO=false KBIN_FEDERATION_PAGE_ENABLED=true MBIN_DEFAULT_THEME=default -# If you are running Mbin behind a reverse proxy be sure to adjust the proxy address/range below +# If you are running Mbin behind a reverse proxy, uncomment the line below and adjust the proxy address/range below # to your server's IP address if it does not already fall within the private IP spaces specified. #TRUSTED_PROXIES=::1,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 TRUSTED_PROXIES= diff --git a/.env.example_docker b/.env.example_docker index 0bd7cf320..8bb786abd 100644 --- a/.env.example_docker +++ b/.env.example_docker @@ -32,7 +32,7 @@ KBIN_HEADER_LOGO=false KBIN_FEDERATION_PAGE_ENABLED=true MBIN_DEFAULT_THEME=default -# If you are running Mbin behind a reverse proxy be sure to adjust the proxy address/range below +# If you are running Mbin behind a reverse proxy, uncomment the line below and adjust the proxy address/range below # to your server's IP address if it does not already fall within the private IP spaces specified. #TRUSTED_PROXIES=::1,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 TRUSTED_PROXIES= From 0457d4d6a52ba732ad7b20af590a829f90e35396 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:04:09 -0500 Subject: [PATCH 05/15] add comment edit ip updates --- src/Service/EntryCommentManager.php | 4 ++++ src/Service/PostCommentManager.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Service/EntryCommentManager.php b/src/Service/EntryCommentManager.php index 78acc80c4..eaa5c9119 100644 --- a/src/Service/EntryCommentManager.php +++ b/src/Service/EntryCommentManager.php @@ -118,6 +118,10 @@ public function canUserEditComment(EntryComment $comment, User $user): bool public function edit(EntryComment $comment, EntryCommentDto $dto, ?User $editedByUser = null): EntryComment { + if (null !== $editedByUser && !$editedByUser->apId) { + $editedByUser->ip = $dto->ip; + } + Assert::same($comment->entry->getId(), $dto->entry->getId()); $comment->body = $dto->body; diff --git a/src/Service/PostCommentManager.php b/src/Service/PostCommentManager.php index 2f5e4f2e5..659c86118 100644 --- a/src/Service/PostCommentManager.php +++ b/src/Service/PostCommentManager.php @@ -126,6 +126,10 @@ public function canUserEditPostComment(PostComment $postComment, User $user): bo */ public function edit(PostComment $comment, PostCommentDto $dto, ?User $editedBy = null): PostComment { + if (null !== $editedBy && !$editedBy->apId) { + $editedBy->ip = $dto->ip; + } + Assert::same($comment->post->getId(), $dto->post->getId()); $comment->body = $dto->body; From 9c0791a1dee2e0cd418e6cedcaaaadadc560e08f Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:31:44 -0500 Subject: [PATCH 06/15] add missing ip resolve on entry and post comment edits --- .../Entry/Comment/EntryCommentEditController.php | 3 +++ src/Controller/Post/PostEditController.php | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Controller/Entry/Comment/EntryCommentEditController.php b/src/Controller/Entry/Comment/EntryCommentEditController.php index 4d9c03763..82ecf17bf 100644 --- a/src/Controller/Entry/Comment/EntryCommentEditController.php +++ b/src/Controller/Entry/Comment/EntryCommentEditController.php @@ -13,6 +13,7 @@ use App\PageView\EntryCommentPageView; use App\Repository\EntryCommentRepository; use App\Service\EntryCommentManager; +use App\Service\IpResolver; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; @@ -27,6 +28,7 @@ class EntryCommentEditController extends AbstractController public function __construct( private readonly EntryCommentManager $manager, private readonly EntryCommentRepository $repository, + private readonly IpResolver $ipResolver ) { } @@ -44,6 +46,7 @@ public function __invoke( $dto = $this->manager->createDto($comment); $form = $this->getForm($dto, $comment); + $dto->ip = $this->ipResolver->resolve(); try { // Could thrown an error on event handlers (eg. onPostSubmit if a user upload an incorrect image) $form->handleRequest($request); diff --git a/src/Controller/Post/PostEditController.php b/src/Controller/Post/PostEditController.php index c4e627e5e..b920d0398 100644 --- a/src/Controller/Post/PostEditController.php +++ b/src/Controller/Post/PostEditController.php @@ -11,6 +11,7 @@ use App\PageView\PostCommentPageView; use App\Repository\PostCommentRepository; use App\Service\PostManager; +use App\Service\IpResolver; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -20,8 +21,10 @@ class PostEditController extends AbstractController { - public function __construct(private readonly PostManager $manager) - { + public function __construct( + private readonly PostManager $manager, + private readonly IpResolver $ipResolver + ) { } #[IsGranted('ROLE_USER')] @@ -37,6 +40,7 @@ public function __invoke( $dto = $this->manager->createDto($post); $form = $this->createForm(PostType::class, $dto); + $dto->ip = $this->ipResolver->resolve(); try { // Could thrown an error on event handlers (eg. onPostSubmit if a user upload an incorrect image) $form->handleRequest($request); From df027b3a205e4094c0126a7f0153ae14f85b2c69 Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:32:15 -0500 Subject: [PATCH 07/15] fix lint --- src/Controller/Post/PostEditController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Post/PostEditController.php b/src/Controller/Post/PostEditController.php index b920d0398..fa0e3a2f7 100644 --- a/src/Controller/Post/PostEditController.php +++ b/src/Controller/Post/PostEditController.php @@ -10,8 +10,8 @@ use App\Form\PostType; use App\PageView\PostCommentPageView; use App\Repository\PostCommentRepository; -use App\Service\PostManager; use App\Service\IpResolver; +use App\Service\PostManager; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; From 0de0856b9628c34627ae350056db23737600e6ec Mon Sep 17 00:00:00 2001 From: nobodyatroot <35878315+nobodyatroot@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:05:57 -0500 Subject: [PATCH 08/15] add email and ip info to user page for admins --- templates/user/_info.html.twig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/templates/user/_info.html.twig b/templates/user/_info.html.twig index d5d64430c..26a337b1d 100644 --- a/templates/user/_info.html.twig +++ b/templates/user/_info.html.twig @@ -23,6 +23,13 @@ {{ component('user_actions', {user: user}) }} {% endif %}