MediaWiki master
createAndPromote.php
Go to the documentation of this file.
1<?php
26require_once __DIR__ . '/Maintenance.php';
27
31
38 private static $permitRoles = [ 'sysop', 'bureaucrat', 'interface-admin', 'bot' ];
39
40 public function __construct() {
41 parent::__construct();
42 $this->addDescription( 'Create a new user account and/or grant it additional rights' );
43 $this->addOption(
44 'force',
45 'If account exists already, just grant it rights or change password.'
46 );
47 foreach ( self::$permitRoles as $role ) {
48 $this->addOption( $role, "Add the account to the {$role} group" );
49 }
50
51 $this->addOption(
52 'custom-groups',
53 'Comma-separated list of groups to add the user to',
54 false,
55 true
56 );
57
58 $this->addOption(
59 'reason',
60 'Reason for account creation and user rights assignment to log to wiki',
61 false,
62 true
63 );
64
65 $this->addArg( 'username', 'Username of new user' );
66 $this->addArg( 'password', 'Password to set', false );
67 }
68
69 public function execute() {
70 $username = $this->getArg( 0 );
71 $password = $this->getArg( 1 );
72 $force = $this->hasOption( 'force' );
73 $inGroups = [];
74 $services = $this->getServiceContainer();
75
76 $user = $services->getUserFactory()->newFromName( $username );
77 if ( !is_object( $user ) ) {
78 $this->fatalError( 'invalid username.' );
79 }
80
81 $exists = ( $user->idForName() !== 0 );
82
83 if ( $exists && !$force ) {
84 $this->fatalError( 'Account exists. Perhaps you want the --force option?' );
85 } elseif ( !$exists && !$password ) {
86 $this->error( 'Argument <password> required!' );
87 $this->maybeHelp( true );
88 } elseif ( $exists ) {
89 $inGroups = $services->getUserGroupManager()->getUserGroups( $user );
90 }
91
92 $groups = array_filter( self::$permitRoles, [ $this, 'hasOption' ] );
93 if ( $this->hasOption( 'custom-groups' ) ) {
94 $allGroups = array_fill_keys( $services->getUserGroupManager()->listAllGroups(), true );
95 $customGroupsText = $this->getOption( 'custom-groups' );
96 if ( $customGroupsText !== '' ) {
97 $customGroups = explode( ',', $customGroupsText );
98 foreach ( $customGroups as $customGroup ) {
99 if ( isset( $allGroups[$customGroup] ) ) {
100 $groups[] = trim( $customGroup );
101 } else {
102 $this->output( "$customGroup is not a valid group, ignoring!\n" );
103 }
104 }
105 }
106 }
107
108 $promotions = array_diff(
109 $groups,
110 $inGroups
111 );
112
113 if ( $exists && !$password && count( $promotions ) === 0 ) {
114 $this->output( "Account exists and nothing to do.\n" );
115
116 return;
117 } elseif ( count( $promotions ) !== 0 ) {
118 $dbDomain = WikiMap::getCurrentWikiDbDomain()->getId();
119 $promoText = "User:{$username} into " . implode( ', ', $promotions ) . "...\n";
120 if ( $exists ) {
121 $this->output( "$dbDomain: Promoting $promoText" );
122 } else {
123 $this->output( "$dbDomain: Creating and promoting $promoText" );
124 }
125 }
126
127 if ( !$exists ) {
128 // Verify the password meets the password requirements before creating.
129 // This check is repeated below to account for differences between
130 // the password policy for regular users and for users in certain groups.
131 if ( $password ) {
132 $status = $user->checkPasswordValidity( $password );
133
134 if ( !$status->isGood() ) {
135 $this->fatalError( $status->getMessage( false, false, 'en' )->text() );
136 }
137 }
138
139 // Create the user via AuthManager as there may be various side
140 // effects that are performed by the configured AuthManager chain.
141 $status = $this->getServiceContainer()->getAuthManager()->autoCreateUser(
142 $user,
143 MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_MAINT,
144 false
145 );
146 if ( !$status->isGood() ) {
147 $this->fatalError( $status->getMessage( false, false, 'en' )->text() );
148 }
149 }
150
151 if ( $promotions ) {
152 // Add groups before changing password, as the password policy for certain groups has
153 // stricter requirements.
154 $userGroupManager = $services->getUserGroupManager();
155 $userGroupManager->addUserToMultipleGroups( $user, $promotions );
156 $reason = $this->getOption( 'reason' ) ?: '';
157 $this->addLogEntry( $user, $inGroups, array_merge( $inGroups, $promotions ), $reason );
158 }
159
160 if ( $password ) {
161 # Try to set the password
162 try {
163 $status = $user->changeAuthenticationData( [
164 'username' => $user->getName(),
165 'password' => $password,
166 'retype' => $password,
167 ] );
168 if ( !$status->isGood() ) {
169 throw new PasswordError( $status->getMessage( false, false, 'en' )->text() );
170 }
171 if ( $exists ) {
172 $this->output( "Password set.\n" );
173 $user->saveSettings();
174 }
175 } catch ( PasswordError $pwe ) {
176 $this->fatalError( 'Setting the password failed: ' . $pwe->getMessage() );
177 }
178 }
179
180 if ( !$exists ) {
181 # Increment site_stats.ss_users
182 $ssu = SiteStatsUpdate::factory( [ 'users' => 1 ] );
183 $ssu->doUpdate();
184 }
185
186 $this->output( "done.\n" );
187 }
188
197 private function addLogEntry( $user, array $oldGroups, array $newGroups, string $reason ) {
198 $logEntry = new ManualLogEntry( 'rights', 'rights' );
199 $logEntry->setPerformer( User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [ 'steal' => true ] ) );
200 $logEntry->setTarget( $user->getUserPage() );
201 $logEntry->setComment( $reason );
202 $logEntry->setParameters( [
203 '4::oldgroups' => $oldGroups,
204 '5::newgroups' => $newGroups
205 ] );
206 $logid = $logEntry->insert();
207 $logEntry->publish( $logid );
208 }
209}
210
211$maintClass = CreateAndPromote::class;
212require_once RUN_MAINTENANCE_IF_MAIN;
Maintenance script to create an account and grant it rights.
execute()
Do the actual work.
__construct()
Default constructor.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
error( $err, $die=0)
Throw an error to the user.
addArg( $arg, $description, $required=true, $multi=false)
Add some args that are needed.
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
getServiceContainer()
Returns the main service container.
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
maybeHelp( $force=false)
Maybe show the help.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
Class for creating new log entries and inserting them into the database.
Class for handling updates to the site_stats table.
internal since 1.36
Definition User.php:93
Tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:31
Show an error when any operation involving passwords fails to run.
A helper class for throttling authentication attempts.