Completed
Push — master ( 04d19a...743d3f )
by Simon
02:19
created

includes/DataObjects/User.php (14 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * User data object
5
 */
6
class User extends DataObject
7
{
8
	private $username;
9
	private $email;
10
	private $password;
11
	private $status = "New";
12
	private $onwikiname = "##OAUTH##";
13
	private $welcome_sig = "";
14
	private $lastactive = "0000-00-00 00:00:00";
15
	private $forcelogout = 0;
16
	private $checkuser = 0;
17
	private $identified = 0;
18
	private $welcome_template = 0;
19
	private $abortpref = 0;
20
	private $confirmationdiff = 0;
21
	private $emailsig = "";
22
	private $oauthrequesttoken = null;
23
	private $oauthrequestsecret = null;
24
	private $oauthaccesstoken = null;
25
	private $oauthaccesssecret = null;
26
	private $oauthidentitycache = null;
27
28
	// cache variable of the current user - it's never going to change in the middle of a request.
29
	private static $currentUser;
30
31
	private $identityCache = null;
32
33
	private $isCheckuserCache = null;
34
35
	/**
36
	 * Summary of getCurrent
37
	 * @param PdoDatabase $database
38
	 * @return User The currently logged in user, or an anonymous coward with userid -1.
39
	 */
40
	public static function getCurrent(PdoDatabase $database = null)
41
	{
42
		if ($database === null) {
43
			$database = gGetDb();   
44
		}
45
46
		if (self::$currentUser === null) {
47
			if (isset($_SESSION['userID'])) {
48
				self::$currentUser = self::getById($_SESSION['userID'], $database);
49
			}
50
			else {
51
				$anonymousCoward = new CommunityUser();
52
53
				self::$currentUser = $anonymousCoward;
54
			}
55
		}
56
57
58
		return self::$currentUser;
59
	}
60
    
61
	public static function getById($id, PdoDatabase $database)
62
	{
63
		if ($id == "-1") {
64
			return new CommunityUser();
65
		}
66
67
		return parent::getById($id, $database);
68
	}
69
70
	public static function getCommunity()
71
	{
72
		return new CommunityUser();   
73
	}
74
75
	public static function getByUsername($username, PdoDatabase $database)
76
	{
77
		global $communityUsername;
78
		if ($username == $communityUsername) {
79
			return new CommunityUser();
80
		}
81
82
		$statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
83
		$statement->bindValue(":id", $username);
84
85
		$statement->execute();
86
87
		$resultObject = $statement->fetchObject(get_called_class());
88
89
		if ($resultObject != false) {
90
			$resultObject->isNew = false;
91
			$resultObject->setDatabase($database); 
92
		}
93
94
		return $resultObject;
95
	}
96
97
	/**
98
	 * Gets a user by their on-wiki username.
99
	 * 
100
	 * Don't use without asking me first. It's really inefficient in it's current implementation.
101
	 * We need to restructure the user table again to make this more efficient.
102
	 * We don't actually store the on-wiki name in the table any more, instead we
103
	 * are storing JSON in a column (!!). Yep, my fault. Code review is an awesome thing.
104
	 *            -- stw 2015-10-20
105
	 * @param string $username 
106
	 * @param PdoDatabase $database 
107
	 * @return User|false
108
	 */
109
	public static function getByOnWikiUsername($username, PdoDatabase $database)
110
	{
111
		// Firstly, try to search by the efficient database lookup.
112
		$statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
113
		$statement->bindValue(":id", $username);
114
		$statement->execute();
115
116
		$resultObject = $statement->fetchObject(get_called_class());
117
118
		if ($resultObject != false) {
119
			$resultObject->isNew = false;
120
			$resultObject->setDatabase($database); 
121
122
			return $resultObject;
123
		}
124
125
		// For active users, the above has failed. Let's do it the hard way.
126
		$sqlStatement = "SELECT * FROM user WHERE onwikiname = '##OAUTH##' AND oauthaccesstoken IS NOT NULL;";
127
		$statement = $database->prepare($sqlStatement);
128
		$statement->execute();
129
		$resultSet = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
130
131
		/** @var User $user */
132 View Code Duplication
		foreach ($resultSet as $user) {
133
			// We have to set this before doing OAuth queries. :(
134
			$user->isNew = false;
135
			$user->setDatabase($database); 
136
137
			// Using cached data here!
138
			if ($user->getOAuthOnWikiName(true) == $username) {
139
				// Success.
140
				return $user;
141
			}
142
		}
143
144
		// Cached data failed. Let's do it the *REALLY* hard way.
145 View Code Duplication
		foreach ($resultSet as $user) {
146
			// We have to set this before doing OAuth queries. :(
147
			$user->isNew = false;
148
			$user->setDatabase($database); 
149
150
			// Don't use the cached data, but instead query the API.
151
			if ($user->getOAuthOnWikiName(false) == $username) {
152
				// Success.
153
				return $user;
154
			}
155
		}
156
157
		// Nope. Sorry.
158
		return false;
159
	}
160
161
	public static function getByRequestToken($requestToken, PdoDatabase $database)
162
	{
163
		$statement = $database->prepare("SELECT * FROM user WHERE oauthrequesttoken = :id LIMIT 1;");
164
		$statement->bindValue(":id", $requestToken);
165
166
		$statement->execute();
167
168
		$resultObject = $statement->fetchObject(get_called_class());
169
170
		if ($resultObject != false) {
171
			$resultObject->isNew = false;
172
			$resultObject->setDatabase($database); 
173
		}
174
175
		return $resultObject;
176
	}
177
178
	/**
179
	 * @param string $status
180
	 * @param PdoDatabase $database
181
	 * @return User[]
182
	 */
183
	public static function getAllWithStatus($status, PdoDatabase $database)
184
	{
185
		$statement = $database->prepare("SELECT * FROM user WHERE status = :status");
186
		$statement->execute(array(":status" => $status));
187
188
		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
189
190
		/** @var User $u */
191
		foreach ($resultObject as $u) {
192
			$u->setDatabase($database);
193
			$u->isNew = false;
194
		}
195
196
		return $resultObject;
197
	}
198
199
	public static function getAllCheckusers(PdoDatabase $database)
200
	{
201
		$statement = $database->prepare("SELECT * FROM user WHERE checkuser = 1;");
202
		$statement->execute();
203
204
		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
205
206
		$resultsCollection = array();
207
208
		/** @var User $u */
209
		foreach ($resultObject as $u) {
210
			$u->setDatabase($database);
211
			$u->isNew = false;
212
213
			if (!$u->isCheckuser()) {
214
				continue;
215
			}
216
217
			$resultsCollection[] = $u;
218
		}
219
220
		return $resultsCollection;
221
	}
222
223
	public static function getAllInactive(PdoDatabase $database)
224
	{
225
		$date = new DateTime();
226
		$date->modify("-90 days");
227
228
		$statement = $database->prepare(<<<SQL
229
			SELECT * 
230
			FROM user 
231
			WHERE lastactive < :lastactivelimit 
232
				AND status != 'Suspended' 
233
				AND status != 'Declined' 
234
				AND status != 'New' 
235
			ORDER BY lastactive ASC;
236
SQL
237
		);
238
		$statement->execute(array(":lastactivelimit" => $date->format("Y-m-d H:i:s")));
239
240
		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
241
242
		/** @var User $u */
243
		foreach ($resultObject as $u) {
244
			$u->setDatabase($database);
245
			$u->isNew = false;
246
		}
247
248
		return $resultObject;
249
	}
250
251
	/**
252
	 * Gets all the usernames in the system
253
	 * @param PdoDatabase $database
254
	 * @param null|bool|string $filter If null, no filter. If true, active users only, otherwise provided status.
255
	 * @return string[]
256
	 */
257
	public static function getAllUsernames(PdoDatabase $database, $filter = null)
258
	{
259
		if ($filter === null) {
260
			$userListQuery = "SELECT username FROM user;";
261
			$userListResult = $database->query($userListQuery);
262
		}
263
		elseif ($filter === true) {
264
			$userListQuery = "SELECT username FROM user WHERE status IN ('User', 'Admin');";
265
			$userListResult = $database->query($userListQuery);
266
		}
267
		else {
268
			$userListQuery = "SELECT username FROM user WHERE status = :status;";
269
			$userListResult = $database->prepare($userListQuery);
270
			$userListResult->execute(array(":status" => $filter));
271
		}
272
		
273
		return $userListResult->fetchAll(PDO::FETCH_COLUMN);
274
	}
275
	
276
	public function save()
277
	{
278
		if ($this->isNew) {
279
// insert
280
			$statement = $this->dbObject->prepare(<<<SQL
281
				INSERT INTO `user` ( 
282
					username, email, password, status, onwikiname, welcome_sig, 
283
					lastactive, forcelogout, checkuser, identified, 
284
					welcome_template, abortpref, confirmationdiff, emailsig, 
285
					oauthrequesttoken, oauthrequestsecret, 
286
					oauthaccesstoken, oauthaccesssecret
287
				) VALUES (
288
					:username, :email, :password, :status, :onwikiname, :welcome_sig,
289
					:lastactive, :forcelogout, :checkuser, :identified, 
290
					:welcome_template, :abortpref, :confirmationdiff, :emailsig, 
291
					:ort, :ors, :oat, :oas
292
				);
293
SQL
294
			);
295
			$statement->bindValue(":username", $this->username);
296
			$statement->bindValue(":email", $this->email);
297
			$statement->bindValue(":password", $this->password);
298
			$statement->bindValue(":status", $this->status);
299
			$statement->bindValue(":onwikiname", $this->onwikiname);
300
			$statement->bindValue(":welcome_sig", $this->welcome_sig);
301
			$statement->bindValue(":lastactive", $this->lastactive);
302
			$statement->bindValue(":forcelogout", $this->forcelogout);
303
			$statement->bindValue(":checkuser", $this->checkuser);
304
			$statement->bindValue(":identified", $this->identified);
305
			$statement->bindValue(":welcome_template", $this->welcome_template);
306
			$statement->bindValue(":abortpref", $this->abortpref);
307
			$statement->bindValue(":confirmationdiff", $this->confirmationdiff);
308
			$statement->bindValue(":emailsig", $this->emailsig);
309
			$statement->bindValue(":ort", $this->oauthrequesttoken);
310
			$statement->bindValue(":ors", $this->oauthrequestsecret);
311
			$statement->bindValue(":oat", $this->oauthaccesstoken);
312
			$statement->bindValue(":oas", $this->oauthaccesssecret);
313
            
314
			if ($statement->execute()) {
315
				$this->isNew = false;
316
				$this->id = (int)$this->dbObject->lastInsertId();
317
			}
318
			else {
319
				throw new Exception($statement->errorInfo());
320
			}
321
		}
322
		else {
323
// update
324
			$statement = $this->dbObject->prepare(<<<SQL
325
				UPDATE `user` SET 
326
					username = :username, email = :email, 
327
					password = :password, status = :status,
328
					onwikiname = :onwikiname, welcome_sig = :welcome_sig, 
329
					lastactive = :lastactive, forcelogout = :forcelogout, 
330
					checkuser = :checkuser, identified = :identified,
331
					welcome_template = :welcome_template, abortpref = :abortpref, 
332
					confirmationdiff = :confirmationdiff, emailsig = :emailsig, 
333
					oauthrequesttoken = :ort, oauthrequestsecret = :ors, 
334
					oauthaccesstoken = :oat, oauthaccesssecret = :oas 
335
				WHERE id = :id 
336
				LIMIT 1;
337
SQL
338
			);
339
			$statement->bindValue(":id", $this->id);
340
			$statement->bindValue(":username", $this->username);
341
			$statement->bindValue(":email", $this->email);
342
			$statement->bindValue(":password", $this->password);
343
			$statement->bindValue(":status", $this->status);
344
			$statement->bindValue(":onwikiname", $this->onwikiname);
345
			$statement->bindValue(":welcome_sig", $this->welcome_sig);
346
			$statement->bindValue(":lastactive", $this->lastactive);
347
			$statement->bindValue(":forcelogout", $this->forcelogout);
348
			$statement->bindValue(":checkuser", $this->checkuser);
349
			$statement->bindValue(":identified", $this->identified);
350
			$statement->bindValue(":welcome_template", $this->welcome_template);
351
			$statement->bindValue(":abortpref", $this->abortpref);
352
			$statement->bindValue(":confirmationdiff", $this->confirmationdiff);
353
			$statement->bindValue(":emailsig", $this->emailsig);
354
			$statement->bindValue(":ort", $this->oauthrequesttoken);
355
			$statement->bindValue(":ors", $this->oauthrequestsecret);
356
			$statement->bindValue(":oat", $this->oauthaccesstoken);
357
			$statement->bindValue(":oas", $this->oauthaccesssecret);
358
            
359
			if (!$statement->execute()) {
360
				throw new Exception($statement->errorInfo());
361
			}
362
		} 
363
	}
364
365
	public function authenticate($password)
366
	{
367
		$result = AuthUtility::testCredentials($password, $this->password);
368
        
369
		if ($result === true) {
370
			// password version is out of date, update it.
371
			if (!AuthUtility::isCredentialVersionLatest($this->password)) {
372
				$this->password = AuthUtility::encryptPassword($password);
373
				$this->save();
374
			}
375
		}
376
        
377
		return $result;
378
	}
379
    
380
	public function touchLastLogin()
381
	{
382
		$query = "UPDATE user SET lastactive = CURRENT_TIMESTAMP() WHERE id = :id;";
383
		$this->dbObject->prepare($query)->execute(array(":id" => $this->id));
384
	}
385
    
386
	#region properties
387
    
388
	public function getUsername()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
389
	{
390
		return $this->username;
391
	}
392
393
	public function setUsername($username)
394
	{
395
		$this->username = $username;
396
		$this->forcelogout = 1;
397
	}
398
399
	public function getEmail()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
400
	{
401
		return $this->email;
402
	}
403
404
	public function setEmail($email)
405
	{
406
		$this->email = $email;
407
	}
408
409
	public function setPassword($password)
410
	{
411
		$this->password = AuthUtility::encryptPassword($password);
412
	}
413
414
	public function getStatus()
415
	{
416
		return $this->status;
417
	}
418
419
	/**
420
	 * Gets the user's on-wiki name
421
	 * @return mixed
422
	 */
423
	public function getOnWikiName()
424
	{
425
		if ($this->oauthaccesstoken != null) {
426
			try {
427
				return $this->getOAuthOnWikiName();   
428
			}
429
			catch (Exception $ex) {
430
				// urm.. log this?
431
				return $this->onwikiname;
432
			}
433
		}
434
        
435
		return $this->onwikiname;
436
	}
437
    
438
	/**
439
	 * This is probably NOT the function you want!
440
	 * 
441
	 * Take a look at getOnWikiName() instead.
442
	 * @return string
443
	 */
444
	public function getStoredOnWikiName()
445
	{
446
		return $this->onwikiname;
447
	}
448
449
	public function setOnWikiName($onWikiName)
450
	{
451
		$this->onwikiname = $onWikiName;
452
	}
453
454
	public function getWelcomeSig()
455
	{
456
		return $this->welcome_sig;
457
	}
458
459
	public function setWelcomeSig($welcomeSig)
460
	{
461
		$this->welcome_sig = $welcomeSig;
462
	}
463
464
	public function getLastActive()
465
	{
466
		return $this->lastactive;
467
	}
468
469
	public function setLastActive($lastActive)
470
	{
471
		$this->lastactive = $lastActive;
472
	}
473
474
	public function getForcelogout()
475
	{
476
		return $this->forcelogout;
477
	}
478
479
	public function setForcelogout($forceLogout)
480
	{
481
		$this->forcelogout = $forceLogout ? 1 : 0;
482
	}
483
    
484
	public function getSecure()
485
	{
486
		return true;
487
	}
488
489
	public function getCheckuser()
490
	{
491
		return $this->checkuser;
492
	}
493
494
	public function setCheckuser($checkuser)
495
	{
496
		$this->checkuser = $checkuser;
497
	}
498
499
	public function getIdentified()
500
	{
501
		return $this->identified;
502
	}
503
504
	public function setIdentified($identified)
505
	{
506
		$this->identified = $identified;
507
	}
508
509
	public function getWelcomeTemplate()
510
	{
511
		return $this->welcome_template;
512
	}
513
514
	public function setWelcomeTemplate($welcomeTemplate)
515
	{
516
		$this->welcome_template = $welcomeTemplate;
517
	}
518
519
	public function getAbortPref()
520
	{
521
		return $this->abortpref;
522
	}
523
524
	public function setAbortPref($abortPreference)
525
	{
526
		$this->abortpref = $abortPreference;
527
	}
528
529
	public function getConfirmationDiff()
530
	{
531
		return $this->confirmationdiff;
532
	}
533
534
	public function setConfirmationDiff($confirmationDiff)
535
	{
536
		$this->confirmationdiff = $confirmationDiff;
537
	}
538
539
	/**
540
	 * @return string
541
	 */
542
	public function getEmailSig()
543
	{
544
		return $this->emailsig;
545
	}
546
547
	public function setEmailSig($emailSignature)
548
	{
549
		$this->emailsig = $emailSignature;
550
	}
551
    
552
	public function getOAuthRequestToken()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
553
	{
554
		return $this->oauthrequesttoken;
555
	}
556
557
	public function setOAuthRequestToken($oAuthRequestToken)
558
	{
559
		$this->oauthrequesttoken = $oAuthRequestToken;
560
	}
561
562
	public function getOAuthRequestSecret()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
563
	{
564
		return $this->oauthrequestsecret;
565
	}
566
567
	public function setOAuthRequestSecret($oAuthRequestSecret)
568
	{
569
		$this->oauthrequestsecret = $oAuthRequestSecret;
570
	}
571
572
	public function getOAuthAccessToken()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
573
	{
574
		return $this->oauthaccesstoken;
575
	}
576
577
	public function setOAuthAccessToken($oAuthAccessToken)
578
	{
579
		$this->oauthaccesstoken = $oAuthAccessToken;
580
	}
581
582
	public function getOAuthAccessSecret()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
583
	{
584
		return $this->oauthaccesssecret;
585
	}
586
587
	public function setOAuthAccessSecret($oAuthAccessSecret)
588
	{
589
		$this->oauthaccesssecret = $oAuthAccessSecret;
590
	}
591
592
	#endregion
593
    
594
	#region changing access level
595
    
596
	public function approve()
597
	{
598
		$this->dbObject->transactionally(function()
599
		{
600
			$this->status = "User";
601
			$this->save();
602
			Logger::approvedUser($this->dbObject, $this);
603
		});
604
	}
605
    
606 View Code Duplication
	public function suspend($comment)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
607
	{
608
		$this->dbObject->transactionally(function() use ($comment)
609
		{
610
			$this->status = "Suspended";
611
			$this->save();
612
			Logger::suspendedUser($this->dbObject, $this, $comment);
613
		});
614
	}
615
    
616 View Code Duplication
	public function decline($comment)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
617
	{
618
		$this->dbObject->transactionally(function() use ($comment)
619
		{
620
			$this->status = "Declined";
621
			$this->save();
622
			Logger::declinedUser($this->dbObject, $this, $comment);
623
		});
624
	}
625
    
626
	public function promote()
627
	{
628
		$this->dbObject->transactionally(function()
629
		{
630
			$this->status = "Admin";
631
			$this->save();
632
			Logger::promotedUser($this->dbObject, $this);
633
		});
634
	}
635
    
636 View Code Duplication
	public function demote($comment)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
637
	{
638
		$this->dbObject->transactionally(function() use ($comment)
639
		{
640
			$this->status = "User";
641
			$this->save();
642
			Logger::demotedUser($this->dbObject, $this, $comment);
643
		});
644
	}
645
646
	#endregion
647
    
648
	#region user access checks
649
    
650
	public function isAdmin()
651
	{
652
		return $this->status == "Admin";
653
	}
654
    
655
	public function isCheckuser()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
656
	{
657
	    if($this->isCheckuserCache === null) {
658
	        $this->isCheckuserCache = $this->checkuser == 1 || $this->oauthCanCheckUser();
659
        }
660
661
		return $this->isCheckuserCache;
662
	}
663
    
664
	public function isIdentified()
665
	{
666
		return $this->identified == 1;
667
	}
668
    
669
	public function isSuspended()
670
	{
671
		return $this->status == "Suspended";
672
	}
673
    
674
	public function isNew()
675
	{
676
		return $this->status == "New";
677
	}
678
    
679
	public function isUser()
680
	{
681
		return $this->status == "User";
682
	}
683
    
684
	public function isDeclined()
685
	{
686
		return $this->status == "Declined";
687
	}
688
    
689
	public function isCommunityUser()
690
	{
691
		return false;   
692
	}
693
    
694
	#endregion 
695
696
	#region OAuth
697
    
698
	public function getOAuthIdentity($useCached = false)
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
699
	{
700
		if ($this->oauthaccesstoken == null) {
701
			$this->clearOAuthData();
702
		}
703
        
704
		global $oauthConsumerToken, $oauthMediaWikiCanonicalServer;
705
706
		if ($this->oauthidentitycache == null) {
707
			$this->identityCache = null;
708
		}
709
		else {
710
			$this->identityCache = unserialize($this->oauthidentitycache);
711
		}
712
        
713
		// check the cache
714
		if (
715
			$this->identityCache != null &&
716
			$this->identityCache->aud == $oauthConsumerToken &&
717
			$this->identityCache->iss == $oauthMediaWikiCanonicalServer
718
			) {
719
			if (
720
				$useCached || (
721
					DateTime::createFromFormat("U", $this->identityCache->iat) < new DateTime() &&
722
					DateTime::createFromFormat("U", $this->identityCache->exp) > new DateTime()
723
					)
724
				) {
725
				// Use cached value - it's either valid or we don't care.
726
				return $this->identityCache;
727
			}
728
			else {
729
				// Cache expired and not forcing use of cached value
730
				$this->getIdentityCache();
731
				return $this->identityCache;
732
			}
733
		}
734
		else {
735
			// Cache isn't ours or doesn't exist
736
			$this->getIdentityCache();
737
			return $this->identityCache;
738
		}
739
	}
740
    
741
	/**
742
	 * Summary of getOAuthOnWikiName
743
	 * @param mixed $useCached Set to false for everything where up-to-date data is important.
744
	 * @return mixed
745
	 */
746
	private function getOAuthOnWikiName($useCached = false)
747
	{
748
		$identity = $this->getOAuthIdentity($useCached);
749
		if ($identity !== null) {
750
			return $identity->username;
751
		}
752
753
		return false;
754
	}
755
756
	/**
757
	 * @return bool
758
	 */
759
	public function isOAuthLinked()
760
	{
761
		if ($this->onwikiname === "##OAUTH##") {
762
			return true; // special value. If an account must be oauth linked, this is true.
763
		}
764
        
765
		return $this->oauthaccesstoken !== null;
766
	}
767
768
	private function clearOAuthData()
769
	{
770
		$this->identityCache = null;
771
		$this->oauthidentitycache = null;
772
		$clearCacheQuery = "UPDATE user SET oauthidentitycache = null WHERE id = :id;";
773
		$this->dbObject->prepare($clearCacheQuery)->execute(array(":id" => $this->id));
774
        
775
		return null;
776
	}
777
    
778
	private function getIdentityCache()
779
	{
780
		global $oauthConsumerToken, $oauthSecretToken, $oauthBaseUrl, $oauthBaseUrlInternal;
781
        
782
		try {
783
			$util = new OAuthUtility($oauthConsumerToken, $oauthSecretToken, $oauthBaseUrl, $oauthBaseUrlInternal);
784
			$this->identityCache = $util->getIdentity($this->oauthaccesstoken, $this->oauthaccesssecret);
785
			$this->oauthidentitycache = serialize($this->identityCache);
786
			$this->dbObject->
787
				prepare("UPDATE user SET oauthidentitycache = :identity WHERE id = :id;")->
788
				execute(array(":id" => $this->id, ":identity" => $this->oauthidentitycache));
789
		}
790
		catch (UnexpectedValueException $ex) {
791
			$this->identityCache = null;
792
			$this->oauthidentitycache = null;
793
			$this->dbObject->
794
				prepare("UPDATE user SET oauthidentitycache = null WHERE id = :id;")->
795
				execute(array(":id" => $this->id));
796
797
			SessionAlert::warning("OAuth error getting identity from MediaWiki: " . $ex->getMessage());
798
		}   
799
	}
800
    
801
	public function detachAccount()
802
	{
803
		$this->setOnWikiName($this->getOAuthOnWikiName());
804
		$this->setOAuthAccessSecret(null);
805
		$this->setOAuthAccessToken(null);
806
		$this->setOAuthRequestSecret(null);
807
		$this->setOAuthRequestToken(null);
808
809
		$this->clearOAuthData();
810
        
811
		$this->save();
812
	}
813
    
814
	public function oauthCanUse()
815
	{
816
		try {
817
			return in_array('useoauth', $this->getOAuthIdentity()->grants); 
818
		}
819
		catch (Exception $ex) {
820
			return false;
821
		}
822
	}
823
    
824 View Code Duplication
	public function oauthCanEdit()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
825
	{
826
		try {
827
			return in_array('useoauth', $this->getOAuthIdentity()->grants)
828
				&& in_array('createeditmovepage', $this->getOAuthIdentity()->grants)
829
				&& in_array('createtalk', $this->getOAuthIdentity()->rights)
830
				&& in_array('edit', $this->getOAuthIdentity()->rights)
831
				&& in_array('writeapi', $this->getOAuthIdentity()->rights); }
832
				catch (Exception $ex) {
833
			return false;
834
		}
835
	}
836
    
837 View Code Duplication
	public function oauthCanCreateAccount()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
838
	{
839
		try {
840
			return in_array('useoauth', $this->getOAuthIdentity()->grants)
841
				&& in_array('createaccount', $this->getOAuthIdentity()->grants)
842
				&& in_array('createaccount', $this->getOAuthIdentity()->rights)
843
				&& in_array('writeapi', $this->getOAuthIdentity()->rights);
844
		}
845
		catch (Exception $ex) {
846
			return false;
847
		}
848
	}
849
850
	/**
851
	 * @return bool
852
	 */
853
	protected function oauthCanCheckUser()
854
	{
855
		if (!$this->isOAuthLinked()) {
856
			return false;
857
		}
858
859
		try {
860
			$identity = $this->getOAuthIdentity();
861
			return in_array('checkuser', $identity->rights);
862
		}
863
		catch (Exception $ex) {
864
			return false;
865
		}
866
	}
867
    
868
	#endregion
869
    
870
	public function getForgottenPasswordHash()
871
	{
872
		return md5($this->username . $this->email . $this->welcome_template . $this->id . $this->password);
873
	}
874
875 View Code Duplication
	public function getApprovalDate()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
876
	{
877
		$query = $this->dbObject->prepare(<<<SQL
878
			SELECT timestamp 
879
			FROM log 
880
			WHERE objectid = :userid
881
				AND objecttype = 'User'
882
				AND action = 'Approved' 
883
			ORDER BY id DESC 
884
			LIMIT 1;
885
SQL
886
		);
887
		$query->execute(array(":userid" => $this->id));
888
        
889
		$data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
890
		$query->closeCursor();
891
        
892
		return $data;
893
	}
894
	
895
	public function getObjectDescription()
896
	{
897
		return '<a href="statistics.php?page=Users&amp;user=' . $this->getId() . '">' . htmlentities($this->username) . "</a>";
898
	}
899
}
900