Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow admins to view user IP addresses in the admin panel users table and users page #1009

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions migrations/Version20240808230919.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240808230919 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add user IP column';
}

public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE "user" ADD ip VARCHAR(255) DEFAULT NULL');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE "user" DROP ip');
}
}
3 changes: 3 additions & 0 deletions src/Controller/Entry/Comment/EntryCommentEditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,7 @@ class EntryCommentEditController extends AbstractController
public function __construct(
private readonly EntryCommentManager $manager,
private readonly EntryCommentRepository $repository,
private readonly IpResolver $ipResolver
) {
}

Expand All @@ -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);
Expand Down
8 changes: 6 additions & 2 deletions src/Controller/Post/PostEditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Form\PostType;
use App\PageView\PostCommentPageView;
use App\Repository\PostCommentRepository;
use App\Service\IpResolver;
use App\Service\PostManager;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand All @@ -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')]
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -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])]
Expand Down
3 changes: 3 additions & 0 deletions src/Security/GithubAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
) {
Expand Down Expand Up @@ -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());
Expand Down
7 changes: 7 additions & 0 deletions src/Security/UserChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace App\Security;

use App\Entity\User as AppUser;
use App\Service\IpResolver;
use App\Service\UserManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
Expand All @@ -18,6 +20,8 @@ class UserChecker implements UserCheckerInterface
public function __construct(
private readonly TranslatorInterface $translator,
private readonly UrlGeneratorInterface $urlGenerator,
private readonly EntityManagerInterface $entityManager,
private readonly IpResolver $ipResolver,
private readonly UserManager $userManager
) {
}
Expand Down Expand Up @@ -55,5 +59,8 @@ public function checkPostAuth(UserInterface $user): void
if (!$user instanceof AppUser) {
return;
}

$user->ip = $this->ipResolver->resolve();
$this->entityManager->flush();
}
}
8 changes: 8 additions & 0 deletions src/Service/EntryCommentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -114,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;
Expand Down
8 changes: 8 additions & 0 deletions src/Service/EntryManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions src/Service/PostCommentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -122,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;
Expand Down
8 changes: 8 additions & 0 deletions src/Service/PostManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 8 additions & 2 deletions templates/admin/users.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
<thead>
<tr>
<th>{{ 'username'|trans }}</th>
<th>{{ 'email'|trans }}</th>
{% if withFederated is not defined or withFederated is same as false %}
<th>{{ 'email'|trans }}</th>
<th>{{ 'IP' }}</th>
{% endif %}
<th>{{ 'created_at'|trans }}</th>
<th>{{ 'last_active'|trans }}</th>
</tr>
Expand All @@ -79,7 +82,10 @@
{% for user in users %}
<tr>
<td>{{ component('user_inline', {user: user}) }}</td>
<td>{{ user.apId ? '-' : user.email }}</td>
{% if withFederated is not defined or withFederated is same as false %}
<td>{{ user.email }}</td>
<td>{{ user.ip }}</td>
{% endif %}
<td>{{ component('date', {date: user.createdAt}) }}</td>
<td>{{ component('date', {date: user.lastActive}) }}</td>
</tr>
Expand Down
5 changes: 5 additions & 0 deletions templates/user/_info.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
{{ component('user_actions', {user: user}) }}
{% endif %}
<ul class="info">
{% if app.user is defined and app.user is not null and app.user.admin() and user.apId is null %}
<li>{{ 'IP' }}: <span>{{ user.ip }}</span></li>
<li>{{ 'email'|trans }}: <span>{{ user.email }}</span></li>
{% endif %}

<li>{{ 'joined'|trans }}: {{ component('date', {date: user.createdAt}) }}</li>
<li>{{ 'cake_day'|trans }}: <div><i class="fa-solid fa-cake" aria-hidden="true"></i> <span>{{ user.createdAt|format_date('short', '', null, 'gregorian', mbin_lang()) }}</span></div></li>
{% if app.user is defined and app.user is not null and app.user.admin() and user.apId is not null %}
Expand Down
Loading