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