TestUser.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <?php
  2. use MediaWiki\MediaWikiServices;
  3. /**
  4. * Wraps the user object, so we can also retain full access to properties
  5. * like password if we log in via the API.
  6. */
  7. class TestUser {
  8. /**
  9. * @var string
  10. */
  11. private $username;
  12. /**
  13. * @var string
  14. */
  15. private $password;
  16. /**
  17. * @var User
  18. */
  19. private $user;
  20. private function assertNotReal() {
  21. global $wgDBprefix;
  22. if ( $wgDBprefix !== MediaWikiTestCase::DB_PREFIX &&
  23. $wgDBprefix !== MediaWikiTestCase::ORA_DB_PREFIX
  24. ) {
  25. throw new MWException( "Can't create user on real database" );
  26. }
  27. }
  28. public function __construct( $username, $realname = 'Real Name',
  29. $email = 'sample@example.com', $groups = []
  30. ) {
  31. $this->assertNotReal();
  32. $this->username = $username;
  33. $this->password = 'TestUser';
  34. $this->user = User::newFromName( $this->username );
  35. $this->user->load();
  36. // In an ideal world we'd have a new wiki (or mock data store) for every single test.
  37. // But for now, we just need to create or update the user with the desired properties.
  38. // we particularly need the new password, since we just generated it randomly.
  39. // In core MediaWiki, there is no functionality to delete users, so this is the best we can do.
  40. if ( !$this->user->isLoggedIn() ) {
  41. // create the user
  42. $this->user = User::createNew(
  43. $this->username, [
  44. "email" => $email,
  45. "real_name" => $realname
  46. ]
  47. );
  48. if ( !$this->user ) {
  49. throw new MWException( "Error creating TestUser " . $username );
  50. }
  51. }
  52. // Update the user to use the password and other details
  53. $this->setPassword( $this->password );
  54. $change = $this->setEmail( $email ) ||
  55. $this->setRealName( $realname );
  56. // Adjust groups by adding any missing ones and removing any extras
  57. $currentGroups = $this->user->getGroups();
  58. foreach ( array_diff( $groups, $currentGroups ) as $group ) {
  59. $this->user->addGroup( $group );
  60. }
  61. foreach ( array_diff( $currentGroups, $groups ) as $group ) {
  62. $this->user->removeGroup( $group );
  63. }
  64. if ( $change ) {
  65. // Disable CAS check before saving. The User object may have been initialized from cached
  66. // information that may be out of whack with the database during testing. If tests were
  67. // perfectly isolated, this would not happen. But if it does happen, let's just ignore the
  68. // inconsistency, and just write the data we want - during testing, we are not worried
  69. // about data loss.
  70. $this->user->mTouched = '';
  71. $this->user->saveSettings();
  72. }
  73. }
  74. /**
  75. * @param string $realname
  76. * @return bool
  77. */
  78. private function setRealName( $realname ) {
  79. if ( $this->user->getRealName() !== $realname ) {
  80. $this->user->setRealName( $realname );
  81. return true;
  82. }
  83. return false;
  84. }
  85. /**
  86. * @param string $email
  87. * @return bool
  88. */
  89. private function setEmail( $email ) {
  90. if ( $this->user->getEmail() !== $email ) {
  91. $this->user->setEmail( $email );
  92. return true;
  93. }
  94. return false;
  95. }
  96. /**
  97. * @param string $password
  98. */
  99. private function setPassword( $password ) {
  100. self::setPasswordForUser( $this->user, $password );
  101. }
  102. /**
  103. * Set the password on a testing user
  104. *
  105. * This assumes we're still using the generic AuthManager config from
  106. * PHPUnitMaintClass::finalSetup(), and just sets the password in the
  107. * database directly.
  108. * @param User $user
  109. * @param string $password
  110. */
  111. public static function setPasswordForUser( User $user, $password ) {
  112. if ( !$user->getId() ) {
  113. throw new MWException( "Passed User has not been added to the database yet!" );
  114. }
  115. $dbw = wfGetDB( DB_MASTER );
  116. $row = $dbw->selectRow(
  117. 'user',
  118. [ 'user_password' ],
  119. [ 'user_id' => $user->getId() ],
  120. __METHOD__
  121. );
  122. if ( !$row ) {
  123. throw new MWException( "Passed User has an ID but is not in the database?" );
  124. }
  125. $passwordFactory = MediaWikiServices::getInstance()->getPasswordFactory();
  126. if ( !$passwordFactory->newFromCiphertext( $row->user_password )->equals( $password ) ) {
  127. $passwordHash = $passwordFactory->newFromPlaintext( $password );
  128. $dbw->update(
  129. 'user',
  130. [ 'user_password' => $passwordHash->toString() ],
  131. [ 'user_id' => $user->getId() ],
  132. __METHOD__
  133. );
  134. }
  135. }
  136. /**
  137. * @since 1.25
  138. * @return User
  139. */
  140. public function getUser() {
  141. return $this->user;
  142. }
  143. /**
  144. * @since 1.25
  145. * @return string
  146. */
  147. public function getPassword() {
  148. return $this->password;
  149. }
  150. }