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