source: trunk/Modules/User/UserModel.php@ 7

Last change on this file since 7 was 7, checked in by chronos, 5 years ago
  • Modified: Improved code formatting.
File size: 18.2 KB
Line 
1<?php
2
3define('LOGIN_USED', 'Přihlašovací jméno již použito.');
4define('NAME_USED', 'Jméno uživatele již použito');
5define('EMAIL_USED', 'Email je již použitý. Použijte jiný email nebo si můžete nechat zaslat nové heslo na email.');
6define('USER_REGISTRATED', 'Uživatel registrován. Na zadanou emailovou adresu byl poslán mail s odkazem pro aktivování účtu.');
7define('USER_REGISTRATION_CONFIRMED', 'Vaše registrace byla potvrzena.');
8define('DATA_MISSING', 'Chybí emailová adresa, přezdívka, nebo některé z hesel.');
9define('PASSWORDS_UNMATCHED', 'Hesla si neodpovídají.');
10define('ACCOUNT_LOCKED', 'Účet je uzamčen. Po registraci je nutné provést aktivaci účtu pomocí odkazu zaslaného v aktivačním emailu.');
11define('USER_NOT_LOGGED', 'Nejste přihlášen.');
12define('USER_LOGGED', 'Uživatel přihlášen.');
13define('USER_NOT_REGISTRED', 'Uživatel neregistrován.');
14define('USER_ALREADY_LOGGED', 'Uživatel již přihlášen.');
15define('USER_LOGGED_IN', 'Byl jste přihlášen.');
16define('USER_LOGGED_OUT', 'Byl jste odhlášen.');
17define('BAD_PASSWORD', 'Špatné heslo.');
18define('USER_NOT_FOUND', 'Uživatel nenalezen.');
19define('USER_PASSWORD_RECOVERY_SUCCESS', 'Přihlašovací údaje byly odeslány na zadanou emailovou adresu.');
20define('USER_PASSWORD_RECOVERY_FAIL', 'Podle zadaných údajů nebyl nalezen žádný uživatel.');
21define('USER_PASSWORD_RECOVERY_CONFIRMED', 'Nové heslo bylo aktivováno.');
22
23define('USER_EVENT_REGISTER', 1);
24define('USER_EVENT_LOGIN', 2);
25define('USER_EVENT_LOGOUT', 3);
26define('USER_EVENT_OPTIONS_CHANGED', 4);
27
28define('DEFAULT_GROUP', 1);
29
30class PasswordHash
31{
32 function Hash($Password, $Salt)
33 {
34 return sha1(sha1($Password).$Salt);
35 }
36
37 function Verify($Password, $Salt, $StoredHash)
38 {
39 return $this->Hash($Password, $Salt) == $StoredHash;
40 }
41
42 function GetSalt()
43 {
44 mt_srand(microtime(true) * 100000 + memory_get_usage(true));
45 return sha1(uniqid(mt_rand(), true));
46 }
47}
48
49// TODO: Make User class more general without dependencies to System, Mail, Log
50
51class User extends Model
52{
53 var $Roles = array();
54 var $User = array();
55 var $OnlineStateTimeout;
56 var $PermissionCache = array();
57 var $PermissionGroupCache = array();
58 var $PermissionGroupCacheOp = array();
59 /** @var Password */
60 var $PasswordHash;
61
62 function __construct($System)
63 {
64 parent::__construct($System);
65 $this->OnlineStateTimeout = 600; // in seconds
66 $this->PasswordHash = new PasswordHash();
67 $this->User = array('Id' => null, 'Member' => null);
68 }
69
70 function Check()
71 {
72 $SID = session_id();
73 // Lookup user record
74 $Query = $this->Database->select('UserOnline', '*', 'SessionId="'.$SID.'"');
75 if ($Query->num_rows > 0)
76 {
77 // Refresh time of last access
78 $this->Database->update('UserOnline', '`SessionId`="'.$SID.'"', array('ActivityTime' => 'NOW()'));
79 } else $this->Database->insert('UserOnline', array('SessionId' => $SID,
80 'User' => null, 'LoginTime' => 'NOW()', 'ActivityTime' => 'NOW()',
81 'IpAddress' => GetRemoteAddress(), 'HostName' => gethostbyaddr(GetRemoteAddress()),
82 'ScriptName' => $_SERVER['PHP_SELF'], 'StayLogged' => 0, 'StayLoggedHash' => ''));
83
84 // Logged permanently?
85 if (array_key_exists('LoginHash', $_COOKIE))
86 {
87 $DbResult = $this->Database->query('SELECT * FROM `UserOnline` WHERE `User`='.$_COOKIE['LoginUserId'].
88 ' AND `StayLogged`=1 AND SessionId!="'.$SID.'"');
89 if ($DbResult->num_rows > 0)
90 {
91 $DbRow = $DbResult->fetch_assoc();
92 if (sha1($_COOKIE['LoginUserId'].$DbRow['StayLoggedHash']) == $_COOKIE['LoginHash'])
93 {
94 $this->Database->query('DELETE FROM `UserOnline` WHERE `SessionId`="'.$SID.'"');
95 $this->Database->query('UPDATE `UserOnline` SET `SessionId`="'.$SID.'" WHERE `Id`='.$DbRow['Id']);
96 }
97 }
98 }
99
100 // Check login
101 $Query = $this->Database->select('UserOnline', '*', '`SessionId`="'.$SID.'"');
102 $Row = $Query->fetch_assoc();
103 if ($Row['User'] != '')
104 {
105 $Query = $this->Database->query('SELECT `User`.* FROM `User` '.
106 ' WHERE `User`.`Id`='.$Row['User']);
107 $this->User = $Query->fetch_assoc();
108 $Result = USER_LOGGED;
109 } else
110 {
111 $Query = $this->Database->select('User', '*', 'Id IS NULL');
112 $this->User = array('Id' => null, 'Member' => null);
113 $Result = USER_NOT_LOGGED;
114 }
115
116 // Remove nonactive users
117 $DbResult = $this->Database->select('UserOnline', '`Id`, `User`', '(`ActivityTime` < DATE_SUB(NOW(), INTERVAL '.$this->OnlineStateTimeout.' SECOND)) AND (`StayLogged` = 0)');
118 while ($DbRow = $DbResult->fetch_array())
119 {
120 $this->Database->delete('UserOnline', 'Id='.$DbRow['Id']);
121 if (($DbRow['User'] != null) and $this->System->ModuleManager->ModulePresent('Log'))
122 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'Logout');
123 }
124 //$this->LoadPermission($this->User['Role']);
125
126 // Role and permission
127 //$this->LoadRoles();
128 }
129
130 function Register($Login, $Password, $Password2, $Email, $Name)
131 {
132 if (($Email == '') || ($Login == '') || ($Password == '') || ($Password2 == '') || ($Name == '')) $Result = DATA_MISSING;
133 else if ($Password != $Password2) $Result = PASSWORDS_UNMATCHED;
134 else
135 {
136 // Is user registred yet?
137 $Query = $this->Database->select('User', '*', 'Login = "'.$Login.'"');
138 if ($Query->num_rows > 0) $Result = LOGIN_USED;
139 else
140 {
141 $Query = $this->Database->select('User', '*', 'Name = "'.$Name.'"');
142 if ($Query->num_rows > 0) $Result = NAME_USED;
143 else
144 {
145 $Query = $this->Database->select('User', '*', 'Email = "'.$Email.'"');
146 if ($Query->num_rows > 0) $Result = EMAIL_USED;
147 else
148 {
149 $PasswordHash = new PasswordHash();
150 $Salt = $PasswordHash->GetSalt();
151 $this->Database->insert('User', array('Name' => $Name, 'Login' => $Login,
152 'Password' => $PasswordHash->Hash($Password, $Salt), 'Salt' => $Salt,
153 'Email' => $Email, 'RegistrationTime' => 'NOW()',
154 'Locked' => 1));
155 $UserId = $this->Database->insert_id;
156 $this->Database->insert('PermissionUserAssignment', array('User' => $UserId,
157 'AssignedGroup' => DEFAULT_GROUP));
158
159 $NewPassword = substr(sha1(strtoupper($Login)), 0, 7);
160
161 // Send activation mail to user email
162 $ServerURL = 'http://'.$this->System->Config['Web']['Host'].$this->System->Config['Web']['RootFolder'];
163 $Mail = new Mail();
164 $Mail->Subject = 'Registrace nového účtu';
165 $Mail->AddBody('Provedli jste registraci nového účtu na serveru <a href="'.$ServerURL.'">'.$ServerURL.'"</a>.'.
166 '<br/>\nPokud jste tak neučinili, měli by jste tento email ignorovat.<br/><br/>\n\n'.
167 'Váš účet je: '.$Login."\n<br/>Pro dokončení registrace klikněte na tento odkaz: ".'<a href="'.
168 $ServerURL.'/user/?Action=UserRegisterConfirm&User='.
169 $UserId.'&H='.$NewPassword.'">'.$ServerURL.'/?Action=UserRegisterConfirm&User='.
170 $UserId.'&H='.$NewPassword.'</a>.'."\n<br> \n\n'.
171 '<br/><br/>Na tento email neodpovídejte.", 'text/html');
172 $Mail->AddTo($Email, $Name);
173 $Mail->From = $this->System->Config['Web']['Title'].' <noreplay@zdechov.net>';
174 $Mail->Send();
175
176 $Result = USER_REGISTRATED;
177 if ($this->System->ModuleManager->ModulePresent('Log'))
178 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'NewRegistration', $Login);
179 }
180 }
181 }
182 }
183 return $Result;
184 }
185
186 function RegisterConfirm($Id, $Hash)
187 {
188 $DbResult = $this->Database->select('User', 'Id, Login, Password', 'Id = '.$Id);
189 if ($DbResult->num_rows > 0)
190 {
191 $Row = $DbResult->fetch_array();
192 $NewPassword = substr(sha1(strtoupper($Row['Login'])), 0, 7);
193 if ($Hash == $NewPassword)
194 {
195 $this->Database->update('User', 'Id='.$Row['Id'], array('Locked' => 0));
196 $Output = USER_REGISTRATION_CONFIRMED;
197 if ($this->System->ModuleManager->ModulePresent('Log'))
198 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'RegisterConfirm', 'Login='.
199 $Row['Login'].', Id='.$Row['Id']);
200 } else $Output = PASSWORDS_UNMATCHED;
201 } else $Output = USER_NOT_FOUND;
202 return $Output;
203 }
204
205 function Login($Login, $Password, $StayLogged = false)
206 {
207 if ($StayLogged) $StayLogged = 1; else $StayLogged = 0;
208 $SID = session_id();
209 $Query = $this->Database->select('User', '*', 'Login="'.$Login.'"');
210 if ($Query->num_rows > 0)
211 {
212 $Row = $Query->fetch_assoc();
213 $PasswordHash = new PasswordHash();
214 if (!$PasswordHash->Verify($Password, $Row['Salt'], $Row['Password'])) $Result = BAD_PASSWORD;
215 else if ($Row['Locked'] == 1) $Result = ACCOUNT_LOCKED;
216 else
217 {
218 $this->Database->update('User', 'Id='.$Row['Id'], array('LastLoginTime' => 'NOW()',
219 'LastIpAddress' => GetRemoteAddress()));
220 $Hash = new PasswordHash();
221 $StayLoggedSalt = $Hash->GetSalt();
222 $this->Database->update('UserOnline', 'SessionId="'.$SID.'"', array(
223 'User' => $Row['Id'], 'StayLogged' => $StayLogged, 'StayLoggedHash' => $StayLoggedSalt));
224 if ($StayLogged)
225 {
226 setcookie('LoginUserId', $Row['Id'], time()+365*24*60*60, $this->System->Link('/'));
227 setcookie('LoginHash', sha1($Row['Id'].$StayLoggedSalt), time()+365*24*60*60, $this->System->Link('/'));
228 } else {
229 setcookie('LoginUserId', '', time() - 3600, $this->System->Link('/'));
230 setcookie('LoginHash', '', time() - 3600, $this->System->Link('/'));
231 }
232
233 $Result = USER_LOGGED_IN;
234 $this->Check();
235 if (array_key_exists('Log', $this->System->ModuleManager->Modules))
236 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'Login', 'Login='.$Login.',Host='.gethostbyaddr(GetRemoteAddress()));
237 }
238 } else $Result = USER_NOT_REGISTRED;
239 return $Result;
240 }
241
242 function Logout()
243 {
244 $SID = session_id();
245 $this->Database->update('UserOnline', 'SessionId="'.$SID.'"', array('User' => null));
246 if ($this->System->ModuleManager->ModulePresent('Log'))
247 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'Logout', $this->User['Login']);
248 $this->Check();
249 return USER_LOGGED_OUT;
250 }
251
252 function LoadRoles()
253 {
254 $this->Roles = array();
255 $DbResult = $this->Database->select('UserRole', '*');
256 while ($DbRow = $DbResult->fetch_array())
257 $this->Roles[] = $DbRow;
258 }
259
260 function LoadPermission($Role)
261 {
262 $this->User['Permission'] = array();
263 $DbResult = $this->Database->query('SELECT `UserRolePermission`.*, `PermissionOperation`.`Description` FROM `UserRolePermission` JOIN `PermissionOperation` ON `PermissionOperation`.`Id` = `UserRolePermission`.`Operation` WHERE `UserRolePermission`.`Role` = '.$Role);
264 if ($DbResult->num_rows > 0)
265 while ($DbRow = $DbResult->fetch_array())
266 $this->User['Permission'][$DbRow['Operation']] = $DbRow;
267 }
268
269 function PermissionMatrix()
270 {
271 $Result = array();
272 $DbResult = $this->Database->query('SELECT `UserRolePermission`.*, `PermissionOperation`.`Description`, `UserRole`.`Title` FROM `UserRolePermission` LEFT JOIN `PermissionOperation` ON `PermissionOperation`.`Id` = `UserRolePermission`.`Operation` LEFT JOIN `UserRole` ON `UserRole`.`Id` = `UserRolePermission`.`Role`');
273 while ($DbRow = $DbResult->fetch_array())
274 {
275 $Value = '';
276 if ($DbRow['Read']) $Value .= 'R';
277 if ($DbRow['Write']) $Value .= 'W';
278 $Result[$DbRow['Description']][$DbRow['Title']] = $Value;
279 }
280 return $Result;
281 }
282
283 function CheckGroupPermission($GroupId, $OperationId)
284 {
285 $PermissionExists = false;
286 // First try to check cache group-group relation
287 if (array_key_exists($GroupId, $this->PermissionGroupCache))
288 {
289 $PermissionExists = true;
290 } else
291 {
292 // If no permission combination exists in cache, do new check of database items
293 $DbResult = $this->Database->select('PermissionGroupAssignment', '*', '(`Group`="'.$GroupId.
294 '") AND (`AssignedGroup` IS NOT NULL)');
295 $DbRow = array();
296 while ($DbRow[] = $DbResult->fetch_array());
297 $this->PermissionGroupCache[$GroupId] = $DbRow;
298 $PermissionExists = true;
299 }
300 if ($PermissionExists)
301 {
302 foreach ($this->PermissionGroupCache[$GroupId] as $DbRow)
303 {
304 if ($DbRow['AssignedGroup'] != '')
305 if ($this->CheckGroupPermission($DbRow['AssignedGroup'], $OperationId) == true) return true;
306 }
307 }
308
309 // Check group-operation relation
310 if (array_key_exists($GroupId.','.$OperationId, $this->PermissionGroupCacheOp))
311 {
312 $PermissionExists = true;
313 } else
314 {
315 // If no permission combination exists in cache, do new check of database items
316 $DbResult = $this->Database->select('PermissionGroupAssignment', '*', '`Group`="'.$GroupId.'" AND `AssignedOperation`="'.$OperationId.'"');
317 if ($DbResult->num_rows > 0) $this->PermissionGroupCacheOp[$GroupId.','.$OperationId] = true;
318 else $this->PermissionGroupCacheOp[$GroupId.','.$OperationId] = false;
319 $PermissionExists = true;
320 }
321 if ($PermissionExists)
322 {
323 return $this->PermissionGroupCacheOp[$GroupId.','.$OperationId];
324 }
325 return false;
326 }
327
328 function CheckPermission($Module, $Operation, $ItemType = '', $ItemIndex = 0)
329 {
330 // Get module id
331 $DbResult = $this->Database->select('Module', 'Id', '`Name`="'.$Module.'"');
332 if ($DbResult->num_rows > 0)
333 {
334 $DbRow = $DbResult->fetch_assoc();
335 $ModuleId = $DbRow['Id'];
336 } else return false;
337
338 // First try to check cache
339 if (in_array(array($Module, $Operation, $ItemType, $ItemType), $this->PermissionCache))
340 {
341 $OperationId = array_search(array($Module, $Operation, $ItemType, $ItemIndex), $this->PermissionCache);
342 $PermissionExists = is_numeric($OperationId);
343 } else
344 {
345 // If no permission combination exists in cache, do new check of database items
346 $DbResult = $this->Database->select('PermissionOperation', 'Id', '(`Module`="'.$ModuleId.
347 '") AND (`Item`="'.$ItemType.'") AND (`ItemId`='.$ItemIndex.') AND (`Operation`="'.$Operation.'")');
348 if ($DbResult->num_rows > 0)
349 {
350 $DbRow = $DbResult->fetch_array();
351 $OperationId = $DbRow['Id'];
352 $this->PermissionCache[$DbRow['Id']] = array($Module, $Operation, $ItemType, $ItemIndex);
353 $PermissionExists = true;
354 } else
355 {
356 $this->PermissionCache[count($this->PermissionCache).'_'] = array($Module, $Operation, $ItemType, $ItemIndex);
357 $PermissionExists = false;
358 }
359 }
360
361 if ($PermissionExists)
362 {
363 if ($this->User['Id'] == null) $UserCondition = '(`User` IS NULL)';
364 else $UserCondition = '(`User`="'.$this->User['Id'].'")';
365 // Check user-operation relation
366 $DbResult = $this->Database->select('PermissionUserAssignment', '*', $UserCondition.' AND (`AssignedOperation`="'.$OperationId.'")');
367 if ($DbResult->num_rows > 0) return true;
368
369 // Check user-group relation
370 $DbResult = $this->Database->select('PermissionUserAssignment', 'AssignedGroup', $UserCondition);
371 while ($DbRow = $DbResult->fetch_array())
372 {
373 if ($this->CheckGroupPermission($DbRow['AssignedGroup'], $OperationId) == true) return true;
374 }
375 return false;
376 } else return false;
377 }
378
379 function PasswordRecoveryRequest($Login, $Email)
380 {
381 $DbResult = $this->Database->select('User', 'Login, Name, Id, Email, Password', '`Login`="'.$Login.'" AND `Email`="'.$Email.'"');
382 if ($DbResult->num_rows > 0)
383 {
384 $Row = $DbResult->fetch_array();
385 $NewPassword = substr(sha1(strtoupper($Row['Login'])), 0, 7);
386
387 $ServerURL = 'http://'.$this->System->Config['Web']['Host'].$this->System->Config['Web']['RootFolder'];
388 $Mail = new Mail();
389 $Mail->Subject = 'Obnova hesla';
390 $Mail->From = $this->System->Config['Web']['Title'].' <noreplay@zdechov.net>';
391 $Mail->AddTo($Row['Email'], $Row['Name']);
392 $Mail->AddBody('Požádali jste o zaslání nového hesla na serveru <a href="'.$ServerURL.'">'.$ServerURL.'"</a>.<br />\n'.
393 "Pokud jste tak neučinili, měli by jste tento email ignorovat.<br /><br />\n\nVaše nové heslo k účtu ".
394 $Row['Login'].' je: '.$NewPassword."\n<br/>".
395 'Pro aktivaci tohoto hesla klikněte na <a href="'.$ServerURL.'/user/?Action=PasswordRecoveryConfirm&User='.
396 $Row['Id'].'&H='.$Row['Password'].'&P='.$NewPassword.'">tento odkaz</a>.'."\n<br />".
397 "Po přihlášení si prosím změňte heslo na nové.\n\n<br><br>Na tento email neodpovídejte.", 'text/html');
398 $Mail->Send();
399
400 $Output = USER_PASSWORD_RECOVERY_SUCCESS;
401 if ($this->System->ModuleManager->ModulePresent('Log'))
402 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'PasswordRecoveryRequest', 'Login='.$Login.',Email='.$Email);
403 } else $Output = USER_PASSWORD_RECOVERY_FAIL;
404 return $Output;
405 }
406
407 function PasswordRecoveryConfirm($Id, $Hash, $NewPassword)
408 {
409 $DbResult = $this->Database->select('User', 'Id, Login, Password', 'Id = '.$Id);
410 if ($DbResult->num_rows > 0)
411 {
412 $Row = $DbResult->fetch_array();
413 $NewPassword2 = substr(sha1(strtoupper($Row['Login'])), 0, 7);
414 if (($NewPassword == $NewPassword2) and ($Hash == $Row['Password']))
415 {
416 $PasswordHash = new PasswordHash();
417 $Salt = $PasswordHash->GetSalt();
418 $this->Database->update('User', 'Id='.$Row['Id'], array('Password' => $PasswordHash->Hash($NewPassword, $Salt),
419 'Salt' => $Salt, 'Locked' => 0));
420 if ($this->System->ModuleManager->ModulePresent('Log'))
421 $this->System->ModuleManager->Modules['Log']->NewRecord('User', 'PasswordRecoveryConfirm', 'Login='.$Row['Login']);
422 } else $Output = PASSWORDS_UNMATCHED;
423 } else $Output = USER_NOT_FOUND;
424 return $Output;
425 }
426
427 function CheckToken($Module, $Operation, $Token)
428 {
429 $DbResult = $this->Database->select('APIToken', 'User', '`Token`="'.$Token.'"');
430 if ($DbResult->num_rows > 0)
431 {
432 $DbRow = $DbResult->fetch_assoc();
433 $User = new User($this->System);
434 $User->User = array('Id' => $DbRow['User']);
435 return $User->CheckPermission($Module, $Operation);
436 } else return false;
437 }
438}
Note: See TracBrowser for help on using the repository browser.