Skip to content

Commit ad52a60

Browse files
TatevikGrtatevikg1
andauthored
Blacklist (#156)
* blacklist controller * blacklist request * UserBlacklistNormalizer * add in development flags * phpcs fix * use blacklist branch * fix swagger * use core dev branch --------- Co-authored-by: Tatevik <[email protected]>
1 parent 3ca5387 commit ad52a60

File tree

7 files changed

+471
-1
lines changed

7 files changed

+471
-1
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
},
3737
"require": {
3838
"php": "^8.1",
39-
"phplist/core": "dev-main",
39+
"phplist/core": "dev-dev",
4040
"friendsofsymfony/rest-bundle": "*",
4141
"symfony/test-pack": "^1.0",
4242
"symfony/process": "^6.4",

config/services/normalizers.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,7 @@ services:
9393
PhpList\RestBundle\Statistics\Serializer\TopLocalPartsNormalizer:
9494
tags: [ 'serializer.normalizer' ]
9595
autowire: true
96+
97+
PhpList\RestBundle\Subscription\Serializer\UserBlacklistNormalizer:
98+
tags: [ 'serializer.normalizer' ]
99+
autowire: true
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Subscription\Controller;
6+
7+
use OpenApi\Attributes as OA;
8+
use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
9+
use PhpList\Core\Domain\Subscription\Service\Manager\SubscriberBlacklistManager;
10+
use PhpList\Core\Security\Authentication;
11+
use PhpList\RestBundle\Common\Controller\BaseController;
12+
use PhpList\RestBundle\Common\Validator\RequestValidator;
13+
use PhpList\RestBundle\Subscription\Request\AddToBlacklistRequest;
14+
use PhpList\RestBundle\Subscription\Serializer\UserBlacklistNormalizer;
15+
use Symfony\Component\HttpFoundation\JsonResponse;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\Response;
18+
use Symfony\Component\Routing\Attribute\Route;
19+
20+
/**
21+
* This controller provides REST API access to subscriber blacklist functionality.
22+
*/
23+
#[Route('/blacklist', name: 'blacklist_')]
24+
class BlacklistController extends BaseController
25+
{
26+
private SubscriberBlacklistManager $blacklistManager;
27+
private UserBlacklistNormalizer $normalizer;
28+
29+
public function __construct(
30+
Authentication $authentication,
31+
RequestValidator $validator,
32+
SubscriberBlacklistManager $blacklistManager,
33+
UserBlacklistNormalizer $normalizer,
34+
) {
35+
parent::__construct($authentication, $validator);
36+
$this->authentication = $authentication;
37+
$this->blacklistManager = $blacklistManager;
38+
$this->normalizer = $normalizer;
39+
}
40+
41+
#[Route('/check/{email}', name: 'check', methods: ['GET'])]
42+
#[OA\Get(
43+
path: '/api/v2/blacklist/check/{email}',
44+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production.',
45+
summary: 'Check if email is blacklisted',
46+
tags: ['blacklist'],
47+
parameters: [
48+
new OA\Parameter(
49+
name: 'php-auth-pw',
50+
description: 'Session key obtained from login',
51+
in: 'header',
52+
required: true,
53+
schema: new OA\Schema(type: 'string')
54+
),
55+
new OA\Parameter(
56+
name: 'email',
57+
description: 'Email address to check',
58+
in: 'path',
59+
required: true,
60+
schema: new OA\Schema(type: 'string')
61+
)
62+
],
63+
responses: [
64+
new OA\Response(
65+
response: 200,
66+
description: 'Success',
67+
content: new OA\JsonContent(
68+
properties: [
69+
new OA\Property(property: 'blacklisted', type: 'boolean'),
70+
new OA\Property(property: 'reason', type: 'string', nullable: true)
71+
]
72+
),
73+
),
74+
new OA\Response(
75+
response: 403,
76+
description: 'Failure',
77+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
78+
),
79+
]
80+
)]
81+
public function checkEmailBlacklisted(Request $request, string $email): JsonResponse
82+
{
83+
$admin = $this->requireAuthentication($request);
84+
if (!$admin->getPrivileges()->has(PrivilegeFlag::Subscribers)) {
85+
throw $this->createAccessDeniedException('You are not allowed to check blacklisted emails.');
86+
}
87+
88+
$isBlacklisted = $this->blacklistManager->isEmailBlacklisted($email);
89+
$reason = $isBlacklisted ? $this->blacklistManager->getBlacklistReason($email) : null;
90+
91+
return $this->json([
92+
'blacklisted' => $isBlacklisted,
93+
'reason' => $reason,
94+
]);
95+
}
96+
97+
#[Route('/add', name: 'add', methods: ['POST'])]
98+
#[OA\Post(
99+
path: '/api/v2/blacklist/add',
100+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production.',
101+
summary: 'Adds an email address to the blacklist.',
102+
requestBody: new OA\RequestBody(
103+
description: 'Email to blacklist',
104+
required: true,
105+
content: new OA\JsonContent(
106+
properties: [
107+
new OA\Property(property: 'email', type: 'string'),
108+
new OA\Property(property: 'reason', type: 'string', nullable: true)
109+
]
110+
)
111+
),
112+
tags: ['blacklist'],
113+
parameters: [
114+
new OA\Parameter(
115+
name: 'php-auth-pw',
116+
description: 'Session key obtained from login',
117+
in: 'header',
118+
required: true,
119+
schema: new OA\Schema(type: 'string')
120+
)
121+
],
122+
responses: [
123+
new OA\Response(
124+
response: 201,
125+
description: 'Success',
126+
content: new OA\JsonContent(
127+
properties: [
128+
new OA\Property(property: 'success', type: 'boolean'),
129+
new OA\Property(property: 'message', type: 'string')
130+
]
131+
),
132+
),
133+
new OA\Response(
134+
response: 403,
135+
description: 'Failure',
136+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
137+
),
138+
new OA\Response(
139+
response: 422,
140+
description: 'Failure',
141+
content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse')
142+
),
143+
]
144+
)]
145+
public function addEmailToBlacklist(Request $request): JsonResponse
146+
{
147+
$admin = $this->requireAuthentication($request);
148+
if (!$admin->getPrivileges()->has(PrivilegeFlag::Subscribers)) {
149+
throw $this->createAccessDeniedException('You are not allowed to add emails to blacklist.');
150+
}
151+
152+
/** @var AddToBlacklistRequest $definitionRequest */
153+
$definitionRequest = $this->validator->validate($request, AddToBlacklistRequest::class);
154+
155+
$userBlacklisted = $this->blacklistManager->addEmailToBlacklist(
156+
email: $definitionRequest->email,
157+
reasonData: $definitionRequest->reason
158+
);
159+
$json = $this->normalizer->normalize($userBlacklisted, 'json');
160+
161+
return $this->json($json, Response::HTTP_CREATED);
162+
}
163+
164+
#[Route('/remove/{email}', name: 'remove', methods: ['DELETE'])]
165+
#[OA\Delete(
166+
path: '/api/v2/blacklist/remove/{email}',
167+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production.',
168+
summary: 'Removes an email address from the blacklist.',
169+
tags: ['blacklist'],
170+
parameters: [
171+
new OA\Parameter(
172+
name: 'php-auth-pw',
173+
description: 'Session key obtained from login',
174+
in: 'header',
175+
required: true,
176+
schema: new OA\Schema(type: 'string')
177+
),
178+
new OA\Parameter(
179+
name: 'email',
180+
description: 'Email address to remove from blacklist',
181+
in: 'path',
182+
required: true,
183+
schema: new OA\Schema(type: 'string')
184+
)
185+
],
186+
responses: [
187+
new OA\Response(
188+
response: 200,
189+
description: 'Success',
190+
content: new OA\JsonContent(
191+
properties: [
192+
new OA\Property(property: 'success', type: 'boolean'),
193+
new OA\Property(property: 'message', type: 'string')
194+
]
195+
),
196+
),
197+
new OA\Response(
198+
response: 403,
199+
description: 'Failure',
200+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
201+
),
202+
]
203+
)]
204+
public function removeEmailFromBlacklist(Request $request, string $email): JsonResponse
205+
{
206+
$admin = $this->requireAuthentication($request);
207+
if (!$admin->getPrivileges()->has(PrivilegeFlag::Subscribers)) {
208+
throw $this->createAccessDeniedException('You are not allowed to remove emails from blacklist.');
209+
}
210+
211+
$this->blacklistManager->removeEmailFromBlacklist($email);
212+
213+
return $this->json(null, Response::HTTP_NO_CONTENT);
214+
}
215+
216+
#[Route('/info/{email}', name: 'info', methods: ['GET'])]
217+
#[OA\Get(
218+
path: '/api/v2/blacklist/info/{email}',
219+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production.',
220+
summary: 'Gets detailed information about a blacklisted email.',
221+
tags: ['blacklist'],
222+
parameters: [
223+
new OA\Parameter(
224+
name: 'php-auth-pw',
225+
description: 'Session key obtained from login',
226+
in: 'header',
227+
required: true,
228+
schema: new OA\Schema(type: 'string')
229+
),
230+
new OA\Parameter(
231+
name: 'email',
232+
description: 'Email address to get information for',
233+
in: 'path',
234+
required: true,
235+
schema: new OA\Schema(type: 'string')
236+
)
237+
],
238+
responses: [
239+
new OA\Response(
240+
response: 200,
241+
description: 'Success',
242+
content: new OA\JsonContent(
243+
properties: [
244+
new OA\Property(property: 'email', type: 'string'),
245+
new OA\Property(property: 'added', type: 'string', format: 'date-time', nullable: true),
246+
new OA\Property(property: 'reason', type: 'string', nullable: true)
247+
]
248+
),
249+
),
250+
new OA\Response(
251+
response: 403,
252+
description: 'Failure',
253+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
254+
),
255+
new OA\Response(
256+
response: 404,
257+
description: 'Failure',
258+
content: new OA\JsonContent(ref: '#/components/schemas/BadRequestResponse')
259+
),
260+
]
261+
)]
262+
public function getBlacklistInfo(Request $request, string $email): JsonResponse
263+
{
264+
$admin = $this->requireAuthentication($request);
265+
if (!$admin->getPrivileges()->has(PrivilegeFlag::Subscribers)) {
266+
throw $this->createAccessDeniedException('You are not allowed to view blacklist information.');
267+
}
268+
269+
$blacklistInfo = $this->blacklistManager->getBlacklistInfo($email);
270+
if (!$blacklistInfo) {
271+
return $this->json([
272+
'error' => sprintf('Email %s is not blacklisted', $email)
273+
], Response::HTTP_NOT_FOUND);
274+
}
275+
276+
$reason = $this->blacklistManager->getBlacklistReason($email);
277+
278+
return $this->json([
279+
'email' => $blacklistInfo->getEmail(),
280+
'added' => $blacklistInfo->getAdded()?->format('c'),
281+
'reason' => $reason,
282+
]);
283+
}
284+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Subscription\Request;
6+
7+
use PhpList\RestBundle\Common\Request\RequestInterface;
8+
use Symfony\Component\Validator\Constraints as Assert;
9+
10+
class AddToBlacklistRequest implements RequestInterface
11+
{
12+
#[Assert\NotBlank]
13+
#[Assert\Email]
14+
public string $email;
15+
16+
#[Assert\Type(type: 'string')]
17+
public ?string $reason = null;
18+
19+
public function getDto(): self
20+
{
21+
return $this;
22+
}
23+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Subscription\Serializer;
6+
7+
use PhpList\Core\Domain\Subscription\Model\UserBlacklist;
8+
use PhpList\Core\Domain\Subscription\Service\Manager\SubscriberBlacklistManager;
9+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
10+
11+
class UserBlacklistNormalizer implements NormalizerInterface
12+
{
13+
public function __construct(private readonly SubscriberBlacklistManager $blacklistManager)
14+
{
15+
}
16+
17+
/**
18+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
19+
*/
20+
public function normalize($object, string $format = null, array $context = []): array
21+
{
22+
if (!$object instanceof UserBlacklist) {
23+
return [];
24+
}
25+
26+
$reason = $this->blacklistManager->getBlacklistReason($object->getEmail());
27+
28+
return [
29+
'email' => $object->getEmail(),
30+
'added' => $object->getAdded()?->format('Y-m-d\TH:i:sP'),
31+
'reason' => $reason,
32+
];
33+
}
34+
35+
/**
36+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
37+
*/
38+
public function supportsNormalization($data, string $format = null): bool
39+
{
40+
return $data instanceof UserBlacklist;
41+
}
42+
}

0 commit comments

Comments
 (0)