* (bug 24563) Entries on Special:WhatLinksHere now have a link to their history
[lhc/web/wiklou.git] / includes / ExternalUser.php
1 <?php
2
3 # Copyright (C) 2009 Aryeh Gregor
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # http://www.gnu.org/copyleft/gpl.html
19
20 /**
21 * @defgroup ExternalUser ExternalUser
22 */
23
24 /**
25 * A class intended to supplement, and perhaps eventually replace, AuthPlugin.
26 * See: http://www.mediawiki.org/wiki/ExternalAuth
27 *
28 * The class represents a user whose data is in a foreign database. The
29 * database may have entirely different conventions from MediaWiki, but it's
30 * assumed to at least support the concept of a user id (possibly not an
31 * integer), a user name (possibly not meeting MediaWiki's username
32 * requirements), and a password.
33 *
34 * @ingroup ExternalUser
35 */
36 abstract class ExternalUser {
37 protected function __construct() {}
38
39 /**
40 * Wrappers around initFrom*().
41 */
42
43 /**
44 * @param $name string
45 * @return mixed ExternalUser, or false on failure
46 */
47 public static function newFromName( $name ) {
48 global $wgExternalAuthType;
49 if ( is_null( $wgExternalAuthType ) ) {
50 return false;
51 }
52 $obj = new $wgExternalAuthType;
53 if ( !$obj->initFromName( $name ) ) {
54 return false;
55 }
56 return $obj;
57 }
58
59 /**
60 * @param $id string
61 * @return mixed ExternalUser, or false on failure
62 */
63 public static function newFromId( $id ) {
64 global $wgExternalAuthType;
65 if ( is_null( $wgExternalAuthType ) ) {
66 return false;
67 }
68 $obj = new $wgExternalAuthType;
69 if ( !$obj->initFromId( $id ) ) {
70 return false;
71 }
72 return $obj;
73 }
74
75 /**
76 * @return mixed ExternalUser, or false on failure
77 */
78 public static function newFromCookie() {
79 global $wgExternalAuthType;
80 if ( is_null( $wgExternalAuthType ) ) {
81 return false;
82 }
83 $obj = new $wgExternalAuthType;
84 if ( !$obj->initFromCookie() ) {
85 return false;
86 }
87 return $obj;
88 }
89
90 /**
91 * Creates the object corresponding to the given User object, assuming the
92 * user exists on the wiki and is linked to an external account. If either
93 * of these is false, this will return false.
94 *
95 * This is a wrapper around newFromId().
96 *
97 * @param $user User
98 * @return mixed ExternalUser or false
99 */
100 public static function newFromUser( $user ) {
101 global $wgExternalAuthType;
102 if ( is_null( $wgExternalAuthType ) ) {
103 # Short-circuit to avoid database query in common case so no one
104 # kills me
105 return false;
106 }
107
108 $dbr = wfGetDB( DB_SLAVE );
109 $id = $dbr->selectField( 'external_user', 'eu_external_id',
110 array( 'eu_local_id' => $user->getId() ), __METHOD__ );
111 if ( $id === false ) {
112 return false;
113 }
114 return self::newFromId( $id );
115 }
116
117 /**
118 * Given a name, which is a string exactly as input by the user in the
119 * login form but with whitespace stripped, initialize this object to be
120 * the corresponding ExternalUser. Return true if successful, otherwise
121 * false.
122 *
123 * @param $name string
124 * @return bool Success?
125 */
126 protected abstract function initFromName( $name );
127
128 /**
129 * Given an id, which was at some previous point in history returned by
130 * getId(), initialize this object to be the corresponding ExternalUser.
131 * Return true if successful, false otherwise.
132 *
133 * @param $id string
134 * @return bool Success?
135 */
136 protected abstract function initFromId( $id );
137
138 /**
139 * Try to magically initialize the user from cookies or similar information
140 * so he or she can be logged in on just viewing the wiki. If this is
141 * impossible to do, just return false.
142 *
143 * TODO: Actually use this.
144 *
145 * @return bool Success?
146 */
147 protected function initFromCookie() {
148 return false;
149 }
150
151 /**
152 * This must return some identifier that stably, uniquely identifies the
153 * user. In a typical web application, this could be an integer
154 * representing the "user id". In other cases, it might be a string. In
155 * any event, the return value should be a string between 1 and 255
156 * characters in length; must uniquely identify the user in the foreign
157 * database; and, if at all possible, should be permanent.
158 *
159 * This will only ever be used to reconstruct this ExternalUser object via
160 * newFromId(). The resulting object in that case should correspond to the
161 * same user, even if details have changed in the interim (e.g., renames or
162 * preference changes).
163 *
164 * @return string
165 */
166 abstract public function getId();
167
168 /**
169 * This must return the name that the user would normally use for login to
170 * the external database. It is subject to no particular restrictions
171 * beyond rudimentary sanity, and in particular may be invalid as a
172 * MediaWiki username. It's used to auto-generate an account name that
173 * *is* valid for MediaWiki, either with or without user input, but
174 * basically is only a hint.
175 *
176 * @return string
177 */
178 abstract public function getName();
179
180 /**
181 * Is the given password valid for the external user? The password is
182 * provided in plaintext.
183 *
184 * @param $password string
185 * @return bool
186 */
187 abstract public function authenticate( $password );
188
189 /**
190 * Retrieve the value corresponding to the given preference key. The most
191 * important values are:
192 *
193 * - emailaddress
194 * - language
195 *
196 * The value must meet MediaWiki's requirements for values of this type,
197 * and will be checked for validity before use. If the preference makes no
198 * sense for the backend, or it makes sense but is unset for this user, or
199 * is unrecognized, return null.
200 *
201 * $pref will never equal 'password', since passwords are usually hashed
202 * and cannot be directly retrieved. authenticate() is used for this
203 * instead.
204 *
205 * TODO: Currently this is only called for 'emailaddress'; generalize! Add
206 * some config option to decide which values are grabbed on user
207 * initialization.
208 *
209 * @param $pref string
210 * @return mixed
211 */
212 public function getPref( $pref ) {
213 return null;
214 }
215
216 /**
217 * Return an array of identifiers for all the foreign groups that this user
218 * has. The identifiers are opaque objects that only need to be
219 * specifiable by the administrator in LocalSettings.php when configuring
220 * $wgAutopromote. They may be, for instance, strings or integers.
221 *
222 * TODO: Support this in $wgAutopromote.
223 *
224 * @return array
225 */
226 public function getGroups() {
227 return array();
228 }
229
230 /**
231 * Given a preference key (e.g., 'emailaddress'), provide an HTML message
232 * telling the user how to change it in the external database. The
233 * administrator has specified that this preference cannot be changed on
234 * the wiki, and may only be changed in the foreign database. If no
235 * message is available, such as for an unrecognized preference, return
236 * false.
237 *
238 * TODO: Use this somewhere.
239 *
240 * @param $pref string
241 * @return mixed String or false
242 */
243 public static function getPrefMessage( $pref ) {
244 return false;
245 }
246
247 /**
248 * Set the given preference key to the given value. Two important
249 * preference keys that you might want to implement are 'password' and
250 * 'emailaddress'. If the set fails, such as because the preference is
251 * unrecognized or because the external database can't be changed right
252 * now, return false. If it succeeds, return true.
253 *
254 * If applicable, you should make sure to validate the new value against
255 * any constraints the external database may have, since MediaWiki may have
256 * more limited constraints (e.g., on password strength).
257 *
258 * TODO: Untested.
259 *
260 * @param $key string
261 * @param $value string
262 * @return bool Success?
263 */
264 public static function setPref( $key, $value ) {
265 return false;
266 }
267
268 /**
269 * Create a link for future reference between this object and the provided
270 * user_id. If the user was already linked, the old link will be
271 * overwritten.
272 *
273 * This is part of the core code and is not overridable by specific
274 * plugins. It's in this class only for convenience.
275 *
276 * @param $id int user_id
277 */
278 public final function linkToLocal( $id ) {
279 $dbw = wfGetDB( DB_MASTER );
280 $dbw->replace( 'external_user',
281 array( 'eu_local_id', 'eu_external_id' ),
282 array( 'eu_local_id' => $id,
283 'eu_external_id' => $this->getId() ),
284 __METHOD__ );
285 }
286
287 /**
288 * Check whether this external user id is already linked with
289 * a local user.
290 * @return Mixed User if the account is linked, Null otherwise.
291 */
292 public final function getLocalUser(){
293 $dbr = wfGetDb( DB_SLAVE );
294 $row = $dbr->selectRow(
295 'external_user',
296 '*',
297 array( 'eu_external_id' => $this->getId() )
298 );
299 return $row
300 ? User::newFromId( $row->eu_local_id )
301 : null;
302 }
303
304 }