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

Last change on this file since 895 was 895, checked in by chronos, 4 years ago
  • Modified: Setup is now AppModule and it is installed and stated as first module.
  • Modified: Improved modular system.
File size: 20.8 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
28class PasswordHash
29{
30 function Hash(string $Password, string $Salt): string
31 {
32 return sha1(sha1($Password).$Salt);
33 }
34
35 function Verify(string $Password, string $Salt, string $StoredHash): bool
36 {
37 return $this->Hash($Password, $Salt) == $StoredHash;
38 }
39
40 function GetSalt(): string
41 {
42 mt_srand(microtime(true) * 100000 + memory_get_usage(true));
43 return sha1(uniqid(mt_rand(), true));
44 }
45}
46
47// TODO: Make User class more general without dependencies to System, Mail, Log
48
49class User extends Model
50{
51 public array $Roles = array();
52 public array $User = array();
53 public int $OnlineStateTimeout;
54 public array $PermissionCache = array();
55 public array $PermissionGroupCache = array();
56 public array $PermissionGroupCacheOp = array();
57 public PasswordHash $PasswordHash;
58
59 function __construct(System $System)
60 {
61 parent::__construct($System);
62 $this->OnlineStateTimeout = 600; // in seconds
63 $this->PasswordHash = new PasswordHash();
64 $this->User = array('Id' => null, 'Member' => null);
65 }
66
67 static function GetDesc(): ModelDesc
68 {
69 $Desc = new ModelDesc(self::GetClassName());
70 $Column = $Desc->AddString('Login');
71 $Column->Unique = true;
72 $Column = $Desc->AddString('Name');
73 $Column->Unique = true;
74 $Desc->AddString('Password');
75 $Desc->AddString('Salt');
76 $Desc->AddString('Email');
77 $Column = $Desc->AddString('LastIpAddress');
78 $Column->HasDefault = true;
79 $Column->Nullable = true;
80 $Column = $Desc->AddDateTime('LastLoginTime');
81 $Column->Nullable = true;
82 $Column->HasDefault = true;
83 $Desc->AddDateTime('RegistrationTime');
84 $Desc->AddBoolean('Locked');
85 $Column = $Desc->AddString('InitPassword');
86 $Column->Nullable = true;
87 $Column->HasDefault = true;
88 return $Desc;
89 }
90
91 function Check(): void
92 {
93 $SID = session_id();
94 // Lookup user record
95 $Query = $this->Database->select('UserOnline', '*', 'SessionId="'.$SID.'"');
96 if ($Query->num_rows > 0)
97 {
98 // Refresh time of last access
99 $this->Database->update('UserOnline', '`SessionId`="'.$SID.'"', array('ActivityTime' => 'NOW()'));
100 } else $this->Database->insert('UserOnline', array('SessionId' => $SID,
101 'User' => null, 'LoginTime' => 'NOW()', 'ActivityTime' => 'NOW()',
102 'IpAddress' => GetRemoteAddress(), 'HostName' => gethostbyaddr(GetRemoteAddress()),
103 'ScriptName' => $_SERVER['PHP_SELF'], 'StayLogged' => 0, 'StayLoggedHash' => ''));
104
105 // Logged permanently?
106 if (array_key_exists('LoginHash', $_COOKIE))
107 {
108 $DbResult = $this->Database->query('SELECT * FROM `UserOnline` WHERE `User`='.$_COOKIE['LoginUserId'].
109 ' AND `StayLogged`=1 AND SessionId!="'.$SID.'"');
110 if ($DbResult->num_rows > 0)
111 {
112 $DbRow = $DbResult->fetch_assoc();
113 if (sha1($_COOKIE['LoginUserId'].$DbRow['StayLoggedHash']) == $_COOKIE['LoginHash'])
114 {
115 $this->Database->query('DELETE FROM `UserOnline` WHERE `SessionId`="'.$SID.'"');
116 $this->Database->query('UPDATE `UserOnline` SET `SessionId`="'.$SID.'" WHERE `Id`='.$DbRow['Id']);
117 }
118 }
119 }
120
121 // Check login
122 $Query = $this->Database->select('UserOnline', '*', '`SessionId`="'.$SID.'"');
123 $Row = $Query->fetch_assoc();
124 if ($Row['User'] != '')
125 {
126 $Query = $this->Database->query('SELECT `User`.* FROM `User` WHERE `User`.`Id`='.$Row['User']);
127 $this->User = $Query->fetch_assoc();
128 $Result = USER_LOGGED;
129 } else
130 {
131 $Query = $this->Database->select('User', '*', 'Id IS NULL');
132 $this->User = array('Id' => null, 'Member' => null);
133 $Result = USER_NOT_LOGGED;
134 }
135
136 // Remove nonactive users
137 $DbResult = $this->Database->select('UserOnline', '`Id`, `User`', '(`ActivityTime` < DATE_SUB(NOW(), INTERVAL '.$this->OnlineStateTimeout.' SECOND)) AND (`StayLogged` = 0)');
138 while ($DbRow = $DbResult->fetch_array())
139 {
140 $this->Database->delete('UserOnline', 'Id='.$DbRow['Id']);
141 if ($DbRow['User'] != null) ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'Logout');
142 }
143 //$this->LoadPermission($this->User['Role']);
144
145 // Role and permission
146 //$this->LoadRoles();
147 }
148
149 function Register(string $Login, string $Password, string $Password2, string $Email, string $Name): string
150 {
151 if (($Email == '') || ($Login == '') || ($Password == '') || ($Password2 == '') || ($Name == '')) $Result = DATA_MISSING;
152 else if ($Password != $Password2) $Result = PASSWORDS_UNMATCHED;
153 else
154 {
155 // Is user registred yet?
156 $Query = $this->Database->select('User', '*', 'Login = "'.$Login.'"');
157 if ($Query->num_rows > 0) $Result = LOGIN_USED;
158 else
159 {
160 $Query = $this->Database->select('User', '*', 'Name = "'.$Name.'"');
161 if ($Query->num_rows > 0) $Result = NAME_USED;
162 else
163 {
164 $Query = $this->Database->select('User', '*', 'Email = "'.$Email.'"');
165 if ($Query->num_rows > 0) $Result = EMAIL_USED;
166 else
167 {
168 $PasswordHash = new PasswordHash();
169 $Salt = $PasswordHash->GetSalt();
170 $this->Database->insert('User', array('Name' => $Name, 'Login' => $Login,
171 'Password' => $PasswordHash->Hash($Password, $Salt), 'Salt' => $Salt,
172 'Email' => $Email, 'RegistrationTime' => 'NOW()',
173 'Locked' => 1));
174 $UserId = $this->Database->insert_id;
175 $this->Database->insert('PermissionUserAssignment', array('User' => $UserId,
176 'AssignedGroup' => 2));
177
178 $NewPassword = substr(sha1(strtoupper($Login)), 0, 7);
179
180 // Send activation mail to user email
181 $ServerURL = 'http://'.$this->System->Config['Web']['Host'].$this->System->Config['Web']['RootFolder'];
182 $Mail = new Mail();
183 $Mail->Subject = 'Registrace nového účtu';
184 $Mail->AddBody('Provedli jste registraci nového účtu na serveru <a href="'.$ServerURL.'">'.$ServerURL.'"</a>.'.
185 '<br/>\nPokud jste tak neučinili, měli by jste tento email ignorovat.<br/><br/>\n\n'.
186 'Váš účet je: '.$Login."\n<br/>Pro dokončení registrace klikněte na tento odkaz: ".'<a href="'.
187 $ServerURL.'/user/?Action=UserRegisterConfirm&User='.
188 $UserId.'&H='.$NewPassword.'">'.$ServerURL.'/?Action=UserRegisterConfirm&User='.
189 $UserId.'&H='.$NewPassword.'</a>.'."\n<br> \n\n'.
190 '<br/><br/>Na tento email neodpovídejte.", 'text/html');
191 $Mail->AddTo($Email, $Name);
192 $Mail->From = $this->System->Config['Web']['Title'].' <noreplay@zdechov.net>';
193 $Mail->Send();
194
195 $Result = USER_REGISTRATED;
196 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'NewRegistration', $Login);
197 }
198 }
199 }
200 }
201 return $Result;
202 }
203
204 function RegisterConfirm(string $Id, string $Hash): string
205 {
206 $DbResult = $this->Database->select('User', 'Id, Login, Password', 'Id = '.$Id);
207 if ($DbResult->num_rows > 0)
208 {
209 $Row = $DbResult->fetch_array();
210 $NewPassword = substr(sha1(strtoupper($Row['Login'])), 0, 7);
211 if ($Hash == $NewPassword)
212 {
213 $this->Database->update('User', 'Id='.$Row['Id'], array('Locked' => 0));
214 $Output = USER_REGISTRATION_CONFIRMED;
215 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'RegisterConfirm', 'Login='.
216 $Row['Login'].', Id='.$Row['Id']);
217 } else $Output = PASSWORDS_UNMATCHED;
218 } else $Output = USER_NOT_FOUND;
219 return $Output;
220 }
221
222 function Login(string $Login, string $Password, bool $StayLogged = false): string
223 {
224 if ($StayLogged) $StayLogged = 1; else $StayLogged = 0;
225 $SID = session_id();
226 $Query = $this->Database->select('User', '*', 'Login="'.$Login.'"');
227 if ($Query->num_rows > 0)
228 {
229 $Row = $Query->fetch_assoc();
230 $PasswordHash = new PasswordHash();
231 if (!$PasswordHash->Verify($Password, $Row['Salt'], $Row['Password'])) $Result = BAD_PASSWORD;
232 else if ($Row['Locked'] == 1) $Result = ACCOUNT_LOCKED;
233 else
234 {
235 $this->Database->update('User', 'Id='.$Row['Id'], array('LastLoginTime' => 'NOW()',
236 'LastIpAddress' => GetRemoteAddress()));
237 $Hash = new PasswordHash();
238 $StayLoggedSalt = $Hash->GetSalt();
239 $this->Database->update('UserOnline', 'SessionId="'.$SID.'"', array(
240 'User' => $Row['Id'], 'StayLogged' => $StayLogged, 'StayLoggedHash' => $StayLoggedSalt));
241 if ($StayLogged)
242 {
243 setcookie('LoginUserId', $Row['Id'], time()+365*24*60*60, $this->System->Link('/'));
244 setcookie('LoginHash', sha1($Row['Id'].$StayLoggedSalt), time()+365*24*60*60, $this->System->Link('/'));
245 } else {
246 setcookie('LoginUserId', '', time() - 3600, $this->System->Link('/'));
247 setcookie('LoginHash', '', time() - 3600, $this->System->Link('/'));
248 }
249
250 $Result = USER_LOGGED_IN;
251 $this->Check();
252 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'Login', 'Login='.$Login.',Host='.gethostbyaddr(GetRemoteAddress()));
253 }
254 } else $Result = USER_NOT_REGISTRED;
255 return $Result;
256 }
257
258 function Logout(): string
259 {
260 $SID = session_id();
261 $this->Database->update('UserOnline', 'SessionId="'.$SID.'"', array('User' => null));
262 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'Logout', $this->User['Login']);
263 $this->Check();
264 return USER_LOGGED_OUT;
265 }
266
267 function LoadRoles()
268 {
269 $this->Roles = array();
270 $DbResult = $this->Database->select('UserRole', '*');
271 while ($DbRow = $DbResult->fetch_array())
272 {
273 $this->Roles[] = $DbRow;
274 }
275 }
276
277 function LoadPermission($Role)
278 {
279 $this->User['Permission'] = array();
280 $DbResult = $this->Database->query('SELECT `UserRolePermission`.*, `PermissionOperation`.`Description` FROM `UserRolePermission` JOIN `PermissionOperation` ON `PermissionOperation`.`Id` = `UserRolePermission`.`Operation` WHERE `UserRolePermission`.`Role` = '.$Role);
281 if ($DbResult->num_rows > 0)
282 while ($DbRow = $DbResult->fetch_array())
283 {
284 $this->User['Permission'][$DbRow['Operation']] = $DbRow;
285 }
286 }
287
288 function PermissionMatrix(): array
289 {
290 $Result = array();
291 $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`');
292 while ($DbRow = $DbResult->fetch_array())
293 {
294 $Value = '';
295 if ($DbRow['Read']) $Value .= 'R';
296 if ($DbRow['Write']) $Value .= 'W';
297 $Result[$DbRow['Description']][$DbRow['Title']] = $Value;
298 }
299 return $Result;
300 }
301
302 function CheckGroupPermission(string $GroupId, string $OperationId): bool
303 {
304 $PermissionExists = false;
305 // First try to check cache group-group relation
306 if (array_key_exists($GroupId, $this->PermissionGroupCache))
307 {
308 $PermissionExists = true;
309 } else
310 {
311 $this->PermissionGroupCache[$GroupId] = array();
312
313 // If no permission combination exists in cache, do new check of database items
314 $DbResult = $this->Database->select('PermissionGroupAssignment', '*', '(`Group`="'.$GroupId.
315 '") AND (`AssignedGroup` IS NOT NULL)');
316 while ($DbRow = $DbResult->fetch_array())
317 {
318 $this->PermissionGroupCache[$GroupId][] = $DbRow;
319 }
320 $PermissionExists = true;
321 }
322 if ($PermissionExists)
323 {
324 foreach ($this->PermissionGroupCache[$GroupId] as $DbRow)
325 {
326 if ($DbRow['AssignedGroup'] != '')
327 if ($this->CheckGroupPermission($DbRow['AssignedGroup'], $OperationId) == true) return true;
328 }
329 }
330
331 // Check group-operation relation
332 if (array_key_exists($GroupId.','.$OperationId, $this->PermissionGroupCacheOp))
333 {
334 $PermissionExists = true;
335 } else
336 {
337 // If no permission combination exists in cache, do new check of database items
338 $DbResult = $this->Database->select('PermissionGroupAssignment', '*', '`Group`="'.$GroupId.'" AND `AssignedOperation`="'.$OperationId.'"');
339 if ($DbResult->num_rows > 0) $this->PermissionGroupCacheOp[$GroupId.','.$OperationId] = true;
340 else $this->PermissionGroupCacheOp[$GroupId.','.$OperationId] = false;
341 $PermissionExists = true;
342 }
343 if ($PermissionExists)
344 {
345 return $this->PermissionGroupCacheOp[$GroupId.','.$OperationId];
346 }
347 return false;
348 }
349
350 function CheckPermission(string $Module, string $Operation, string $ItemType = '', int $ItemIndex = 0): bool
351 {
352 // Get module id
353 $DbResult = $this->Database->select('Module', 'Id', '`Name`="'.$Module.'"');
354 if ($DbResult->num_rows > 0)
355 {
356 $DbRow = $DbResult->fetch_assoc();
357 $ModuleId = $DbRow['Id'];
358 } else return false;
359
360 // First try to check cache
361 if (in_array(array($Module, $Operation, $ItemType, $ItemType), $this->PermissionCache))
362 {
363 $OperationId = array_search(array($Module, $Operation, $ItemType, $ItemIndex), $this->PermissionCache);
364 $PermissionExists = is_numeric($OperationId);
365 } else
366 {
367 // If no permission combination exists in cache, do new check of database items
368 $DbResult = $this->Database->select('PermissionOperation', 'Id', '(`Module`="'.$ModuleId.
369 '") AND (`Item`="'.$ItemType.'") AND (`ItemId`='.$ItemIndex.') AND (`Operation`="'.$Operation.'")');
370 if ($DbResult->num_rows > 0)
371 {
372 $DbRow = $DbResult->fetch_array();
373 $OperationId = $DbRow['Id'];
374 $this->PermissionCache[$DbRow['Id']] = array($Module, $Operation, $ItemType, $ItemIndex);
375 $PermissionExists = true;
376 } else
377 {
378 $this->PermissionCache[count($this->PermissionCache).'_'] = array($Module, $Operation, $ItemType, $ItemIndex);
379 $PermissionExists = false;
380 }
381 }
382
383 if ($PermissionExists)
384 {
385 if ($this->User['Id'] == null) $UserCondition = '(`User` IS NULL)';
386 else $UserCondition = '(`User`="'.$this->User['Id'].'")';
387 // Check user-operation relation
388 $DbResult = $this->Database->select('PermissionUserAssignment', '*', $UserCondition.' AND (`AssignedOperation`="'.$OperationId.'")');
389 if ($DbResult->num_rows > 0) return true;
390
391 // Check user-group relation
392 $DbResult = $this->Database->select('PermissionUserAssignment', 'AssignedGroup', $UserCondition);
393 while ($DbRow = $DbResult->fetch_array())
394 {
395 if ($this->CheckGroupPermission($DbRow['AssignedGroup'], $OperationId) == true) return true;
396 }
397 return false;
398 } else return false;
399 }
400
401 function PasswordRecoveryRequest(string $Login, string $Email): string
402 {
403 $DbResult = $this->Database->select('User', 'Login, Name, Id, Email, Password', '`Login`="'.$Login.'" AND `Email`="'.$Email.'"');
404 if ($DbResult->num_rows > 0)
405 {
406 $Row = $DbResult->fetch_array();
407 $NewPassword = substr(sha1(strtoupper($Row['Login'])), 0, 7);
408
409 $ServerURL = 'http://'.$this->System->Config['Web']['Host'].$this->System->Config['Web']['RootFolder'];
410 $Mail = new Mail();
411 $Mail->Subject = 'Obnova hesla';
412 $Mail->From = $this->System->Config['Web']['Title'].' <noreplay@zdechov.net>';
413 $Mail->AddTo($Row['Email'], $Row['Name']);
414 $Mail->AddBody('Požádali jste o zaslání nového hesla na serveru <a href="'.$ServerURL.'">'.$ServerURL.'"</a>.<br />\n'.
415 "Pokud jste tak neučinili, měli by jste tento email ignorovat.<br /><br />\n\nVaše nové heslo k účtu ".
416 $Row['Login'].' je: '.$NewPassword."\n<br/>".
417 'Pro aktivaci tohoto hesla klikněte na <a href="'.$ServerURL.'/user/?Action=PasswordRecoveryConfirm&User='.
418 $Row['Id'].'&H='.$Row['Password'].'&P='.$NewPassword.'">tento odkaz</a>.'."\n<br />".
419 "Po přihlášení si prosím změňte heslo na nové.\n\n<br><br>Na tento email neodpovídejte.", 'text/html');
420 $Mail->Send();
421
422 $Output = USER_PASSWORD_RECOVERY_SUCCESS;
423 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'PasswordRecoveryRequest', 'Login='.$Login.',Email='.$Email);
424 } else $Output = USER_PASSWORD_RECOVERY_FAIL;
425 return $Output;
426 }
427
428 function PasswordRecoveryConfirm(string $Id, string $Hash, string $NewPassword): string
429 {
430 $DbResult = $this->Database->select('User', 'Id, Login, Password', 'Id = '.$Id);
431 if ($DbResult->num_rows > 0)
432 {
433 $Row = $DbResult->fetch_array();
434 $NewPassword2 = substr(sha1(strtoupper($Row['Login'])), 0, 7);
435 if (($NewPassword == $NewPassword2) and ($Hash == $Row['Password']))
436 {
437 $PasswordHash = new PasswordHash();
438 $Salt = $PasswordHash->GetSalt();
439 $this->Database->update('User', 'Id='.$Row['Id'], array('Password' => $PasswordHash->Hash($NewPassword, $Salt),
440 'Salt' => $Salt, 'Locked' => 0));
441 $Output = USER_PASSWORD_RECOVERY_CONFIRMED;
442 ModuleLog::Cast($this->System->GetModule('Log'))->NewRecord('User', 'PasswordRecoveryConfirm', 'Login='.$Row['Login']);
443 } else $Output = PASSWORDS_UNMATCHED;
444 } else $Output = USER_NOT_FOUND;
445 return $Output;
446 }
447
448 function CheckToken(string $Module, string $Operation, string $Token): bool
449 {
450 $DbResult = $this->Database->select('APIToken', 'User', '`Token`="'.$Token.'"');
451 if ($DbResult->num_rows > 0)
452 {
453 $DbRow = $DbResult->fetch_assoc();
454 $User = new User($this->System);
455 $User->User = array('Id' => $DbRow['User']);
456 return $User->CheckPermission($Module, $Operation);
457 } else return false;
458 }
459}
460
461class UserOnline extends Model
462{
463 static function GetDesc(): ModelDesc
464 {
465 $Desc = new ModelDesc(self::GetClassName());
466 $Desc->Memory = true;
467 $Desc->AddReference('User', User::GetClassName(), true);
468 $Desc->AddDateTime('ActivityTime');
469 $Desc->AddDateTime('LoginTime');
470 $Desc->AddString('SessionId');
471 $Desc->AddString('IpAddress');
472 $Desc->AddString('HostName');
473 $Desc->AddString('ScriptName');
474 $Desc->AddBoolean('StayLogged');
475 $Desc->AddString('StayLoggedHash');
476 return $Desc;
477 }
478}
479
480class PermissionGroup extends Model
481{
482 static function GetDesc(): ModelDesc
483 {
484 $Desc = new ModelDesc(self::GetClassName());
485 $Desc->AddString('Description');
486 return $Desc;
487 }
488}
489
490class PermissionGroupAssignment extends Model
491{
492 static function GetDesc(): ModelDesc
493 {
494 $Desc = new ModelDesc(self::GetClassName());
495 $Desc->AddReference('Group', PermissionGroup::GetClassName());
496 $Desc->AddReference('AssignedGroup', PermissionGroup::GetClassName(), true);
497 $Desc->AddReference('AssignedOperation', PermissionOperation::GetClassName(), true);
498 return $Desc;
499 }
500}
501
502class PermissionOperation extends Model
503{
504 static function GetDesc(): ModelDesc
505 {
506 $Desc = new ModelDesc(self::GetClassName());
507 //$Desc->AddReference('Module', Module::GetClassName());
508 $Desc->AddString('Operation');
509 $Desc->AddString('Item');
510 $Desc->AddInteger('ItemId');
511 $Desc->Indices = array('Operation', 'Item', 'ItemId');
512 return $Desc;
513 }
514}
515
516class PermissionUserAssignment extends Model
517{
518 static function GetDesc(): ModelDesc
519 {
520 $Desc = new ModelDesc(self::GetClassName());
521 $Desc->AddReference('User', User::GetClassName());
522 $Desc->AddReference('AssignedGroup', PermissionGroup::GetClassName(), true);
523 $Desc->AddReference('AssignedOperation', PermissionOperation::GetClassName(), true);
524 return $Desc;
525 }
526}
Note: See TracBrowser for help on using the repository browser.