File modules/cms/model/User.class.php
Last commit: Sun Jan 5 23:14:07 2025 +0100 Jan Dankert New UI feature: Show a timeline in the start page.
1 <?php 2 3 namespace cms\model; 4 5 // OpenRat Content Management System 6 // Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de 7 // 8 // This program is free software; you can redistribute it and/or 9 // modify it under the terms of the GNU General Public License 10 // as published by the Free Software Foundation; either version 2 11 // of the License, or (at your option) any later version. 12 // 13 // This program is distributed in the hope that it will be useful, 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 // GNU General Public License for more details. 17 // 18 // You should have received a copy of the GNU General Public License 19 // along with this program; if not, write to the Free Software 20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 use cms\base\Configuration; 22 use cms\base\DB; 23 use cms\base\Startup; 24 use cms\base\Language; 25 use language\Messages; 26 use security\Password; 27 use util\exception\ObjectNotFoundException; 28 use util\Request; 29 30 31 /** 32 * Darstellen eines Benutzers 33 * 34 * @author Jan Dankert 35 */ 36 class User extends ModelBase 37 { 38 /** 39 * Local user database 40 */ 41 const AUTH_TYPE_INTERNAL = 1; 42 43 /** 44 * OpenId Connect 45 */ 46 const AUTH_TYPE_OIDC = 2; 47 48 const STYLE_SCHEME_AUTO = 1; 49 const STYLE_SCHEME_LIGHT = 2; 50 const STYLE_SCHEME_DARK = 3; 51 52 public $userid = 0; 53 54 /** 55 * Username. 56 * This name should not be displayed to other users. 57 * 58 * @var string 59 */ 60 public $name = ''; 61 62 /** 63 * natural name of this user. 64 * 65 * @var string 66 */ 67 public $fullname = ''; 68 public $tel; 69 public $mail; 70 public $desc; 71 public $style; 72 public $styleScheme; 73 public $isAdmin; 74 public $loginDate = 0; 75 76 /** 77 * User prefered language. 78 * 79 * As ISO string. 80 * 81 * @var string 82 */ 83 public $language; 84 85 public $timezone; 86 public $passwordExpires; 87 public $passwordAlgo; 88 89 public $lastLogin; 90 public $otpSecret; 91 public $hotp ; 92 public $hotpCount; 93 public $totp ; 94 95 public $issuer = null; 96 public $type = User::AUTH_TYPE_INTERNAL; 97 98 public $passwordFailedCount = 0; 99 public $passwordLockedUntil = 0; 100 101 private $groups = null; 102 103 104 // Konstruktor 105 public function __construct( $userid='' ) 106 { 107 if ( is_numeric($userid) ) 108 $this->userid = $userid; 109 } 110 111 112 /** 113 * get all users. 114 * 115 * @return array 116 */ 117 public static function listAll() 118 { 119 $sql = Db::sql( <<<SQL 120 SELECT id,name 121 FROM {{user}} 122 ORDER BY name 123 SQL 124 ); 125 return $sql->getAssoc(); 126 } 127 128 129 /** 130 * Get all users. 131 * 132 * @return array with user objects 133 */ 134 public static function getAllUsers() 135 { 136 $list = array(); 137 $sql = Db::sql( <<<SQL 138 SELECT * 139 FROM {{user}} 140 ORDER BY name 141 SQL 142 ); 143 foreach($sql->getAll() as $row ) 144 { 145 $user = new User(); 146 $user->setDatabaseRow( $row ); 147 148 $list[] = $user; 149 } 150 151 return $list; 152 } 153 154 155 /** 156 * Benutzer als aktiven Benutzer in die Session schreiben. 157 */ 158 public function setCurrent() 159 { 160 $this->loginDate = time(); 161 162 Request::setUser( $this ); 163 } 164 165 166 167 /** 168 * Benutzer als aktiven Benutzer in die Session schreiben. 169 */ 170 public function updateLoginTimestamp() 171 { 172 $stmt = Db::sql( <<<SQL 173 UPDATE {{user}} 174 SET last_login={time} 175 WHERE id={userid} 176 SQL 177 ); 178 $stmt->setInt( 'time' ,time() ); 179 $stmt->setInt( 'userid',$this->userid ); 180 181 // Datenbankabfrage ausfuehren 182 $stmt->execute(); 183 } 184 185 186 /** 187 * Creates an SQL-WHERE-Clause. 188 * 189 * A "WHERE IN()" clause is difficult, because groups may be part of another groups. 190 * 191 * @return String SQL-WHERE-Clause 192 */ 193 public function getGroupClause() 194 { 195 $groupIds = $this->getEffectiveGroups(); 196 197 if ( count($groupIds) > 0 ) 198 $groupclause = ' groupid='.implode(' OR groupid=',$groupIds ); 199 else 200 $groupclause = ' 1=0 '; 201 202 return $groupclause; 203 } 204 205 206 /** 207 * Lesen aller Projekte, fuer die der Benutzer berechtigt ist. 208 * 209 * @return array [Projekt-Id] = Projekt-Name 210 */ 211 public function getReadableProjects() 212 { 213 $db = Db::get(); 214 215 if ( $this->isAdmin ) 216 { 217 // Administratoren haben Rechte auf alle Projekte. 218 return Project::getAllProjects(); 219 } 220 else 221 { 222 $groupClause = $this->getGroupClause(); 223 $sql = $db->sql(<<<SQL 224 SELECT DISTINCT {{project}}.id,{{project}}.name 225 FROM {{object}} 226 LEFT JOIN {{acl}} ON {{object}}.id = {{acl}}.objectid 227 LEFT JOIN {{project}} ON {{project}}.id = {{object}}.projectid 228 WHERE {{object}}.parentid IS NULL AND 229 {{acl}}.id IS NOT NULL AND 230 ( 231 {{acl}}.type = {usertype} AND {{acl}}.userid={userid} 232 OR {{acl}}.type = {grouptype} AND $groupClause 233 OR ({{acl}}.type = {alltype} 234 OR ({{acl}}.type = {guesttype} 235 ) 236 ORDER BY {{project}}.name 237 SQL 238 ); 239 $sql->setInt( 'userid',$this->userid ); 240 $sql->setInt( 'usertype' ,Permission::TYPE_USER ); 241 $sql->setInt( 'grouptype',Permission::TYPE_GROUP ); 242 $sql->setInt( 'alltype' ,Permission::TYPE_AUTH ); 243 $sql->setInt( 'guesttype',Permission::TYPE_GUEST ); 244 245 return $sql->getAssoc(); 246 } 247 248 } 249 250 251 252 /** 253 * Ermittelt alls Projekte, fuer die der Benutzer berechtigt ist. 254 * @return array [0..n] = Projekt-Id 255 */ 256 function getReadableProjectIds() 257 { 258 return array_keys( $this->getReadableProjects() ); 259 } 260 261 262 /** 263 * Gets all login tokens for this user. 264 * 265 * @return array 266 */ 267 public function getLoginTokens() { 268 269 $stmt = Db::sql( <<<SQL 270 SELECT selector,expires,create_date,platform,name 271 FROM {{auth}} 272 WHERE userid={userid} 273 SQL 274 ); 275 276 $stmt->setInt('userid',$this->getId() ); 277 278 return $stmt->getAll(); 279 } 280 281 /** 282 * Creates a completly new login token. 283 * 284 * @return string new login token 285 */ 286 function createNewLoginToken() 287 { 288 $selector = Password::randomHexString(24); 289 $token = Password::randomHexString(24); 290 291 $tokenHash = Password::hash($token,Password::ALGO_SHA1); 292 293 $stmt = Db::sql( 'SELECT max(id) FROM {{auth}}'); 294 $count = $stmt->getOne(); 295 296 $stmt = Db::sql( <<<SQL 297 INSERT INTO {{auth}} (id,userid,selector,token,token_algo,expires,create_date,platform,name) 298 VALUES( {id},{userid},{selector},{token},{token_algo},{expires},{create_date},{platform},{name} ) 299 SQL 300 ); 301 $expirationPeriodDays = Configuration::Conf()->subset('user')->subset('security')->get('token_expires_after_days',730); 302 303 $stmt->setInt( 'id' ,++$count ); 304 $stmt->setInt( 'userid' ,$this->userid ); 305 306 $stmt->setString( 'selector' ,$selector ); 307 $stmt->setString( 'token' ,$tokenHash ); 308 $stmt->setInt ( 'token_algo' ,Password::ALGO_SHA1 ); 309 310 $stmt->setInt( 'expires' ,time() + ($expirationPeriodDays*24*60*60) ); 311 $stmt->setInt( 'create_date',time() ); 312 313 $browser = new \util\Browser(); 314 $stmt->setString( 'platform',$browser->platform ); 315 $stmt->setString( 'name' ,$browser->name ); 316 $row = $stmt->getRow(); 317 318 // Zusammensetzen des Tokens 319 return $selector.'.'.$token; 320 } 321 322 323 324 /** 325 * Creates a new login token for a serial. 326 * 327 * @param $selector string selector 328 * @return string new login token 329 */ 330 public function createNewLoginTokenForSerial( $selector ) 331 { 332 $algo = Password::ALGO_SHA1; 333 $token = Password::randomHexString(24); 334 335 $tokenHash = Password::hash($token,$algo); 336 337 $stmt = Db::sql( <<<SQL 338 UPDATE {{auth}} 339 SET token={token},token_algo={token_algo} 340 WHERE selector={selector} 341 SQL 342 ); 343 344 $stmt->setString( 'selector' ,$selector ); 345 $stmt->setString( 'token' ,$tokenHash ); 346 $stmt->setInt ( 'token_algo' ,$algo ); 347 $stmt->execute(); 348 349 // Zusammensetzen des Tokens 350 return $selector.'.'.$token; 351 } 352 353 354 355 356 /** 357 * Deletes a login token. 358 * @param $selector string selector 359 */ 360 function deleteLoginToken( $selector ) 361 { 362 $stmt = Db::sql( <<<SQL 363 DELETE FROM {{auth}} 364 WHERE selector = {selector} 365 SQL 366 ); 367 $stmt->setString('selector',$selector ); 368 $stmt->execute(); 369 } 370 371 372 /** 373 * Lesen Benutzer aus der Datenbank. 374 * 375 * @return User 376 */ 377 public function load() 378 { 379 $stmt = Db::sql( 'SELECT * FROM {{user}}'. 380 ' WHERE id={userid}' ); 381 $stmt->setInt( 'userid',$this->userid ); 382 $row = $stmt->getRow(); 383 384 if ( count($row) == 0 ) 385 throw new \util\exception\ObjectNotFoundException(); 386 387 $this->setDatabaseRow( $row ); 388 return $this; 389 } 390 391 392 /** 393 * Gets a user by its name. 394 * 395 * @static 396 * @param $name string user name 397 * @param $authType int authentication type 398 * @param $issuer string issuer who created this user 399 * @return User or null, if user is not found 400 */ 401 public static function loadWithName( $name,$authType,$issuer = null ) 402 { 403 // Search user with name 404 $sql = Db::sql( <<<SQL 405 SELECT id FROM {{user}} 406 WHERE name={name} 407 AND auth_type={type} 408 AND ( issuer={issuer} 409 OR (auth_type=1 AND issuer IS NULL) 410 ) 411 SQL 412 ); 413 $sql->setString( 'name' ,$name ); 414 $sql->setString( 'type' ,$authType ); 415 $sql->setString( 'issuer',$issuer ); 416 417 $userId = $sql->getOne(); 418 419 if ( ! $userId ) 420 return null; // no user found. 421 422 // Create the user by id. 423 $namedUser = new User( $userId ); 424 425 $namedUser->load(); 426 427 return $namedUser; 428 } 429 430 431 432 /** 433 * Stellt fest, ob der Benutzer korrekt geladen ist. 434 */ 435 public function isValid() 436 { 437 return intval($this->userid) > 0; 438 } 439 440 441 442 /** 443 * Lesen Benutzer aus der Datenbank 444 */ 445 protected function setDatabaseRow( $row ) 446 { 447 $this->userid = $row['id' ]; 448 $this->name = $row['name' ]; 449 $this->style = $row['style' ]; 450 $this->styleScheme = $row['style_scheme' ]; 451 $this->isAdmin = ( $row['is_admin'] == '1'); 452 $this->fullname = $row['fullname']; 453 $this->tel = $row['tel' ]; 454 $this->mail = $row['mail' ]; 455 $this->desc = $row['descr' ]; 456 $this->language = $row['language']; 457 $this->timezone = $row['timezone']; 458 $this->lastLogin = $row['last_login']; 459 $this->otpSecret = $row['otp_secret']; 460 $this->hotp = ($row['hotp']==1); 461 $this->hotpCount = $row['hotp_counter']; 462 $this->totp = ($row['totp']==1); 463 $this->passwordExpires = $row['password_expires']; 464 $this->passwordAlgo = $row['password_algo']; 465 $this->passwordLockedUntil = $row['password_locked_until']; 466 $this->passwordFailedCount = $row['password_fail_count' ]; 467 $this->type = $row['auth_type']; 468 $this->issuer = $row['issuer']; 469 470 if ( ! $this->fullname ) 471 $this->fullname = $this->name; 472 473 if ( ! $this->style ) 474 $this->style = Configuration::get(['interface','style','default']); 475 } 476 477 478 479 /** 480 * Namen ermitteln.<br> 481 * Wenn "fullname" gefuellt, dann diesen benutzen, sonst den Benutzernamen. 482 */ 483 function getName() 484 { 485 if ( ! $this->fullname ) 486 return $this->name; 487 else 488 return $this->fullname; 489 } 490 491 492 493 /** 494 * Liest einen Benutzernamen aus der Datenbank. 495 * 496 * @param int Benutzer-Id 497 * @return String Benutzername 498 */ 499 public static function getUserName( $userid ) 500 { 501 $db = Db::get(); 502 503 $sql = $db->sql( 'SELECT name FROM {{user}}'. 504 ' WHERE id={userid}' ); 505 $sql->setInt( 'userid',$userid ); 506 507 $name = $sql->getOne(); 508 509 if ( $name == '' ) 510 return Language::lang(Messages::UNKNOWN); 511 else return $name; 512 } 513 514 515 /** 516 * Speichern Benutzer in der Datenbank. 517 */ 518 protected function save() 519 { 520 $sql = Db::sql( <<<SQL 521 UPDATE {{user}} 522 SET name={name}, 523 fullname={fullname}, 524 tel ={tel} , 525 descr ={desc} , 526 mail ={mail} , 527 style ={style} , 528 style_scheme = {style_scheme}, 529 language = {language}, 530 timezone = {timezone}, 531 is_admin = {isAdmin}, 532 totp = {totp}, 533 hotp = {hotp}, 534 password_fail_count = {fail_count}, 535 password_locked_until = {locked_until} 536 WHERE id={userid} 537 SQL 538 ); 539 $sql->setString ( 'name' ,$this->name ); 540 $sql->setString ( 'fullname',$this->fullname); 541 $sql->setString ( 'tel' ,$this->tel ); 542 $sql->setString ( 'desc' ,$this->desc ); 543 $sql->setString ( 'mail' ,$this->mail ); 544 $sql->setString ( 'style' ,$this->style ); 545 $sql->setInt ( 'style_scheme',$this->styleScheme ); 546 $sql->setStringOrNull( 'language',$this->language); 547 $sql->setStringOrNull( 'timezone',$this->timezone); 548 $sql->setBoolean( 'isAdmin' ,$this->isAdmin ); 549 $sql->setBoolean( 'totp' ,$this->totp ); 550 $sql->setBoolean( 'hotp' ,$this->hotp ); 551 $sql->setInt ( 'userid' ,$this->userid ); 552 $sql->setInt ( 'fail_count' ,$this->passwordFailedCount ); 553 $sql->setInt ( 'locked_until',$this->passwordLockedUntil ); 554 555 // Datenbankabfrage ausfuehren 556 $sql->execute(); 557 } 558 559 560 /** 561 * Benutzer hinzuf�gen 562 * 563 * @param String $name Benutzername 564 */ 565 public function add() 566 { 567 $sql = Db::sql( <<<SQL 568 SELECT MAX(id) FROM {{user}} 569 SQL 570 ); 571 $this->userid = intval($sql->getOne())+1; 572 573 $sql = Db::sql( <<<SQL 574 INSERT INTO {{user}} 575 (id,name,password_hash,fullname,tel,mail,descr,style,is_admin,password_salt,auth_type,issuer) 576 VALUES( {userid},{name},'','','','','','default',0,'',{type},{issuer} ) 577 SQL 578 ); 579 $sql->setInt ('userid',$this->userid ); 580 $sql->setString ('name' ,$this->name ); 581 $sql->setStringOrNull('issuer',$this->issuer ); 582 $sql->setString ('type' ,$this->type ); 583 584 // Datenbankbefehl ausfuehren 585 $sql->execute(); 586 587 $this->addNewUserGroups(); // Neue Gruppen hinzufuegen. 588 589 $this->renewOTPSecret(); 590 } 591 592 593 594 /** 595 * Enrich a new user with groups. 596 * 597 * Called from add() 598 */ 599 protected function addNewUserGroups() 600 { 601 $newUserConfig = Configuration::subset( ['security','newuser'] ); 602 $templateUser = $newUserConfig->get('copy_user'); 603 604 $userToCopy = null; 605 606 if ( is_int($templateUser)) { 607 $userToCopy = new User( $templateUser ); 608 try { 609 $userToCopy->load(); 610 } catch( ObjectNotFoundException $onfe) { 611 $userToCopy = null; 612 } 613 }elseif ( is_string($templateUser)) { 614 $userToCopy = User::loadWithName( $templateUser,User::AUTH_TYPE_INTERNAL ); 615 } 616 617 if ( $userToCopy ) { 618 foreach( $userToCopy->getGroupIds() as $groupId ) { 619 $this->addGroup( $groupId ); 620 } 621 622 $this->fullname = $userToCopy->fullname; 623 $this->desc = $userToCopy->desc; 624 $this->style = $userToCopy->style; 625 $this->mail = $userToCopy->mail; 626 $this->tel = $userToCopy->tel; 627 $this->language = $userToCopy->language; 628 $this->timezone = $userToCopy->timezone; 629 } 630 631 632 foreach ( $newUserConfig->get('groups',[]) as $group) { 633 634 if ( is_int($group)) { 635 $groupToAdd = new Group( $group ); 636 $groupToAdd->load(); 637 if ( ! $groupToAdd->groupid ) 638 $groupToAdd = null; 639 } 640 elseif ( is_string($group)) { 641 $groupToAdd = Group::loadWithName($group); 642 } 643 644 if ( $groupToAdd ) 645 $this->addGroup( $groupToAdd->groupid ); 646 } 647 648 } 649 650 651 /** 652 * Benutzer entfernen.<br> 653 * Vor dem Entfernen werden alle Referenzen auf diesen Benutzer entfernt:<br> 654 * - "Erzeugt von" f�r diesen Benutzer entfernen.<br> 655 * - "Letzte �nderung von" f�r diesen Benutzer entfernen<br> 656 * - Alle Archivdaten in Dateien mit diesem Benutzer entfernen<br> 657 * - Alle Berechtigungen dieses Benutzers l?schen<br> 658 * - Alle Gruppenzugehoerigkeiten dieses Benutzers l?schen<br> 659 * - Benutzer loeschen<br> 660 */ 661 public function delete() 662 { 663 $db = Db::get(); 664 665 // "Erzeugt von" f�r diesen Benutzer entfernen. 666 $sql = $db->sql( 'UPDATE {{object}} '. 667 'SET create_userid=null '. 668 'WHERE create_userid={userid}' ); 669 $sql->setInt ('userid',$this->userid ); 670 $sql->execute(); 671 672 // "Letzte �nderung von" f�r diesen Benutzer entfernen 673 $sql = $db->sql( 'UPDATE {{object}} '. 674 'SET lastchange_userid=null '. 675 'WHERE lastchange_userid={userid}' ); 676 $sql->setInt ('userid',$this->userid ); 677 $sql->execute(); 678 679 // Alle Archivdaten in Dateien mit diesem Benutzer entfernen 680 $sql = $db->sql( 'UPDATE {{value}} '. 681 'SET lastchange_userid=null '. 682 'WHERE lastchange_userid={userid}' ); 683 $sql->setInt ('userid',$this->userid ); 684 $sql->execute(); 685 686 // Alle Berechtigungen dieses Benutzers l?schen 687 $sql = $db->sql( 'DELETE FROM {{acl}} '. 688 'WHERE userid={userid}' ); 689 $sql->setInt ('userid',$this->userid ); 690 $sql->execute(); 691 692 // Alle Gruppenzugehoerigkeiten dieses Benutzers l?schen 693 $sql = $db->sql( 'DELETE FROM {{usergroup}} '. 694 'WHERE userid={userid}' ); 695 $sql->setInt ('userid',$this->userid ); 696 $sql->execute(); 697 698 // Delete all bookmarks 699 $sql = $db->sql( 'DELETE FROM {{bookmark}} '. 700 'WHERE userid={userid}' ); 701 $sql->setInt ('userid',$this->userid ); 702 $sql->execute(); 703 704 $this->deleteAllLoginTokens(); 705 // Benutzer loeschen 706 $sql = $db->sql( 'DELETE FROM {{user}} '. 707 'WHERE id={userid}' ); 708 $sql->setInt ('userid',$this->userid ); 709 $sql->execute(); 710 711 $this->userid = null; 712 } 713 714 715 /** 716 * Delete all Login tokens for this user. 717 * 718 * @throws \util\exception\DatabaseException 719 */ 720 public function deleteAllLoginTokens() { 721 722 $stmt = Db::sql( <<<SQL 723 DELETE FROM {{auth}} 724 WHERE userid={userid} 725 SQL 726 ); 727 $stmt->setInt ('userid',$this->userid ); 728 $stmt->execute(); 729 } 730 731 732 /** 733 * Ermitteln der Eigenschaften zu diesem Benutzer 734 * 735 * @return array Liste der Eigenschaften als assoziatives Array 736 */ 737 public function getProperties() 738 { 739 return array_merge( parent::getProperties(), [ 740 'id' => $this->userid, 741 'is_admin' => $this->isAdmin, 742 'auth_type' => ($this->type==User::AUTH_TYPE_INTERNAL?'local':'remote') 743 ] ); 744 } 745 746 747 748 /** 749 * Sets a new password for the user. 750 * 751 * if the user account was locked it will be unlocked. 752 * 753 * @param $password string new password 754 * @param $forever int true, wenn Kennwort dauerhaft. 755 */ 756 public function setPassword($password, $forever = true ) 757 { 758 $sql = DB::sql( <<<SQL 759 UPDATE {{user}} 760 SET password_hash={password}, password_algo={algo}, password_expires={expires}, password_fail_count=0, password_locked_until=NULL 761 WHERE id={userid} 762 SQL 763 ); 764 if ( $forever ) { 765 $algo = Password::bestAlgoAvailable(); 766 $expire = null; 767 } 768 else { 769 // cleartext-password, the user must change the password on the next login. 770 $algo = Password::ALGO_PLAIN; 771 $expire = time(); 772 } 773 774 // Hashsumme für Kennwort erzeugen 775 $sql->setIntOrNull('expires',$expire); 776 $sql->setInt ('algo' ,$algo ); 777 $sql->setString('password',Password::hash(Password::pepperPassword($password),$algo) ); 778 $sql->setInt ('userid' ,$this->userid ); 779 780 $sql->execute(); // Updating the password 781 782 // Delete all login tokens, because the user should 783 // use the new password on all devices 784 $this->deleteAllLoginTokens(); 785 } 786 787 788 /** 789 * Gruppen ermitteln, in denen der Benutzer Mitglied ist. 790 * 791 * @return array mit Id:Name 792 */ 793 function getGroups() 794 { 795 if ( !is_array($this->groups) ) 796 { 797 $sql = DB::sql( <<<SQL 798 SELECT {{group}}.id,{{group}}.name FROM {{group}} 799 LEFT JOIN {{usergroup}} ON {{usergroup}}.groupid={{group}}.id 800 WHERE {{usergroup}}.userid={userid} 801 SQL 802 ); 803 $sql->setInt('userid',$this->userid ); 804 $this->groups = $sql->getAssoc(); 805 } 806 807 return $this->groups; 808 } 809 810 811 812 /** 813 * Calculates the effective group list. 814 * 815 * @return array 816 */ 817 function getEffectiveGroups() 818 { 819 $groupIds = array_keys( $this->getGroups() ); 820 $effectiveGroupIds = $groupIds; 821 822 foreach( $groupIds as $id ) { 823 $group = new Group( $id ); 824 $group->load(); 825 $effectiveGroupIds = array_merge( $effectiveGroupIds,$group->getParentGroups() ); 826 } 827 $effectiveGroupIds = array_unique( $effectiveGroupIds ); 828 829 return $effectiveGroupIds; 830 } 831 832 833 834 835 // Gruppen ermitteln, in denen der Benutzer Mitglied ist 836 function getGroupIds() 837 { 838 return array_keys( $this->getGroups() ); 839 840 /* 841 $db = \cms\base\DB::get(); 842 843 $sql = $db->sql( 'SELECT groupid FROM {{usergroup}} '. 844 'WHERE userid={userid}' ); 845 $sql->setInt('userid',$this->userid ); 846 847 return $sql->getCol( $sql ); 848 */ 849 } 850 851 852 // Gruppen ermitteln, in denen der Benutzer *nicht* Mitglied ist 853 function getOtherGroups() 854 { 855 $db = Db::get(); 856 857 $sql = $db->sql( 'SELECT {{group}}.id,{{group}}.name FROM {{group}}'. 858 ' LEFT JOIN {{usergroup}} ON {{usergroup}}.groupid={{group}}.id AND {{usergroup}}.userid={userid}'. 859 ' WHERE {{usergroup}}.userid IS NULL' ); 860 $sql->setInt('userid' ,$this->userid ); 861 862 return $sql->getAssoc(); 863 } 864 865 866 867 /** 868 * Benutzer zu einer Gruppe hinzufuegen. 869 * 870 * @param $groupid int die Gruppen-Id 871 */ 872 function addGroup( $groupid ) 873 { 874 $db = Db::get(); 875 876 $sql = $db->sql('SELECT MAX(id) FROM {{usergroup}}'); 877 $usergroupid = intval($sql->getOne())+1; 878 879 $sql = $db->sql( 'INSERT INTO {{usergroup}} '. 880 ' (id,userid,groupid) '. 881 ' VALUES( {usergroupid},{userid},{groupid} )' ); 882 $sql->setInt('usergroupid',$usergroupid ); 883 $sql->setInt('userid' ,$this->userid ); 884 $sql->setInt('groupid' ,$groupid ); 885 886 $sql->execute(); 887 888 } 889 890 891 892 /** 893 * Benutzer aus Gruppe entfernen. 894 * 895 * @param $groupid int die Gruppen-Id 896 */ 897 function delGroup( $groupid ) 898 { 899 $db = Db::get(); 900 901 $sql = $db->sql( 'DELETE FROM {{usergroup}} '. 902 ' WHERE userid={userid} AND groupid={groupid}' ); 903 $sql->setInt ('userid' ,$this->userid ); 904 $sql->setInt ('groupid' ,$groupid ); 905 906 $sql->execute(); 907 } 908 909 910 /** 911 * Ermitteln aller Berechtigungen des Benutzers.<br> 912 * Diese Daten werden auf der Benutzerseite in der Administration angezeigt. 913 * 914 * @return array 915 */ 916 public function getAllAcls() 917 { 918 $groupClause = $this->getGroupClause(); 919 920 $sql = Db::sql( <<<SQL 921 SELECT {{acl}}.*, 922 {{object}}.projectid, 923 {{language}}.name AS languagename 924 FROM {{acl}} 925 LEFT JOIN {{object}} 926 ON {{object}}.id={{acl}}.objectid 927 LEFT JOIN {{language}} 928 ON {{language}}.id={{acl}}.languageid 929 WHERE ( 930 {{acl}}.type = {usertype} AND {{acl}}.userid={userid} 931 OR {{acl}}.type = {grouptype} AND ($groupClause) 932 OR {{acl}}.type = {alltype} 933 OR {{acl}}.type = {guesttype} 934 ) 935 ORDER BY {{object}}.projectid,{{acl}}.languageid 936 SQL 937 ); 938 $sql->setInt( 'userid' ,$this->userid ); 939 $sql->setInt( 'usertype' ,Permission::TYPE_USER ); 940 $sql->setInt( 'grouptype',Permission::TYPE_GROUP ); 941 $sql->setInt( 'alltype' ,Permission::TYPE_AUTH ); 942 $sql->setInt( 'guesttype',Permission::TYPE_GUEST ); 943 944 $aclList = array(); 945 946 foreach($sql->getAll() as $row ) 947 { 948 $permission = new Permission(); 949 $permission->setDatabaseRow( $row ); 950 $permission->projectid = $row['projectid' ]; 951 if ( intval($permission->languageid) == 0 ) 952 $permission->languagename = Language::lang( Messages::ALL_LANGUAGES); 953 else 954 $permission->languagename = $row['languagename']; 955 $aclList[] = $permission; 956 } 957 958 return $aclList; 959 } 960 961 962 /** 963 * Ermitteln aller Berechtigungen. 964 * @return array Berechtigungen 965 */ 966 function getRights() 967 { 968 throw new \DomainException('User.class::getRights()'); 969 970 // $db = \cms\base\DB::get(); 971 // $var = array(); 972 // 973 // // Alle Projekte lesen 974 // $sql = $db->sql( 'SELECT id,name FROM {{project}}' ); 975 // $projects = $sql->getAssoc( $sql ); 976 // 977 // foreach( $projects as $projectid=>$projectname ) 978 // { 979 // $var[$projectid] = array(); 980 // $var[$projectid]['name'] = $projectname; 981 // $var[$projectid]['folders'] = array(); 982 // $var[$projectid]['rights'] = array(); 983 // 984 // $sql = $db->sql( 'SELECT {{acl}}.* FROM {{acl}}'. 985 // ' LEFT JOIN {{folder}} ON {{acl}}.folderid = {{folder}}.id'. 986 // ' WHERE {{folder}}.projectid={projectid}'. 987 // ' AND {{acl}}.userid={userid}' ); 988 // $sql->setInt('projectid',$projectid ); 989 // $sql->setInt('userid' ,$this->userid ); 990 // 991 // $acls = $sql->getAll( $sql ); 992 // 993 // foreach( $acls as $acl ) 994 // { 995 // $aclid = $acl['id']; 996 // $folder = new Folder( $acl['folderid'] ); 997 // $folder->load(); 998 // $var[$projectid]['rights'][$aclid] = $acl; 999 // $var[$projectid]['rights'][$aclid]['foldername'] = implode(' » ',$folder->parentfolder( false,true )); 1000 // $var[$projectid]['rights'][$aclid]['delete_url'] = Html::url(array('action'=>'user','subaction'=>'delright','aclid'=>$aclid)); 1001 // } 1002 // 1003 // $sql = $db->sql( 'SELECT id FROM {{folder}}'. 1004 // ' WHERE projectid={projectid}' ); 1005 // $sql->setInt('projectid',$projectid); 1006 // $folders = $sql->getCol( $sql ); 1007 // 1008 // $var[$projectid]['folders'] = array(); 1009 // 1010 // foreach( $folders as $folderid ) 1011 // { 1012 // $folder = new Folder( $folderid ); 1013 // $folder->load(); 1014 // $var[$projectid]['folders'][$folderid] = implode(' » ',$folder->parentfolder( false,true )); 1015 // } 1016 // 1017 // asort( $var[$projectid]['folders'] ); 1018 // } 1019 // 1020 // return $var; 1021 } 1022 1023 1024 1025 1026 /** 1027 * Ermitteln aller zur Verfuegung stehenden Stylesheets 1028 * @return array 1029 */ 1030 public function getAvailableStyles() 1031 { 1032 return array_map( function($styleConfig) { 1033 return $styleConfig->get('name',''); 1034 },Configuration::subset('style')->subsets()); 1035 } 1036 1037 /** 1038 * Ueberpruefen des Kennwortes. 1039 * 1040 * Es wird festgestellt, ob das Kennwort dem des Benutzers entspricht. 1041 * Es wird dabei nur gegen die interne Datenbank geprüft. Weitere 1042 * Loginmodule werden nicht aufgerufen! 1043 * Diese Methode darf kein Bestandteil des Logins sein, da nur das Kennwort geprüft wird! 1044 * Kennwortablauf und Token werden nicht geprüft! 1045 */ 1046 function checkPassword( $password ) 1047 { 1048 $db = Db::get(); 1049 // Laden des Benutzers aus der Datenbank, um Password-Hash zu ermitteln. 1050 $sql = $db->sql( 'SELECT * FROM {{user}}'. 1051 ' WHERE id={userid}' ); 1052 $sql->setInt( 'userid',$this->userid ); 1053 $row_user = $sql->getRow(); 1054 1055 // Pruefen ob Kennwort mit Datenbank uebereinstimmt. 1056 return Password::check(Password::pepperPassword($password),$row_user['password_hash'],$row_user['password_algo']); 1057 } 1058 1059 1060 1061 /** 1062 * Ermittelt projektübergreifend die letzten Änderungen des Benutzers. 1063 * 1064 * @return array <string, unknown> 1065 */ 1066 public function getLastChanges( $fromDate ) 1067 { 1068 $sql = Db::sql( <<<SQL 1069 SELECT {{object}}.id as objectid, 1070 {{object}}.filename as filename, 1071 {{object}}.typeid as typeid, 1072 {{object}}.lastchange_date as lastchange_date, 1073 {{project}}.id as projectid, 1074 {{project}}.name as projectname 1075 FROM {{object}} 1076 LEFT JOIN {{project}} 1077 ON {{object}}.projectid = {{project}}.id 1078 WHERE {{object}}.lastchange_userid = {userid} AND 1079 {{object}}.lastchange_date >= {fromdate} 1080 ORDER BY {{object}}.lastchange_date DESC 1081 SQL 1082 ); 1083 1084 $sql->setInt( 'userid' , $this->userid ); 1085 $sql->setInt( 'fromdate', $fromDate ); 1086 1087 return $sql->getAll(); 1088 1089 } 1090 1091 1092 1093 1094 /** 1095 * Timeline 1096 * 1097 * @return array <string, unknown> 1098 */ 1099 public function getValueChanges( $fromDate ) 1100 { 1101 $sql = Db::sql( <<<SQL 1102 SELECT {{value}}.lastchange_date as lastchange_date, 1103 {{value}}.lastchange_userid as userid, 1104 {{user}}.fullname as username, 1105 {{value}}.linkobjectid as linkobjectid, 1106 {{value}}.text as linkobjectid, 1107 {{value}}.number as linkobjectid, 1108 {{value}}.date as date, 1109 {{value}}.format as format, 1110 {{value}}.id as valueid, 1111 {{templatemodel}}.templateid as templateid, 1112 {{file}}.objectid as fileid, 1113 {{pagecontent}}.elementid as elementid, 1114 {{pagecontent}}.languageid as languageid, 1115 {{page}}.objectid as pageid 1116 FROM {{value}} 1117 LEFT JOIN {{templatemodel}} 1118 ON {{templatemodel}}.contentid = {{value}}.contentid 1119 LEFT JOIN {{file}} 1120 ON {{value}}.contentid = {{file}}.contentid 1121 LEFT JOIN {{pagecontent}} 1122 ON {{value}}.contentid = {{pagecontent}}.contentid 1123 LEFT JOIN {{page}} 1124 ON {{pagecontent}}.pageid = {{page}}.id 1125 LEFT JOIN {{user}} 1126 ON {{value}}.lastchange_userid = {{user}}.id 1127 WHERE {{value}}.lastchange_userid = {userid} AND 1128 {{value}}.lastchange_date >= {fromdate} 1129 ORDER BY {{value}}.lastchange_date DESC 1130 SQL 1131 ); 1132 1133 $sql->setInt( 'userid' , $this->userid ); 1134 $sql->setInt( 'fromdate', $fromDate ); 1135 1136 return $sql->getAll(); 1137 1138 } 1139 1140 1141 1142 1143 /** 1144 * Erzeugt ein neues OTP-Secret. 1145 */ 1146 public function renewOTPSecret() { 1147 1148 $secret = Password::randomHexString(64); 1149 1150 $stmt = DB::sql('UPDATE {{user}} SET otp_secret={secret} WHERE id={id}'); 1151 1152 $stmt->setString( 'secret', $secret ); 1153 $stmt->setInt ( 'id' , $this->userid ); 1154 1155 $stmt->execute(); 1156 } 1157 1158 1159 public function getId() 1160 { 1161 return $this->userid; 1162 } 1163 1164 1165 } 1166
Downloadmodules/cms/model/User.class.php
History Sun, 5 Jan 2025 23:14:07 +0100 Jan Dankert New UI feature: Show a timeline in the start page. Sun, 8 Dec 2024 20:57:25 +0100 Jan Dankert History-Size is now limited to 1 year. Sun, 8 Dec 2024 20:56:47 +0100 Jan Dankert New: Users are now able to store bookmarks. Sat, 7 Dec 2024 20:34:52 +0100 Jan Dankert UI: New Selection for user color scheme. Sun, 4 Dec 2022 16:40:34 +0100 Jan Dankert Refactoring without changes. Fri, 15 Apr 2022 20:45:19 +0200 dankert Refactoring: Code cleanup. Fri, 15 Apr 2022 14:51:22 +0200 dankert Refactoring: User,Config and Database info is now stored in the Request, because so there is no session required for clients which are using Basic Authorization. Wed, 9 Mar 2022 00:53:10 +0100 dankert Fix: Setting the correct timezone from the user property. Mon, 7 Feb 2022 22:52:12 +0100 dankert Password lock check is moved into "InternalAuth", because it must be called on all authentication requests. Mon, 6 Dec 2021 21:34:53 +0100 dankert Fix: Read ACL for a user. Sun, 5 Dec 2021 20:33:24 +0100 dankert Cleanup: Removed unusable properties from class 'Value' and 'BaseObject'. Sun, 7 Mar 2021 00:10:20 +0100 Jan Dankert Refactoring: Hopefully more performance while accessing the database resultsets. Thu, 4 Mar 2021 01:15:06 +0100 Jan Dankert Fix: Calculate correct parent groups of group. Mon, 4 Jan 2021 23:14:09 +0100 Jan Dankert New: Groups may contain subgroups. Users within a group inherit the permissions of all parent groups. Mon, 4 Jan 2021 19:03:18 +0100 Jan Dankert Refactoring: ACL class is renamed to Permission, because most RBAC/DMAC concepts are calling it a permission. Mon, 30 Nov 2020 10:31:50 +0100 Jan Dankert Fix: Reset password fail counter after successful login. Sat, 28 Nov 2020 00:53:41 +0100 Jan Dankert New: Lock password after a number of login fails. Thu, 19 Nov 2020 00:45:44 +0100 Jan Dankert Security fix: We must update the login token on every login; Administrators are able to see the login tokens of users. Wed, 18 Nov 2020 01:46:36 +0100 Jan Dankert Refactoring of model classes: New method persist() and some other cleanups. Sat, 14 Nov 2020 22:02:21 +0100 Jan Dankert Fixed: Notices may display a message. Sun, 1 Nov 2020 22:27:37 +0100 Jan Dankert Refactoring: The active menu entrys are now controlled by the action class; The user switch function is now available as a menu entry. Sun, 1 Nov 2020 20:29:38 +0100 Jan Dankert Fix: Password change only possible for internal users. Sun, 1 Nov 2020 03:08:55 +0100 Jan Dankert Replaced the calls to "Configuration::rawConfig()" with the OO style calls; Cleanup LoginAction. Sat, 31 Oct 2020 03:52:03 +0100 Jan Dankert Fix: Better detection if password change is possbile. Sat, 31 Oct 2020 00:43:29 +0100 Jan Dankert New: Support for OpenId Connect; Removed: Support for LDAP. Sun, 25 Oct 2020 02:11:58 +0200 Jan Dankert Fix: The config was not read in the templates :-O. Fri, 2 Oct 2020 23:11:48 +0200 Jan Dankert Cleanup: No '.inputholder' any more, notices with links to objects. Tue, 29 Sep 2020 22:17:11 +0200 Jan Dankert Refactoring: Do not use global constants. Sat, 26 Sep 2020 10:32:02 +0200 Jan Dankert Refactoring: No global $conf array any more. Sat, 26 Sep 2020 04:26:55 +0200 Jan Dankert Refactoring: read configuration values with a class. Sat, 26 Sep 2020 04:03:53 +0200 Jan Dankert Refactoring: read language keys with a class. Sat, 26 Sep 2020 02:26:39 +0200 Jan Dankert Refactoring: No global functions any more, the database object is read from the Db class. Thu, 10 Sep 2020 18:30:16 +0200 Jan Dankert Some code cleanup, killing the old "checkMenu()"-methods. Mon, 17 Aug 2020 22:52:37 +0200 Jan Dankert Cleanup: Killing the old odd 'GLOBAL_' message prefixes. Sun, 23 Feb 2020 04:01:30 +0100 Jan Dankert Refactoring with Namespaces for the cms modules, part 1: moving.