Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
52.46% covered (warning)
52.46%
32 / 61
57.14% covered (warning)
57.14%
8 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialCreateAccount
52.46% covered (warning)
52.46%
32 / 61
57.14% covered (warning)
57.14%
8 / 14
85.89
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getRestriction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkPermissions
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
4.00
 getLoginSecurityLevel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSignup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 successfulAction
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
72
 getToken
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTokenName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 logAuthResult
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Specials;
8
9use MediaWiki\Auth\AuthManager;
10use MediaWiki\Exception\ErrorPageError;
11use MediaWiki\Language\FormatterFactory;
12use MediaWiki\Logger\LoggerFactory;
13use MediaWiki\SpecialPage\LoginSignupSpecialPage;
14use MediaWiki\Title\Title;
15use MediaWiki\User\UserIdentity;
16use MediaWiki\User\UserIdentityUtils;
17use StatusValue;
18
19/**
20 * Implements Special:CreateAccount
21 *
22 * @ingroup SpecialPage
23 * @ingroup Auth
24 */
25class SpecialCreateAccount extends LoginSignupSpecialPage {
26    /** @inheritDoc */
27    protected static $allowedActions = [
28        AuthManager::ACTION_CREATE,
29        AuthManager::ACTION_CREATE_CONTINUE
30    ];
31
32    /** @inheritDoc */
33    protected static $messages = [
34        'authform-newtoken' => 'nocookiesfornew',
35        'authform-notoken' => 'sessionfailure',
36        'authform-wrongtoken' => 'sessionfailure',
37    ];
38
39    public function __construct(
40        AuthManager $authManager,
41        private readonly FormatterFactory $formatterFactory,
42        private readonly UserIdentityUtils $identityUtils,
43    ) {
44        parent::__construct( 'CreateAccount' );
45
46        $this->setAuthManager( $authManager );
47    }
48
49    /** @inheritDoc */
50    public function getRestriction(): string {
51        return 'createaccount';
52    }
53
54    /** @inheritDoc */
55    public function doesWrites() {
56        return true;
57    }
58
59    public function checkPermissions() {
60        parent::checkPermissions();
61
62        $performer = $this->getAuthority();
63        $authManager = $this->getAuthManager();
64
65        $status = $this->mPosted ?
66            $authManager->authorizeCreateAccount( $performer ) :
67            $authManager->probablyCanCreateAccount( $performer );
68
69        if ( !$status->isGood() ) {
70            $formatter = $this->formatterFactory->getStatusFormatter( $this->getContext() );
71            $messages = [];
72            foreach ( $status->getMessages() as $message ) {
73                $messages[] = $message->getKey();
74            }
75            $this->logAuthResult(
76                false, $performer->getUser(),
77                implode( '|', $messages )
78            );
79            throw new ErrorPageError(
80                'createacct-error',
81                $formatter->getMessage( $status )
82            );
83        }
84    }
85
86    /** @inheritDoc */
87    protected function getLoginSecurityLevel() {
88        return false;
89    }
90
91    /** @inheritDoc */
92    protected function getDefaultAction( $subPage ) {
93        return AuthManager::ACTION_CREATE;
94    }
95
96    /** @inheritDoc */
97    public function getDescription() {
98        return $this->msg( 'createaccount' );
99    }
100
101    /** @inheritDoc */
102    protected function isSignup() {
103        return true;
104    }
105
106    /**
107     * Run any hooks registered for logins, then display a message welcoming
108     * the user.
109     * @param bool $direct True if the action was successful just now; false if that happened
110     *    pre-redirection (so this handler was called already)
111     * @param StatusValue|null $extraMessages
112     */
113    protected function successfulAction( $direct = false, $extraMessages = null ) {
114        $session = $this->getRequest()->getSession();
115        $user = $this->targetUser ?: $this->getUser();
116
117        $injected_html = '';
118        if ( $direct ) {
119            # Only save preferences if the user is not creating an account for someone else.
120            if ( $this->proxyAccountCreation ) {
121                $byEmail = false; // FIXME no way to set this
122
123                $out = $this->getOutput();
124                // @phan-suppress-next-line PhanImpossibleCondition
125                $out->setPageTitleMsg( $this->msg( $byEmail ? 'accmailtitle' : 'accountcreated' ) );
126                // @phan-suppress-next-line PhanImpossibleCondition
127                if ( $byEmail ) {
128                    $out->addWikiMsg( 'accmailtext', $user->getName(), $user->getEmail() );
129                } else {
130                    $out->addWikiMsg( 'accountcreatedtext', $user->getName() );
131                }
132
133                $rt = Title::newFromText( $this->mReturnTo );
134                $out->addReturnTo(
135                    ( $rt && !$rt->isExternal() ) ? $rt : $this->getPageTitle(),
136                    wfCgiToArray( $this->mReturnToQuery )
137                );
138                return;
139            }
140            $this->getHookRunner()->onUserLoginComplete( $user, $injected_html, $direct );
141        }
142
143        $this->clearToken();
144
145        # Run any hooks; display injected HTML
146        $welcome_creation_msg = 'welcomecreation-msg';
147        /**
148         * Let any extensions change what message is shown.
149         * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeWelcomeCreation
150         * @since 1.18
151         */
152        $this->getHookRunner()->onBeforeWelcomeCreation( $welcome_creation_msg, $injected_html );
153
154        $this->showSuccessPage( 'signup',
155            // T308471: ensure username is plaintext (aka escaped)
156            $this->msg( 'welcomeuser' )->plaintextParams( $this->getUser()->getName() ),
157            $welcome_creation_msg, $injected_html, $extraMessages );
158    }
159
160    /** @inheritDoc */
161    protected function getToken() {
162        return $this->getRequest()->getSession()->getToken( '', 'createaccount' );
163    }
164
165    protected function clearToken() {
166        $this->getRequest()->getSession()->resetToken( 'createaccount' );
167    }
168
169    /** @inheritDoc */
170    protected function getTokenName() {
171        return 'wpCreateaccountToken';
172    }
173
174    /** @inheritDoc */
175    protected function getGroupName() {
176        return 'users';
177    }
178
179    /** @inheritDoc */
180    protected function logAuthResult( $success, UserIdentity $performer, $status = null ) {
181        LoggerFactory::getInstance( 'authevents' )->info( 'Account creation attempt', [
182            'event' => 'accountcreation',
183            'successful' => $success,
184            'accountType' => $this->identityUtils->getShortUserTypeInternal( $performer ),
185            'status' => strval( $status )
186        ] + $this->getRequest()->getSecurityLogContext( $performer ) );
187    }
188}
189
190// @codeCoverageIgnoreStart
191/** @deprecated class alias since 1.41 */
192class_alias( SpecialCreateAccount::class, 'SpecialCreateAccount' );
193// @codeCoverageIgnoreEnd