Skip to content

Commit a515aa0

Browse files
authored
Merge pull request #270 from dotkernel/issue-184
Issue #184: Implemented `CSRF` protection in all forms
2 parents e02bb2d + 03ca315 commit a515aa0

35 files changed

+324
-195
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"laminas/laminas-config-aggregator": "^1.14.0",
4949
"laminas/laminas-i18n": "^2.26.0",
5050
"laminas/laminas-math": "^3.7.0",
51+
"laminas/laminas-session": "^2.21.0",
5152
"mezzio/mezzio": "^3.18.0",
5253
"mezzio/mezzio-authorization-rbac": "^1.7.0",
5354
"mezzio/mezzio-cors": "^1.11.1",

public/css/app.css

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/js/app.js

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Admin/src/Controller/AdminController.php

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
use Psr\Http\Message\ResponseInterface;
3939
use Throwable;
4040

41+
use function assert;
42+
4143
class AdminController extends AbstractActionController
4244
{
4345
use ServerRequestAwareTrait;
@@ -162,42 +164,54 @@ public function editAction(): ResponseInterface
162164

163165
public function deleteAction(): ResponseInterface
164166
{
165-
if (! $this->isPost()) {
166-
return new JsonResponse(
167-
['message' => Message::METHOD_NOT_ALLOWED],
168-
StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED
169-
);
170-
}
171-
172167
$uuid = $this->getAttribute('uuid');
173168
if (empty($uuid)) {
174169
return new JsonResponse(
175170
['message' => Message::ADMIN_NOT_FOUND],
176171
StatusCodeInterface::STATUS_NOT_FOUND
177172
);
178173
}
174+
$admin = $this->adminService->getAdminRepository()->findOneBy(['uuid' => $uuid]);
175+
assert($admin instanceof Admin);
179176

180177
$form = new AdminDeleteForm();
181-
$form->setData($this->getPostParams());
182-
if (! $form->isValid()) {
183-
return new JsonResponse(
184-
['message' => $this->forms->getMessages($form)],
185-
StatusCodeInterface::STATUS_BAD_REQUEST
186-
);
187-
}
178+
$form->setAttribute('id', 'deleteAdminForm');
179+
$form->setAttribute('method', RequestMethodInterface::METHOD_POST);
180+
$form->setAttribute(
181+
'action',
182+
$this->router->generateUri('admin', ['action' => 'delete', 'uuid' => $uuid])
183+
);
188184

189-
/** @var Admin $admin */
190-
$admin = $this->adminService->getAdminRepository()->findOneBy(['uuid' => $uuid]);
191-
try {
192-
$this->adminService->getAdminRepository()->deleteAdmin($admin);
193-
return new JsonResponse(['message' => Message::ADMIN_DELETED_SUCCESSFULLY]);
194-
} catch (Throwable $e) {
195-
$this->logErrors($e, Message::DELETE_ADMIN);
196-
return new JsonResponse(
197-
['message' => Message::AN_ERROR_OCCURRED],
198-
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR
199-
);
185+
if ($this->isPost()) {
186+
$form->setData($this->getPostParams());
187+
if (! $form->isValid()) {
188+
return new JsonResponse(
189+
['message' => $this->forms->getMessages($form)],
190+
StatusCodeInterface::STATUS_BAD_REQUEST
191+
);
192+
}
193+
194+
try {
195+
$this->adminService->getAdminRepository()->deleteAdmin($admin);
196+
return new JsonResponse(['message' => Message::ADMIN_DELETED_SUCCESSFULLY]);
197+
} catch (Throwable $e) {
198+
$this->logErrors($e, Message::DELETE_ADMIN);
199+
return new JsonResponse(
200+
['message' => Message::AN_ERROR_OCCURRED],
201+
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR
202+
);
203+
}
200204
}
205+
206+
return new JsonResponse([
207+
'data' => $this->template->render(
208+
'admin::delete',
209+
[
210+
'admin' => $admin,
211+
'form' => $form->prepare(),
212+
]
213+
),
214+
]);
201215
}
202216

203217
public function listAction(): ResponseInterface
@@ -216,9 +230,7 @@ public function listAction(): ResponseInterface
216230
public function manageAction(): ResponseInterface
217231
{
218232
return new HtmlResponse(
219-
$this->template->render('admin::list', [
220-
'form' => new AdminDeleteForm(),
221-
])
233+
$this->template->render('admin::list')
222234
);
223235
}
224236

src/Admin/src/Form/AccountForm.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
namespace Frontend\Admin\Form;
66

77
use Frontend\Admin\InputFilter\AccountInputFilter;
8+
use Laminas\Form\Element\Csrf;
89
use Laminas\Form\Form;
910
use Laminas\Form\FormInterface;
1011
use Laminas\InputFilter\InputFilterInterface;
12+
use Laminas\Session\Container;
1113

1214
/** @template-extends Form<FormInterface> */
1315
class AccountForm extends Form
@@ -59,15 +61,6 @@ public function init(): void
5961
],
6062
], ['priority' => -11]);
6163

62-
$this->add([
63-
'name' => 'account_csrf',
64-
'type' => 'csrf',
65-
'options' => [
66-
'timeout' => 3600,
67-
'message' => 'The form CSRF has expired and was refreshed. Please resend the form',
68-
],
69-
]);
70-
7164
$this->add([
7265
'name' => 'submit',
7366
'type' => 'submit',
@@ -76,6 +69,13 @@ public function init(): void
7669
'value' => 'Update account',
7770
],
7871
], ['priority' => -100]);
72+
73+
$this->add(new Csrf('accountCsrf', [
74+
'csrf_options' => [
75+
'timeout' => 3600,
76+
'session' => new Container(),
77+
],
78+
]));
7979
}
8080

8181
public function getInputFilter(): InputFilterInterface

src/Admin/src/Form/AdminDeleteForm.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
namespace Frontend\Admin\Form;
66

77
use Frontend\Admin\InputFilter\AdminDeleteInputFilter;
8+
use Laminas\Form\Element\Csrf;
89
use Laminas\Form\Form;
910
use Laminas\Form\FormInterface;
1011
use Laminas\InputFilter\InputFilterInterface;
12+
use Laminas\Session\Container;
1113

1214
/** @template-extends Form<FormInterface> */
1315
class AdminDeleteForm extends Form
@@ -63,6 +65,13 @@ public function init(): void
6365
'value' => 'Delete',
6466
],
6567
]);
68+
69+
$this->add(new Csrf('adminDeleteCsrf', [
70+
'csrf_options' => [
71+
'timeout' => 3600,
72+
'session' => new Container(),
73+
],
74+
]));
6675
}
6776

6877
public function getInputFilter(): InputFilterInterface

src/Admin/src/Form/AdminForm.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use Frontend\Admin\Entity\Admin;
88
use Frontend\Admin\InputFilter\AdminInputFilter;
9+
use Laminas\Form\Element\Csrf;
910
use Laminas\Form\Form;
1011
use Laminas\Form\FormInterface;
1112
use Laminas\InputFilter\InputFilterInterface;
13+
use Laminas\Session\Container;
1214

1315
/** @template-extends Form<FormInterface> */
1416
class AdminForm extends Form
@@ -111,6 +113,13 @@ public function init(): void
111113
],
112114
],
113115
], ['priority' => -30]);
116+
117+
$this->add(new Csrf('adminManageCsrf', [
118+
'csrf_options' => [
119+
'timeout' => 3600,
120+
'session' => new Container(),
121+
],
122+
]));
114123
}
115124

116125
public function getInputFilter(): InputFilterInterface

src/Admin/src/Form/ChangePasswordForm.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
namespace Frontend\Admin\Form;
66

77
use Frontend\Admin\InputFilter\ChangePasswordInputFilter;
8+
use Laminas\Form\Element\Csrf;
89
use Laminas\Form\Form;
910
use Laminas\Form\FormInterface;
1011
use Laminas\InputFilter\InputFilterInterface;
12+
use Laminas\Session\Container;
1113

1214
/** @template-extends Form<FormInterface> */
1315
class ChangePasswordForm extends Form
@@ -59,15 +61,6 @@ public function init(): void
5961
],
6062
]);
6163

62-
$this->add([
63-
'name' => 'change_password_csrf',
64-
'type' => 'csrf',
65-
'options' => [
66-
'timeout' => 3600,
67-
'message' => 'The form CSRF has expired and was refreshed. Please resend the form',
68-
],
69-
]);
70-
7164
$this->add([
7265
'name' => 'submit',
7366
'type' => 'submit',
@@ -76,6 +69,13 @@ public function init(): void
7669
'value' => 'Change Password',
7770
],
7871
], ['priority' => -100]);
72+
73+
$this->add(new Csrf('changePasswordCsrf', [
74+
'csrf_options' => [
75+
'timeout' => 3600,
76+
'session' => new Container(),
77+
],
78+
]));
7979
}
8080

8181
public function getInputFilter(): InputFilterInterface

src/Admin/src/Form/LoginForm.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
namespace Frontend\Admin\Form;
66

77
use Frontend\Admin\InputFilter\LoginInputFilter;
8+
use Laminas\Form\Element\Csrf;
89
use Laminas\Form\Element\Password;
910
use Laminas\Form\Element\Submit;
1011
use Laminas\Form\Element\Text;
1112
use Laminas\Form\Form;
1213
use Laminas\Form\FormInterface;
1314
use Laminas\InputFilter\InputFilterInterface;
15+
use Laminas\Session\Container;
1416

1517
/** @template-extends Form<FormInterface> */
1618
class LoginForm extends Form
@@ -66,6 +68,13 @@ public function init(): void
6668
],
6769
'type' => Submit::class,
6870
]);
71+
72+
$this->add(new Csrf('loginCsrf', [
73+
'csrf_options' => [
74+
'timeout' => 3600,
75+
'session' => new Container(),
76+
],
77+
]));
6978
}
7079

7180
public function getInputFilter(): InputFilterInterface

src/Admin/src/InputFilter/AccountInputFilter.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Frontend\Admin\InputFilter;
66

7+
use Frontend\App\InputFilter\Input\CsrfInput;
78
use Laminas\Filter\StringTrim;
89
use Laminas\InputFilter\Input;
910
use Laminas\InputFilter\InputFilter;
@@ -57,5 +58,7 @@ public function init(): void
5758
'message' => '<b>Last name</b> must be max 150 characters long.',
5859
]);
5960
$this->add($lastName);
61+
62+
$this->add(new CsrfInput('accountCsrf', true));
6063
}
6164
}

0 commit comments

Comments
 (0)