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