[FEATURE] Trigger execution of a specific task from CLI
[Packages/TYPO3.CMS.git] / typo3 / sysext / sv / class.tx_sv_auth.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2004-2011 René Fritz <r.fritz@colorcube.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
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 * Service 'User authentication' for the 'sv' extension.
29 *
30 * @author René Fritz <r.fritz@colorcube.de>
31 */
32
33 /**
34 * Authentication services class
35 *
36 * @author René Fritz <r.fritz@colorcube.de>
37 * @package TYPO3
38 * @subpackage tx_sv
39 */
40 class tx_sv_auth extends tx_sv_authbase {
41
42 /**
43 * Process the submitted credentials.
44 * In this case hash the clear text password if it has been submitted.
45 *
46 * @param array $loginData Credentials that are submitted and potentially modified by other services
47 * @param string $passwordTransmissionStrategy Keyword of how the password has been hashed or encrypted before submission
48 * @return bool
49 */
50 public function processLoginData(array &$loginData, $passwordTransmissionStrategy) {
51 $isProcessed = TRUE;
52
53 // Processing data according to the state it was submitted in.
54 switch ($passwordTransmissionStrategy) {
55 case 'normal':
56 $loginData['uident_text'] = $loginData['uident'];
57 break;
58 case 'challenged':
59 $loginData['uident_text'] = '';
60 $loginData['uident_challenged'] = $loginData['uident'];
61 $loginData['uident_superchallenged'] = '';
62 break;
63 case 'superchallenged':
64 $loginData['uident_text'] = '';
65 $loginData['uident_challenged'] = '';
66 $loginData['uident_superchallenged'] = $loginData['uident'];
67 break;
68 default:
69 $isProcessed = FALSE;
70 }
71
72 if (!empty($loginData['uident_text'])) {
73 $loginData['uident_challenged'] = (string) md5($loginData['uname'] . ':' . $loginData['uident_text'] . ':' . $loginData['chalvalue']);
74 $loginData['uident_superchallenged'] = (string) md5($loginData['uname'] . ':' . (md5($loginData['uident_text'])) . ':' . $loginData['chalvalue']);
75
76 $this->processOriginalPasswordValue($loginData);
77
78 $isProcessed = TRUE;
79 }
80
81 return $isProcessed;
82 }
83
84 /**
85 * This method ensures backwards compatibility of the processed loginData
86 * with older TYPO3 versions.
87 * Starting with TYPO3 6.1 $loginData['uident'] will always contain the raw
88 * value of the submitted password field and will not be processed any further.
89 *
90 * @param array $loginData
91 * @deprecated will be removed with 6.1
92 */
93 protected function processOriginalPasswordValue(&$loginData) {
94 if ($this->authInfo['security_level'] === 'superchallenged') {
95 $loginData['uident'] = $loginData['uident_superchallenged'];
96 } elseif ($this->authInfo['security_level'] === 'challenged') {
97 $loginData['uident'] = $loginData['uident_challenged'];
98 }
99 }
100
101 /**
102 * Find a user (eg. look up the user record in database when a login is sent)
103 *
104 * @return mixed User array or FALSE
105 */
106 function getUser() {
107 $user = FALSE;
108
109 if ($this->login['status'] == 'login') {
110 if ($this->login['uident']) {
111
112 $user = $this->fetchUserRecord($this->login['uname']);
113
114 if(!is_array($user)) {
115 // Failed login attempt (no username found)
116 $this->writelog(255, 3, 3, 2,
117 'Login-attempt from %s (%s), username \'%s\' not found!!',
118 array($this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname'])
119 ); // Logout written to log
120 t3lib_div::sysLog(
121 sprintf(
122 'Login-attempt from %s (%s), username \'%s\' not found!',
123 $this->authInfo['REMOTE_ADDR'],
124 $this->authInfo['REMOTE_HOST'],
125 $this->login['uname']
126 ),
127 'Core',
128 t3lib_div::SYSLOG_SEVERITY_WARNING
129 );
130 } else {
131 if ($this->writeDevLog) {
132 t3lib_div::devLog(
133 'User found: ' . t3lib_div::arrayToLogString(
134 $user, array($this->db_user['userid_column'], $this->db_user['username_column'])
135 ),
136 'tx_sv_auth'
137 );
138 }
139 }
140 } else {
141 // Failed Login attempt (no password given)
142 $this->writelog(255, 3, 3, 2,
143 'Login-attempt from %s (%s) for username \'%s\' with an empty password!',
144 array($this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname'])
145 );
146 t3lib_div::sysLog(
147 sprintf(
148 'Login-attempt from %s (%s), for username \'%s\' with an empty password!',
149 $this->authInfo['REMOTE_ADDR'],
150 $this->authInfo['REMOTE_HOST'],
151 $this->login['uname']
152 ),
153 'Core',
154 t3lib_div::SYSLOG_SEVERITY_WARNING
155 );
156 }
157 }
158 return $user;
159 }
160
161 /**
162 * Authenticate a user (Check various conditions for the user that might invalidate its authentication, eg. password match, domain, IP, etc.)
163 *
164 * @param array $user Data of user.
165 * @return boolean
166 */
167 public function authUser(array $user) {
168 $OK = 100;
169
170 if ($this->login['uident'] && $this->login['uname']) {
171
172 // Checking password match for user:
173 $OK = $this->compareUident($user, $this->login);
174
175 if (!$OK) {
176 // Failed login attempt (wrong password) - write that to the log!
177 if ($this->writeAttemptLog) {
178 $this->writelog(255, 3, 3, 1, "Login-attempt from %s (%s), username '%s', password not accepted!", Array($this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname']));
179 t3lib_div::sysLog(
180 sprintf( "Login-attempt from %s (%s), username '%s', password not accepted!", $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname'] ),
181 'Core',
182 t3lib_div::SYSLOG_SEVERITY_WARNING
183 );
184 }
185 if ($this->writeDevLog) {
186 t3lib_div::devLog('Password not accepted: '.$this->login['uident'], 'tx_sv_auth', 2);
187 }
188 }
189
190 // Checking the domain (lockToDomain)
191 if ($OK && $user['lockToDomain'] && $user['lockToDomain']!=$this->authInfo['HTTP_HOST']) {
192 // Lock domain didn't match, so error:
193 if ($this->writeAttemptLog) {
194 $this->writelog(255, 3, 3, 1, "Login-attempt from %s (%s), username '%s', locked domain '%s' did not match '%s'!", Array($this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $user[$this->db_user['username_column']], $user['lockToDomain'], $this->authInfo['HTTP_HOST']));
195 t3lib_div::sysLog(
196 sprintf( "Login-attempt from %s (%s), username '%s', locked domain '%s' did not match '%s'!", $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $user[$this->db_user['username_column']], $user['lockToDomain'], $this->authInfo['HTTP_HOST'] ),
197 'Core',
198 t3lib_div::SYSLOG_SEVERITY_WARNING
199 );
200 }
201 $OK = FALSE;
202 }
203 }
204
205 return $OK;
206 }
207
208 /**
209 * Find usergroup records, currently only for frontend
210 *
211 * @param array $user Data of user.
212 * @param array $knownGroups Group data array of already known groups. This is handy if you want select other related groups. Keys in this array are unique IDs of those groups.
213 * @return mixed Groups array, keys = uid which must be unique
214 */
215 function getGroups($user, $knownGroups) {
216 global $TYPO3_CONF_VARS;
217
218 $groupDataArr = array();
219
220 if($this->mode == 'getGroupsFE') {
221
222 $groups = array();
223 if (is_array($user) && $user[$this->db_user['usergroup_column']]) {
224 $groupList = $user[$this->db_user['usergroup_column']];
225 $groups = array();
226 $this->getSubGroups($groupList, '', $groups);
227 }
228
229 // ADD group-numbers if the IPmask matches.
230 if (is_array($TYPO3_CONF_VARS['FE']['IPmaskMountGroups'])) {
231 foreach ($TYPO3_CONF_VARS['FE']['IPmaskMountGroups'] as $IPel) {
232 if ($this->authInfo['REMOTE_ADDR'] && $IPel[0] && t3lib_div::cmpIP($this->authInfo['REMOTE_ADDR'], $IPel[0])) {$groups[]=intval($IPel[1]);}
233 }
234 }
235
236 $groups = array_unique($groups);
237
238 if (count($groups)) {
239 $list = implode(',', $groups);
240
241 if ($this->writeDevLog) {
242 t3lib_div::devLog('Get usergroups with id: '.$list, 'tx_sv_auth');
243 }
244
245 $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=\''.$this->authInfo['HTTP_HOST'].'\')';
246 if (!$this->authInfo['showHiddenRecords']) {
247 $hiddenP = 'AND hidden=0 ';
248 }
249 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->db_groups['table'], 'deleted=0 '.$hiddenP.' AND uid IN ('.$list.')'.$lockToDomain_SQL);
250 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
251 $groupDataArr[$row['uid']] = $row;
252 }
253 if ($res) {
254 $GLOBALS['TYPO3_DB']->sql_free_result($res);
255 }
256
257 } else {
258 if ($this->writeDevLog) {
259 t3lib_div::devLog('No usergroups found.', 'tx_sv_auth', 2);
260 }
261 }
262 } elseif ($this->mode == 'getGroupsBE') {
263
264 // Get the BE groups here.
265 // Still needs to be implemented in t3lib_beUserAuth
266 }
267
268 return $groupDataArr;
269 }
270
271 /**
272 * Fetches subgroups of groups. Function is called recursively for each subgroup.
273 * Function was previously copied from t3lib_beUserAuth->fetchGroups and has been slightly modified.
274 *
275 * @param string $grList Commalist of fe_groups uid numbers
276 * @param string $idList List of already processed fe_groups-uids so the function will not fall into a eternal recursion.
277 * @param array $groups
278 * @return array
279 * @access private
280 */
281 function getSubGroups($grList, $idList = '', &$groups) {
282
283 // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
284 $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=\''.$this->authInfo['HTTP_HOST'].'\')';
285 if (!$this->authInfo['showHiddenRecords']) {
286 $hiddenP = 'AND hidden=0 ';
287 }
288 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,subgroup', 'fe_groups', 'deleted=0 '.$hiddenP.' AND uid IN ('.$grList.')'.$lockToDomain_SQL);
289 // Internal group record storage
290 $groupRows = array();
291
292 // The groups array is filled
293 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
294 if(!in_array($row['uid'], $groups)) {
295 $groups[] = $row['uid'];
296 }
297 $groupRows[$row['uid']] = $row;
298 }
299
300 // Traversing records in the correct order
301 $include_staticArr = t3lib_div::intExplode(',', $grList);
302 // traversing list
303 foreach ($include_staticArr as $uid) {
304
305 // Get row:
306 $row = $groupRows[$uid];
307 // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
308 if (is_array($row) && !t3lib_div::inList($idList, $uid)) {
309
310 // Include sub groups
311 if (trim($row['subgroup'])) {
312 // Make integer list
313 $theList = implode(',', t3lib_div::intExplode(',', $row['subgroup']));
314 // Call recursively, pass along list of already processed groups so they are not recursed again.
315 $this->getSubGroups($theList, $idList.','.$uid, $groups);
316 }
317 }
318 }
319 }
320 }
321 ?>