Show password policy flags on Special:PasswordPolicies

Follow-up to I28c31fc4ea.

Also improves what policy values are considered disabled, documents
how to extend core checks/flags and adds a structure test for it.

Bug: T118774
Change-Id: I66bf396e8e8a8c310a47ba337abe9070e7e83ff6
This commit is contained in:
Gergő Tisza 2019-01-19 18:16:51 -08:00
parent 56fa42d433
commit 22c8cda841
No known key found for this signature in database
GPG key ID: C34FEC97E6257F96
6 changed files with 92 additions and 14 deletions

View file

@ -4483,6 +4483,13 @@ $wgCentralIdLookupProvider = 'local';
* 100,000 commonly used passwords. Due to the size of the list this
* is a probabilistic test.
*
* If you add custom checks, for Special:PasswordPolicies to display them correctly,
* every check should have a corresponding passwordpolicies-policy-<check> message,
* and every settings field other than 'value' should have a corresponding
* passwordpolicies-policyflag-<flag> message (<check> and <flag> are in lowercase).
* The check message receives the policy value as a parameter, the flag message
* receives the flag value (or values if it's an array).
*
* @since 1.26
* @see PasswordPolicyChecks
* @see User::checkPasswordValidity()

View file

@ -136,20 +136,37 @@ class SpecialPasswordPolicies extends SpecialPage {
);
$ret = [];
foreach ( $groupPolicies as $gp => $val ) {
if ( $val === false ) {
// Policy isn't enabled, so no need to dislpay it
continue;
} elseif ( $val === true ) {
$msg = $this->msg( 'passwordpolicies-policy-' . strtolower( $gp ) );
} else {
$msg = $this->msg( 'passwordpolicies-policy-' . strtolower( $gp ) )->numParams( $val );
foreach ( $groupPolicies as $gp => $settings ) {
if ( !is_array( $settings ) ) {
$settings = [ 'value' => $settings ];
}
$val = $settings['value'];
$flags = array_diff_key( $settings, [ 'value' => true ] );
if ( !$val ) {
// Policy isn't enabled, so no need to display it
continue;
}
$msg = $this->msg( 'passwordpolicies-policy-' . strtolower( $gp ) )->numParams( $val );
$flagMsgs = [];
foreach ( array_filter( $flags ) as $flag => $value ) {
$flagMsg = $this->msg( 'passwordpolicies-policyflag-' . strtolower( $flag ) );
$flagMsg->params( $value );
$flagMsgs[] = $flagMsg;
}
if ( $flagMsgs ) {
$ret[] = $this->msg(
'passwordpolicies-policy-displaywithflags',
$msg,
'<span class="mw-passwordpolicies-policy-name">' . $gp . '</span>',
$this->getLanguage()->commaList( $flagMsgs )
)->parse();
} else {
$ret[] = $this->msg(
'passwordpolicies-policy-display',
$msg,
'<span class="mw-passwordpolicies-policy-name">' . $gp . '</span>'
)->parse();
}
$ret[] = $this->msg(
'passwordpolicies-policy-display',
$msg,
'<span class="mw-passwordpolicies-policy-name">' . $gp . '</span>'
)->parse();
}
if ( $ret === [] ) {
return '';

View file

@ -4173,6 +4173,7 @@
"passwordpolicies-group": "Group",
"passwordpolicies-policies": "Policies",
"passwordpolicies-policy-display": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span>",
"passwordpolicies-policy-displaywithflags": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span> <span class=\"passwordpolicies-policy-flags\">($3)</span>",
"passwordpolicies-policy-minimalpasswordlength": "Password must be at least $1 {{PLURAL:$1|character|characters}} long",
"passwordpolicies-policy-minimumpasswordlengthtologin": "Password must be at least $1 {{PLURAL:$1|character|characters}} long to be able to login",
"passwordpolicies-policy-passwordcannotmatchusername": "Password cannot be the same as username",
@ -4180,6 +4181,7 @@
"passwordpolicies-policy-maximalpasswordlength": "Password must be less than $1 {{PLURAL:$1|character|characters}} long",
"passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}",
"passwordpolicies-policy-passwordnotinlargeblacklist": "Password cannot be in the list of 100,000 most commonly used passwords.",
"passwordpolicies-policyflag-forcechange": "must change on login",
"easydeflate-invaliddeflate": "Content provided is not properly deflated",
"unprotected-js": "For security reasons JavaScript cannot be loaded from unprotected pages. Please only create javascript in the MediaWiki: namespace or as a User subpage"
}

View file

@ -4377,7 +4377,8 @@
"passwordpolicies-summary": "The description used on [[Special:PasswordPolicies]].\n\nRefers to {{msg-mw|Passwordpolicies-helppage}}.",
"passwordpolicies-group": "The title of the column in the table, about user groups (like you are in the ''translator'' group).\n\n{{Identical|Group}}\n{{Related|Passwordpolicies}}",
"passwordpolicies-policies": "The title of the column in the table, about password policies.\n{{Related|Passwordpolicies}}",
"passwordpolicies-policy-display": "{{optional}}\nParameters:\n* $1 - the text from the \"passwordpolicies-policy-...\" messages, i.e. {{msg-mw|passwordpolicies-policy-minimalpasswordlength}}\n* $2 - the name of this password policy",
"passwordpolicies-policy-display": "{{optional}}\nParameters:\n* $1 - the text from the \"passwordpolicies-policy-...\" messages, e.g. {{msg-mw|passwordpolicies-policy-minimalpasswordlength}}\n* $2 - the name of this password policy",
"passwordpolicies-policy-displaywithflags": "{{optional}}\nParameters:\n* $1 - the text from the \"passwordpolicies-policy-...\" messages, i.e. {{msg-mw|passwordpolicies-policy-minimalpasswordlength}}\n* $2 - the name of this password policy\n* $3 - comma-separated list of the text from the \"passwordpolicies-policyflag-...\" messages, e.g. {{msg-mw|passwordpolicies-policyflag-forcechange}}",
"passwordpolicies-policy-minimalpasswordlength": "Password policy that enforces a minimum number of characters a password must be. $1 - minimum number of characters that a password can be",
"passwordpolicies-policy-minimumpasswordlengthtologin": "Password policy that enforces a minimum number of characters a password must be to be able to login to the wiki. $1 - minimum number of characters that a password can be to be able to login",
"passwordpolicies-policy-passwordcannotmatchusername": "Password policy that enforces that the password of the account cannot be the same as the username",
@ -4385,6 +4386,7 @@
"passwordpolicies-policy-maximalpasswordlength": "Password policy that enforces a maximum number of characters a password must be. $1 - maximum number of characters that a password can be",
"passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against",
"passwordpolicies-policy-passwordnotinlargeblacklist": "Password policy that enforces that a password is not in a list of 100,000 number of \"popular\" passwords.",
"passwordpolicies-policyflag-forcechange": "Password policy flag that enforces changing invalid passwords on login.",
"easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly",
"unprotected-js": "Error message shown when trying to load javascript via action=raw that is not protected"
}

View file

@ -156,3 +156,7 @@
.mw-passwordpolicies-table tr {
vertical-align: top;
}
.passwordpolicies-policy-flags {
font-size: 90%;
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @coversNothing
*/
class PasswordPolicyStructureTest extends MediaWikiTestCase {
public function provideChecks() {
global $wgPasswordPolicy;
foreach ( $wgPasswordPolicy['checks'] as $name => $callback ) {
yield [ $name ];
}
}
public function provideFlags() {
global $wgPasswordPolicy;
// This won't actually find all flags, just the ones in use. Can't really be helped,
// other than adding the core flags here.
$flags = [ 'forceChange' ];
foreach ( $wgPasswordPolicy['policies'] as $group => $checks ) {
foreach ( $checks as $check => $settings ) {
if ( is_array( $settings ) ) {
$flags = array_merge( $flags, array_diff( $settings, [ 'value' ] ) );
}
}
}
foreach ( $flags as $flag ) {
yield [ $flag ];
}
}
/** @dataProvider provideChecks */
public function testCheckMessage( $check ) {
$msg = wfMessage( 'passwordpolicies-policy-' . strtolower( $check ) );
$this->assertTrue( $msg->exists() );
}
/** @dataProvider provideFlags */
public function testFlagMessage( $flag ) {
$msg = wfMessage( 'passwordpolicies-policyflag-' . strtolower( $flag ) );
$this->assertTrue( $msg->exists() );
}
}