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