[TASK] FormEngine JS refactoring: copy JS files
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / FormProtection / BackendFormProtection.php
1 <?php
2 namespace TYPO3\CMS\Core\FormProtection;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * This class provides protection against cross-site request forgery (XSRF/CSRF)
19 * for forms in the BE.
20 *
21 * How to use:
22 *
23 * For each form in the BE (or link that changes some data), create a token and
24 * insert is as a hidden form element. The name of the form element does not
25 * matter; you only need it to get the form token for verifying it.
26 *
27 * <pre>
28 * $formToken = TYPO3\CMS\Core\FormProtection\BackendFormProtectionFactory::get()
29 * ->generateToken(
30 * 'BE user setup', 'edit'
31 * );
32 * $this->content .= '<input type="hidden" name="formToken" value="' .
33 * $formToken . '" />';
34 * </pre>
35 *
36 * The three parameters $formName, $action and $formInstanceName can be
37 * arbitrary strings, but they should make the form token as specific as
38 * possible. For different forms (e.g. BE user setup and editing a tt_content
39 * record) or different records (with different UIDs) from the same table,
40 * those values should be different.
41 *
42 * For editing a tt_content record, the call could look like this:
43 *
44 * <pre>
45 * $formToken = \TYPO3\CMS\Core\FormProtection\BackendFormProtectionFactory::get()
46 * ->getFormProtection()->generateToken(
47 * 'tt_content', 'edit', $uid
48 * );
49 * </pre>
50 *
51 *
52 * When processing the data that has been submitted by the form, you can check
53 * that the form token is valid like this:
54 *
55 * <pre>
56 * if ($dataHasBeenSubmitted && TYPO3\CMS\Core\FormProtection\BackendFormProtectionFactory::get()
57 * ->validateToken(
58 * \TYPO3\CMS\Core\Utility\GeneralUtility::_POST('formToken'),
59 * 'BE user setup', 'edit
60 * )
61 * ) {
62 * processes the data
63 * } else {
64 * no need to do anything here as the BE form protection will create a
65 * flash message for an invalid token
66 * }
67 * </pre>
68 */
69 use TYPO3\CMS\Core\Messaging\FlashMessageService;
70
71 /**
72 * Backend form protection
73 *
74 * @author Oliver Klee <typo3-coding@oliverklee.de>
75 * @author Helmut Hummel <helmut.hummel@typo3.org>
76 */
77 class BackendFormProtection extends AbstractFormProtection {
78
79 /**
80 * Keeps the instance of the user which existed during creation
81 * of the object.
82 *
83 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
84 */
85 protected $backendUser;
86
87 /**
88 * Instance of the registry, which is used to permanently persist
89 * the session token so that it can be restored during re-login.
90 *
91 * @var \TYPO3\CMS\Core\Registry
92 */
93 protected $registry;
94
95 /**
96 * Only allow construction if we have a backend session
97 *
98 * @throws \TYPO3\CMS\Core\Error\Exception
99 */
100 public function __construct() {
101 if (!$this->isAuthorizedBackendSession()) {
102 throw new \TYPO3\CMS\Core\Error\Exception('A back-end form protection may only be instantiated if there' . ' is an active back-end session.', 1285067843);
103 }
104 $this->backendUser = $GLOBALS['BE_USER'];
105 }
106
107 /**
108 * Creates or displays an error message telling the user that the submitted
109 * form token is invalid.
110 *
111 * @return void
112 */
113 protected function createValidationErrorMessage() {
114 /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
115 $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
116 \TYPO3\CMS\Core\Messaging\FlashMessage::class,
117 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:error.formProtection.tokenInvalid'),
118 '',
119 \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR,
120 !$this->isAjaxRequest()
121 );
122 /** @var $flashMessageService FlashMessageService */
123 $flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(FlashMessageService::class);
124
125 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
126 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
127 $defaultFlashMessageQueue->enqueue($flashMessage);
128 }
129
130 /**
131 * Checks if the current request is an Ajax request
132 *
133 * @return bool
134 */
135 protected function isAjaxRequest() {
136 return (bool)(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX);
137 }
138
139 /**
140 * Retrieves the saved session token or generates a new one.
141 *
142 * @return string
143 */
144 protected function retrieveSessionToken() {
145 $this->sessionToken = $this->backendUser->getSessionData('formSessionToken');
146 if (empty($this->sessionToken)) {
147 $this->sessionToken = $this->generateSessionToken();
148 $this->persistSessionToken();
149 }
150 return $this->sessionToken;
151 }
152
153 /**
154 * Saves the tokens so that they can be used by a later incarnation of this
155 * class.
156 *
157 * @access private
158 * @return void
159 */
160 public function persistSessionToken() {
161 $this->backendUser->setAndSaveSessionData('formSessionToken', $this->sessionToken);
162 }
163
164 /**
165 * Sets the session token for the user from the registry
166 * and returns it additionally.
167 *
168 * @access private
169 * @return string
170 * @throws \UnexpectedValueException
171 */
172 public function setSessionTokenFromRegistry() {
173 $this->sessionToken = $this->getRegistry()->get('core', 'formSessionToken:' . $this->backendUser->user['uid']);
174 if (empty($this->sessionToken)) {
175 throw new \UnexpectedValueException('Failed to restore the session token from the registry.', 1301827270);
176 }
177 return $this->sessionToken;
178 }
179
180 /**
181 * Stores the session token in the registry to have it
182 * available during re-login of the user.
183 *
184 * @access private
185 * @return void
186 */
187 public function storeSessionTokenInRegistry() {
188 $this->getRegistry()->set('core', 'formSessionToken:' . $this->backendUser->user['uid'], $this->getSessionToken());
189 }
190
191 /**
192 * Removes the session token for the user from the registry.
193 *
194 * @access private
195 */
196 public function removeSessionTokenFromRegistry() {
197 $this->getRegistry()->remove('core', 'formSessionToken:' . $this->backendUser->user['uid']);
198 }
199
200 /**
201 * Returns the instance of the registry.
202 *
203 * @return \TYPO3\CMS\Core\Registry
204 */
205 protected function getRegistry() {
206 if (!$this->registry instanceof \TYPO3\CMS\Core\Registry) {
207 $this->registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Registry::class);
208 }
209 return $this->registry;
210 }
211
212 /**
213 * Inject the registry. Currently only used in unit tests.
214 *
215 * @access private
216 * @param \TYPO3\CMS\Core\Registry $registry
217 * @return void
218 */
219 public function injectRegistry(\TYPO3\CMS\Core\Registry $registry) {
220 $this->registry = $registry;
221 }
222
223 /**
224 * Checks if a user is logged in and the session is active.
225 *
226 * @return bool
227 */
228 protected function isAuthorizedBackendSession() {
229 return isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof \TYPO3\CMS\Core\Authentication\BackendUserAuthentication && isset($GLOBALS['BE_USER']->user['uid']);
230 }
231
232 /**
233 * Return language service instance
234 *
235 * @return \TYPO3\CMS\Lang\LanguageService
236 */
237 protected function getLanguageService() {
238 return $GLOBALS['LANG'];
239 }
240
241 }