c7cbdd74808cd29725c0e05dc923181512ed0377
[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('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:error.formProtection.tokenInvalid'), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR, !(isset($GLOBALS['TYPO3_AJAX']) && $GLOBALS['TYPO3_AJAX'] === TRUE));
124 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
125 $flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
126 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
127 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
128 $defaultFlashMessageQueue->enqueue($flashMessage);
129 }
130
131 /**
132 * Retrieves the saved session token or generates a new one.
133 *
134 * @return array<array>
135 */
136 protected function retrieveSessionToken() {
137 $this->sessionToken = $this->backendUser->getSessionData('formSessionToken');
138 if (empty($this->sessionToken)) {
139 $this->sessionToken = $this->generateSessionToken();
140 $this->persistSessionToken();
141 }
142 }
143
144 /**
145 * Saves the tokens so that they can be used by a later incarnation of this
146 * class.
147 *
148 * @access private
149 * @return void
150 */
151 public function persistSessionToken() {
152 $this->backendUser->setAndSaveSessionData('formSessionToken', $this->sessionToken);
153 }
154
155 /**
156 * Sets the session token for the user from the registry
157 * and returns it additionally.
158 *
159 * @access private
160 * @return string
161 */
162 public function setSessionTokenFromRegistry() {
163 $this->sessionToken = $this->getRegistry()->get('core', 'formSessionToken:' . $this->backendUser->user['uid']);
164 if (empty($this->sessionToken)) {
165 throw new \UnexpectedValueException('Failed to restore the session token from the registry.', 1301827270);
166 }
167 return $this->sessionToken;
168 }
169
170 /**
171 * Stores the session token in the registry to have it
172 * available during re-login of the user.
173 *
174 * @access private
175 * @return void
176 */
177 public function storeSessionTokenInRegistry() {
178 $this->getRegistry()->set('core', 'formSessionToken:' . $this->backendUser->user['uid'], $this->sessionToken);
179 }
180
181 /**
182 * Removes the session token for the user from the registry.
183 *
184 * @access private
185 * @return string
186 */
187 public function removeSessionTokenFromRegistry() {
188 return $this->getRegistry()->remove('core', 'formSessionToken:' . $this->backendUser->user['uid']);
189 }
190
191 /**
192 * Returns the instance of the registry.
193 *
194 * @return \TYPO3\CMS\Core\Registry
195 */
196 protected function getRegistry() {
197 if (!$this->registry instanceof \TYPO3\CMS\Core\Registry) {
198 $this->registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
199 }
200 return $this->registry;
201 }
202
203 /**
204 * Inject the registry. Currently only used in unit tests.
205 *
206 * @access private
207 * @param \TYPO3\CMS\Core\Registry $registry
208 * @return void
209 */
210 public function injectRegistry(\TYPO3\CMS\Core\Registry $registry) {
211 $this->registry = $registry;
212 }
213
214 /**
215 * Checks if a user is logged in and the session is active.
216 *
217 * @return boolean
218 */
219 protected function isAuthorizedBackendSession() {
220 return isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof \TYPO3\CMS\Core\Authentication\BackendUserAuthentication && isset($GLOBALS['BE_USER']->user['uid']);
221 }
222
223 }
224
225
226 ?>