Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.14% covered (success)
97.14%
34 / 35
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AuthenticationResponse
97.14% covered (success)
97.14%
34 / 35
83.33% covered (warning)
83.33%
5 / 6
10
0.00% covered (danger)
0.00%
0 / 1
 newPass
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 newFail
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 newRestart
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 newAbstain
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 newUI
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
4.02
 newRedirect
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Auth;
8
9use InvalidArgumentException;
10use MediaWiki\Message\Message;
11
12/**
13 * This is a value object to hold authentication response data
14 *
15 * An AuthenticationResponse represents both the status of the authentication
16 * (success, failure, in progress) and its state (what data is needed to continue).
17 *
18 * @ingroup Auth
19 * @since 1.27
20 */
21class AuthenticationResponse {
22    /** Indicates that the authentication succeeded. */
23    public const PASS = 'PASS';
24
25    /** Indicates that the authentication failed. */
26    public const FAIL = 'FAIL';
27
28    /** Indicates that third-party authentication succeeded but no user exists.
29     * Either treat this like a UI response or pass $this->createRequest to
30     * AuthManager::beginCreateAccount(). For use by AuthManager only (providers
31     * should just return a PASS with no username).
32     */
33    public const RESTART = 'RESTART';
34
35    /** Indicates that the authentication provider does not handle this request. */
36    public const ABSTAIN = 'ABSTAIN';
37
38    /** Indicates that the authentication needs further user input of some sort. */
39    public const UI = 'UI';
40
41    /** Indicates that the authentication needs to be redirected to a third party to proceed. */
42    public const REDIRECT = 'REDIRECT';
43
44    /** @var string One of the constants above */
45    public $status;
46
47    /** @var string|null URL to redirect to for a REDIRECT response */
48    public $redirectTarget = null;
49
50    /**
51     * @var mixed Data for a REDIRECT response that a client might use to
52     * query the remote site via its API rather than by following $redirectTarget.
53     * Value must be something acceptable to ApiResult::addValue().
54     */
55    public $redirectApiData = null;
56
57    /**
58     * @var AuthenticationRequest[] Needed AuthenticationRequests to continue
59     * after a UI or REDIRECT response. This plays the same role when continuing
60     * authentication as AuthManager::getAuthenticationRequests() does when
61     * beginning it.
62     */
63    public $neededRequests = [];
64
65    /** @var Message|null I18n message to display in case of UI or FAIL */
66    public $message = null;
67
68    /** @var string Whether the $message is an error or warning message, for styling reasons */
69    public $messageType = 'warning';
70
71    /**
72     * @var string|null Local username from authentication.
73     * The value may be null if the authentication passed, but no local user is known.
74     */
75    public $username = null;
76
77    /**
78     * @var AuthenticationRequest|null
79     *
80     * Returned with a PrimaryAuthenticationProvider login FAIL or a PASS with
81     * no username, this can be set to a request that should result in a PASS when
82     * passed to that provider's PrimaryAuthenticationProvider::beginPrimaryAccountCreation().
83     * The client will be able to send that back for expedited account creation where only
84     * the username needs to be filled.
85     *
86     * Returned with an AuthManager login FAIL or RESTART, this holds a
87     * CreateFromLoginAuthenticationRequest that may be passed to
88     * AuthManager::beginCreateAccount(), possibly in place of any
89     * "primary-required" requests. It may also be passed to
90     * AuthManager::beginAuthentication() to preserve the list of
91     * accounts which can be linked after success (see $linkRequest).
92     */
93    public $createRequest = null;
94
95    /**
96     * @var AuthenticationRequest|null When returned with a PrimaryAuthenticationProvider
97     *  login PASS with no username, the request this holds will be passed to
98     *  AuthManager::changeAuthenticationData() once the local user has been determined and the
99     *  user has confirmed the account ownership (by reviewing the information given by
100     *  $linkRequest->describeCredentials()). The provider should handle that
101     *  changeAuthenticationData() call by doing the actual linking.
102     */
103    public $linkRequest = null;
104
105    /**
106     * @var AuthenticationRequest|null Returned with an AuthManager account
107     *  creation PASS, this holds a request to pass to AuthManager::beginAuthentication()
108     *  to immediately log into the created account. All provider methods except
109     *   postAuthentication will be skipped.
110     */
111    public $loginRequest = null;
112
113    /**
114     * @var string[]|null String data that is optionally provided on a FAIL. It describes information about the
115     *  failed AuthenticationResponse that shouldn't be shared with the client.
116     *
117     *  The CheckUser extension uses this so that it can receive whether a login request for a locked
118     *  account had the correct password. Using the I18n message would allow the client to see if the
119     *  password they tried on the locked account was correct while this method does not show the client this info.
120     */
121    public $failReasons = null;
122
123    /**
124     * @param string|null $username Local username
125     * @return AuthenticationResponse
126     * @see AuthenticationResponse::PASS
127     */
128    public static function newPass( $username = null ) {
129        $ret = new AuthenticationResponse;
130        $ret->status = self::PASS;
131        $ret->username = $username;
132        return $ret;
133    }
134
135    /**
136     * @param Message $msg
137     * @param string[] $failReasons An array of strings that describes the reason(s) for a login failure
138     * @return AuthenticationResponse
139     * @see AuthenticationResponse::FAIL
140     */
141    public static function newFail( Message $msg, array $failReasons = [] ) {
142        $ret = new AuthenticationResponse;
143        $ret->status = self::FAIL;
144        $ret->message = $msg;
145        $ret->messageType = 'error';
146        $ret->failReasons = $failReasons;
147        return $ret;
148    }
149
150    /**
151     * @param Message $msg
152     * @return AuthenticationResponse
153     * @see AuthenticationResponse::RESTART
154     */
155    public static function newRestart( Message $msg ) {
156        $ret = new AuthenticationResponse;
157        $ret->status = self::RESTART;
158        $ret->message = $msg;
159        return $ret;
160    }
161
162    /**
163     * @return AuthenticationResponse
164     * @see AuthenticationResponse::ABSTAIN
165     */
166    public static function newAbstain() {
167        $ret = new AuthenticationResponse;
168        $ret->status = self::ABSTAIN;
169        return $ret;
170    }
171
172    /**
173     * @param AuthenticationRequest[] $reqs AuthenticationRequests needed to continue
174     * @param ?Message $msg
175     * @param string $msgtype
176     * @return AuthenticationResponse
177     * @see AuthenticationResponse::UI
178     */
179    public static function newUI( array $reqs, ?Message $msg = null, $msgtype = 'warning' ) {
180        if ( !$reqs ) {
181            throw new InvalidArgumentException( '$reqs may not be empty' );
182        }
183        if ( $msgtype !== 'warning' && $msgtype !== 'error' ) {
184            throw new InvalidArgumentException( $msgtype . ' is not a valid message type.' );
185        }
186
187        $ret = new AuthenticationResponse;
188        $ret->status = self::UI;
189        $ret->neededRequests = $reqs;
190        $ret->message = $msg;
191        $ret->messageType = $msgtype;
192        return $ret;
193    }
194
195    /**
196     * @param AuthenticationRequest[] $reqs AuthenticationRequests needed to continue
197     * @param string $redirectTarget URL
198     * @param mixed|null $redirectApiData Data suitable for adding to an ApiResult
199     * @return AuthenticationResponse
200     * @see AuthenticationResponse::REDIRECT
201     */
202    public static function newRedirect( array $reqs, $redirectTarget, $redirectApiData = null ) {
203        if ( !$reqs ) {
204            throw new InvalidArgumentException( '$reqs may not be empty' );
205        }
206
207        $ret = new AuthenticationResponse;
208        $ret->status = self::REDIRECT;
209        $ret->neededRequests = $reqs;
210        $ret->redirectTarget = $redirectTarget;
211        $ret->redirectApiData = $redirectApiData;
212        return $ret;
213    }
214
215}