wrapOldPasswords.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. <?php
  2. /**
  3. * Maintenance script to wrap all old-style passwords in a layered type
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Maintenance
  22. */
  23. require_once __DIR__ . '/Maintenance.php';
  24. /**
  25. * Maintenance script to wrap all passwords of a certain type in a specified layered
  26. * type that wraps around the old type.
  27. *
  28. * @since 1.24
  29. * @ingroup Maintenance
  30. */
  31. class WrapOldPasswords extends Maintenance {
  32. public function __construct() {
  33. parent::__construct();
  34. $this->addDescription( 'Wrap all passwords of a certain type in a new layered type' );
  35. $this->addOption( 'type',
  36. 'Password type to wrap passwords in (must inherit LayeredParameterizedPassword)', true, true );
  37. $this->addOption( 'verbose', 'Enables verbose output', false, false, 'v' );
  38. $this->setBatchSize( 100 );
  39. }
  40. public function execute() {
  41. global $wgAuth;
  42. if ( !$wgAuth->allowSetLocalPassword() ) {
  43. $this->error( '$wgAuth does not allow local passwords. Aborting.', true );
  44. }
  45. $passwordFactory = new PasswordFactory();
  46. $passwordFactory->init( RequestContext::getMain()->getConfig() );
  47. $typeInfo = $passwordFactory->getTypes();
  48. $layeredType = $this->getOption( 'type' );
  49. // Check that type exists and is a layered type
  50. if ( !isset( $typeInfo[$layeredType] ) ) {
  51. $this->error( 'Undefined password type', true );
  52. }
  53. $passObj = $passwordFactory->newFromType( $layeredType );
  54. if ( !$passObj instanceof LayeredParameterizedPassword ) {
  55. $this->error( 'Layered parameterized password type must be used.', true );
  56. }
  57. // Extract the first layer type
  58. $typeConfig = $typeInfo[$layeredType];
  59. $firstType = $typeConfig['types'][0];
  60. // Get a list of password types that are applicable
  61. $dbw = $this->getDB( DB_MASTER );
  62. $typeCond = 'user_password' . $dbw->buildLike( ":$firstType:", $dbw->anyString() );
  63. $minUserId = 0;
  64. do {
  65. $this->beginTransaction( $dbw, __METHOD__ );
  66. $res = $dbw->select( 'user',
  67. [ 'user_id', 'user_name', 'user_password' ],
  68. [
  69. 'user_id > ' . $dbw->addQuotes( $minUserId ),
  70. $typeCond
  71. ],
  72. __METHOD__,
  73. [
  74. 'ORDER BY' => 'user_id',
  75. 'LIMIT' => $this->mBatchSize,
  76. 'LOCK IN SHARE MODE',
  77. ]
  78. );
  79. /** @var User[] $updateUsers */
  80. $updateUsers = [];
  81. foreach ( $res as $row ) {
  82. if ( $this->hasOption( 'verbose' ) ) {
  83. $this->output( "Updating password for user {$row->user_name} ({$row->user_id}).\n" );
  84. }
  85. $user = User::newFromId( $row->user_id );
  86. /** @var ParameterizedPassword $password */
  87. $password = $passwordFactory->newFromCiphertext( $row->user_password );
  88. /** @var LayeredParameterizedPassword $layeredPassword */
  89. $layeredPassword = $passwordFactory->newFromType( $layeredType );
  90. $layeredPassword->partialCrypt( $password );
  91. $updateUsers[] = $user;
  92. $dbw->update( 'user',
  93. [ 'user_password' => $layeredPassword->toString() ],
  94. [ 'user_id' => $row->user_id ],
  95. __METHOD__
  96. );
  97. $minUserId = $row->user_id;
  98. }
  99. $this->commitTransaction( $dbw, __METHOD__ );
  100. // Clear memcached so old passwords are wiped out
  101. foreach ( $updateUsers as $user ) {
  102. $user->clearSharedCache();
  103. }
  104. } while ( $res->numRows() );
  105. }
  106. }
  107. $maintClass = "WrapOldPasswords";
  108. require_once RUN_MAINTENANCE_IF_MAIN;