Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4058643
wrapOldPasswords.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
wrapOldPasswords.php
View Options
<?php
/**
* Maintenance script to wrap all old-style passwords in a layered type
*
* @license GPL-2.0-or-later
* @file
* @ingroup Maintenance
*/
use
MediaWiki\Maintenance\Maintenance
;
use
MediaWiki\Password\LayeredParameterizedPassword
;
use
MediaWiki\Password\ParameterizedPassword
;
use
MediaWiki\User\User
;
use
Wikimedia\Rdbms\IExpression
;
use
Wikimedia\Rdbms\LikeValue
;
// @codeCoverageIgnoreStart
require_once
__DIR__
.
'/Maintenance.php'
;
// @codeCoverageIgnoreEnd
/**
* Maintenance script to wrap all passwords of a certain type in a specified layered
* type that wraps around the old type.
*
* @since 1.24
* @ingroup Maintenance
*/
class
WrapOldPasswords
extends
Maintenance
{
public
function
__construct
()
{
parent
::
__construct
();
$this
->
addDescription
(
'Wrap all passwords of a certain type in a new layered type. '
.
'The script runs in dry-run mode by default (use --update to update rows)'
);
$this
->
addOption
(
'type'
,
'Password type to wrap passwords in (must inherit LayeredParameterizedPassword)'
,
true
,
true
);
$this
->
addOption
(
'verbose'
,
'Enables verbose output'
,
false
,
false
,
'v'
);
$this
->
addOption
(
'update'
,
'Actually wrap passwords'
,
false
,
false
,
'u'
);
$this
->
setBatchSize
(
3
);
}
public
function
execute
()
{
$passwordFactory
=
$this
->
getServiceContainer
()->
getPasswordFactory
();
$typeInfo
=
$passwordFactory
->
getTypes
();
$layeredType
=
$this
->
getOption
(
'type'
);
// Check that type exists and is a layered type
if
(
!
isset
(
$typeInfo
[
$layeredType
]
)
)
{
$this
->
fatalError
(
'Undefined password type'
);
}
$passObj
=
$passwordFactory
->
newFromType
(
$layeredType
);
if
(
!
$passObj
instanceof
LayeredParameterizedPassword
)
{
$this
->
fatalError
(
'Layered parameterized password type must be used.'
);
}
// Extract the first layer type
$typeConfig
=
$typeInfo
[
$layeredType
];
$firstType
=
$typeConfig
[
'types'
][
0
];
$update
=
$this
->
hasOption
(
'update'
);
// Get a list of password types that are applicable
$dbw
=
$this
->
getPrimaryDB
();
$count
=
0
;
$minUserId
=
0
;
while
(
true
)
{
if
(
$update
)
{
$this
->
beginTransaction
(
$dbw
,
__METHOD__
);
}
$start
=
microtime
(
true
);
$res
=
$dbw
->
newSelectQueryBuilder
()
->
select
(
[
'user_id'
,
'user_name'
,
'user_password'
]
)
->
lockInShareMode
()
->
from
(
'user'
)
->
where
(
[
$dbw
->
expr
(
'user_id'
,
'>'
,
$minUserId
),
$dbw
->
expr
(
'user_password'
,
IExpression
::
LIKE
,
new
LikeValue
(
":$firstType:"
,
$dbw
->
anyString
()
)
),
]
)
->
orderBy
(
'user_id'
)
->
limit
(
$this
->
getBatchSize
()
)
->
caller
(
__METHOD__
)->
fetchResultSet
();
if
(
$res
->
numRows
()
===
0
)
{
if
(
$update
)
{
$this
->
commitTransaction
(
$dbw
,
__METHOD__
);
}
break
;
}
/** @var User[] $updateUsers */
$updateUsers
=
[];
foreach
(
$res
as
$row
)
{
$user
=
User
::
newFromId
(
$row
->
user_id
);
/** @var ParameterizedPassword $password */
$password
=
$passwordFactory
->
newFromCiphertext
(
$row
->
user_password
);
'@phan-var ParameterizedPassword $password'
;
/** @var LayeredParameterizedPassword $layeredPassword */
$layeredPassword
=
$passwordFactory
->
newFromType
(
$layeredType
);
'@phan-var LayeredParameterizedPassword $layeredPassword'
;
$layeredPassword
->
partialCrypt
(
$password
);
if
(
$this
->
hasOption
(
'verbose'
)
)
{
$this
->
output
(
"Updating password for user {$row->user_name} ({$row->user_id}) from "
.
"type {$password->getType()} to {$layeredPassword->getType()}.
\n
"
);
}
$count
++;
if
(
$update
)
{
$updateUsers
[]
=
$user
;
$dbw
->
newUpdateQueryBuilder
()
->
update
(
'user'
)
->
set
(
[
'user_password'
=>
$layeredPassword
->
toString
()
]
)
->
where
(
[
'user_id'
=>
$row
->
user_id
]
)
->
caller
(
__METHOD__
)
->
execute
();
}
$minUserId
=
$row
->
user_id
;
}
if
(
$update
)
{
$this
->
commitTransaction
(
$dbw
,
__METHOD__
);
// Clear memcached so old passwords are wiped out
foreach
(
$updateUsers
as
$user
)
{
$user
->
clearSharedCache
(
'refresh'
);
}
}
$this
->
output
(
"Last id processed: $minUserId; Actually updated: $count...
\n
"
);
$delta
=
microtime
(
true
)
-
$start
;
$this
->
output
(
sprintf
(
"%4d passwords wrapped in %6.2fms (%6.2fms each)
\n
"
,
$res
->
numRows
(),
$delta
*
1000.0
,
(
$delta
/
$res
->
numRows
()
)
*
1000.0
)
);
}
if
(
$update
)
{
$this
->
output
(
"$count users rows updated.
\n
"
);
}
else
{
$this
->
output
(
"$count user rows found using old password formats. "
.
"Run script again with --update to update these rows.
\n
"
);
}
}
}
// @codeCoverageIgnoreStart
$maintClass
=
WrapOldPasswords
::
class
;
require_once
RUN_MAINTENANCE_IF_MAIN
;
// @codeCoverageIgnoreEnd
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 16 2026, 04:53 (12 w, 22 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
af/45/d7fc94785ec1e95db99a594871bd
Default Alt Text
wrapOldPasswords.php (4 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment