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