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