Completed
Push — newinternal ( 65a0f5...5b021c )
by Simon
08:29
created

User::getUsername()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\DataObjects;
10
11
use DateTime;
12
use Exception;
13
use JWT;
14
use PDO;
15
use UnexpectedValueException;
16
use Waca\AuthUtility;
17
use Waca\DataObject;
18
use Waca\Exceptions\OptimisticLockFailedException;
19
use Waca\Helpers\Interfaces\IOAuthHelper;
20
use Waca\IdentificationVerifier;
21
use Waca\PdoDatabase;
22
use Waca\SessionAlert;
23
use Waca\WebRequest;
24
25
/**
26
 * User data object
27
 */
28
class User extends DataObject
29
{
30
    const STATUS_ACTIVE = 'Active';
31
    const STATUS_SUSPENDED = 'Suspended';
32
    const STATUS_DECLINED = 'Declined';
33
    const STATUS_NEW = 'New';
34
    private $username;
35
    private $email;
36
    private $password;
37
    private $status = self::STATUS_NEW;
38
    private $onwikiname = "##OAUTH##";
39
    private $welcome_sig = "";
40
    private $lastactive = "0000-00-00 00:00:00";
41
    private $forcelogout = 0;
42
    private $forceidentified = null;
43
    private $welcome_template = 0;
44
    private $abortpref = 0;
45
    private $confirmationdiff = 0;
46
    private $emailsig = "";
47
    /** @var null|string */
48
    private $oauthrequesttoken = null;
49
    /** @var null|string */
50
    private $oauthrequestsecret = null;
51
    /** @var null|string */
52
    private $oauthaccesstoken = null;
53
    /** @var null|string */
54
    private $oauthaccesssecret = null;
55
    private $oauthidentitycache = null;
56
    /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
57
    private static $currentUser;
58
    /** @var null|JWT The identity cache */
59
    private $identityCache = null;
60
    #region Object load methods
61
62
    /**
63
     * Gets the currently logged in user
64
     *
65
     * @param PdoDatabase $database
66
     *
67
     * @return User|CommunityUser
68
     */
69
    public static function getCurrent(PdoDatabase $database)
70
    {
71
        if (self::$currentUser === null) {
72
            $sessionId = WebRequest::getSessionUserId();
73
74
            if ($sessionId !== null) {
75
                /** @var User $user */
76
                $user = self::getById($sessionId, $database);
77
78
                if ($user === false) {
79
                    self::$currentUser = new CommunityUser();
80
                }
81
                else {
82
                    self::$currentUser = $user;
83
                }
84
            }
85
            else {
86
                $anonymousCoward = new CommunityUser();
87
88
                self::$currentUser = $anonymousCoward;
89
            }
90
        }
91
92
        return self::$currentUser;
93
    }
94
95
    /**
96
     * Gets a user by their user ID
97
     *
98
     * Pass -1 to get the community user.
99
     *
100
     * @param int|null    $id
101
     * @param PdoDatabase $database
102
     *
103
     * @return User|false
104
     */
105
    public static function getById($id, PdoDatabase $database)
106
    {
107
        if ($id === null || $id == -1) {
108
            return new CommunityUser();
109
        }
110
111
        /** @var User|false $user */
112
        $user = parent::getById($id, $database);
113
114
        return $user;
115
    }
116
117
    /**
118
     * @return CommunityUser
119
     */
120
    public static function getCommunity()
121
    {
122
        return new CommunityUser();
123
    }
124
125
    /**
126
     * Gets a user by their username
127
     *
128
     * @param  string      $username
129
     * @param  PdoDatabase $database
130
     *
131
     * @return CommunityUser|User|false
132
     */
133
    public static function getByUsername($username, PdoDatabase $database)
134
    {
135
        global $communityUsername;
136
        if ($username == $communityUsername) {
137
            return new CommunityUser();
138
        }
139
140
        $statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
141
        $statement->bindValue(":id", $username);
142
143
        $statement->execute();
144
145
        $resultObject = $statement->fetchObject(get_called_class());
146
147
        if ($resultObject != false) {
148
            $resultObject->setDatabase($database);
149
        }
150
151
        return $resultObject;
152
    }
153
154
    /**
155
     * Gets a user by their on-wiki username.
156
     *
157
     * Don't use without asking me first. It's really inefficient in it's current implementation.
158
     * We need to restructure the user table again to make this more efficient.
159
     * We don't actually store the on-wiki name in the table any more, instead we
160
     * are storing JSON in a column (!!). Yep, my fault. Code review is an awesome thing.
161
     *            -- stw 2015-10-20
162
     *
163
     * @param string      $username
164
     * @param PdoDatabase $database
165
     *
166
     * @return User|false
167
     */
168
    public static function getByOnWikiUsername($username, PdoDatabase $database)
169
    {
170
        // Firstly, try to search by the efficient database lookup.
171
        $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
172
        $statement->bindValue(":id", $username);
173
        $statement->execute();
174
175
        $resultObject = $statement->fetchObject(get_called_class());
176
177
        if ($resultObject != false) {
178
            $resultObject->setDatabase($database);
179
180
            return $resultObject;
181
        }
182
183
        // For active users, the above has failed. Let's do it the hard way.
184
        $sqlStatement = "SELECT * FROM user WHERE onwikiname = '##OAUTH##' AND oauthaccesstoken IS NOT NULL;";
185
        $statement = $database->prepare($sqlStatement);
186
        $statement->execute();
187
        $resultSet = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
188
189
        /** @var User $user */
190
        foreach ($resultSet as $user) {
191
            // We have to set this before doing OAuth queries. :(
192
            $user->setDatabase($database);
193
194
            // Using cached data here!
195
            if ($user->getOAuthOnWikiName(true) == $username) {
196
                // Success.
197
                return $user;
198
            }
199
        }
200
201
        // Cached data failed. Let's do it the *REALLY* hard way.
202
        foreach ($resultSet as $user) {
203
            // We have to set this before doing OAuth queries. :(
204
            $user->setDatabase($database);
205
206
            // Don't use the cached data, but instead query the API.
207
            if ($user->getOAuthOnWikiName(false) == $username) {
208
                // Success.
209
                return $user;
210
            }
211
        }
212
213
        // Nope. Sorry.
214
        return false;
215
    }
216
217
    /**
218
     * Gets a user by their OAuth request token
219
     *
220
     * @param string      $requestToken
221
     * @param PdoDatabase $database
222
     *
223
     * @return User|false
224
     */
225
    public static function getByRequestToken($requestToken, PdoDatabase $database)
226
    {
227
        $statement = $database->prepare("SELECT * FROM user WHERE oauthrequesttoken = :id LIMIT 1;");
228
        $statement->bindValue(":id", $requestToken);
229
230
        $statement->execute();
231
232
        $resultObject = $statement->fetchObject(get_called_class());
233
234
        if ($resultObject != false) {
235
            $resultObject->setDatabase($database);
236
        }
237
238
        return $resultObject;
239
    }
240
241
    #endregion
242
243
    /**
244
     * Saves the current object
245
     *
246
     * @throws Exception
247
     */
248
    public function save()
249
    {
250
        if ($this->isNew()) {
251
            // insert
252
            $statement = $this->dbObject->prepare(<<<SQL
253
				INSERT INTO `user` ( 
254
					username, email, password, status, onwikiname, welcome_sig, 
255
					lastactive, forcelogout, forceidentified,
256
					welcome_template, abortpref, confirmationdiff, emailsig, 
257
					oauthrequesttoken, oauthrequestsecret, 
258
					oauthaccesstoken, oauthaccesssecret
259
				) VALUES (
260
					:username, :email, :password, :status, :onwikiname, :welcome_sig,
261
					:lastactive, :forcelogout, :forceidentified,
262
					:welcome_template, :abortpref, :confirmationdiff, :emailsig, 
263
					:ort, :ors, :oat, :oas
264
				);
265
SQL
266
            );
267
            $statement->bindValue(":username", $this->username);
268
            $statement->bindValue(":email", $this->email);
269
            $statement->bindValue(":password", $this->password);
270
            $statement->bindValue(":status", $this->status);
271
            $statement->bindValue(":onwikiname", $this->onwikiname);
272
            $statement->bindValue(":welcome_sig", $this->welcome_sig);
273
            $statement->bindValue(":lastactive", $this->lastactive);
274
            $statement->bindValue(":forcelogout", $this->forcelogout);
275
            $statement->bindValue(":forceidentified", $this->forceidentified);
276
            $statement->bindValue(":welcome_template", $this->welcome_template);
277
            $statement->bindValue(":abortpref", $this->abortpref);
278
            $statement->bindValue(":confirmationdiff", $this->confirmationdiff);
279
            $statement->bindValue(":emailsig", $this->emailsig);
280
            $statement->bindValue(":ort", $this->oauthrequesttoken);
281
            $statement->bindValue(":ors", $this->oauthrequestsecret);
282
            $statement->bindValue(":oat", $this->oauthaccesstoken);
283
            $statement->bindValue(":oas", $this->oauthaccesssecret);
284
285
            if ($statement->execute()) {
286
                $this->id = (int)$this->dbObject->lastInsertId();
287
            }
288
            else {
289
                throw new Exception($statement->errorInfo());
290
            }
291
        }
292
        else {
293
            // update
294
            $statement = $this->dbObject->prepare(<<<SQL
295
				UPDATE `user` SET 
296
					username = :username, email = :email, 
297
					password = :password, status = :status,
298
					onwikiname = :onwikiname, welcome_sig = :welcome_sig, 
299
					lastactive = :lastactive, forcelogout = :forcelogout, 
300
					forceidentified = :forceidentified,
301
					welcome_template = :welcome_template, abortpref = :abortpref, 
302
					confirmationdiff = :confirmationdiff, emailsig = :emailsig, 
303
					oauthrequesttoken = :ort, oauthrequestsecret = :ors, 
304
					oauthaccesstoken = :oat, oauthaccesssecret = :oas,
305
					updateversion = updateversion + 1
306
				WHERE id = :id AND updateversion = :updateversion
307
				LIMIT 1;
308
SQL
309
            );
310
            $statement->bindValue(":forceidentified", $this->forceidentified);
311
312
            $statement->bindValue(':id', $this->id);
313
            $statement->bindValue(':updateversion', $this->updateversion);
314
315
            $statement->bindValue(':username', $this->username);
316
            $statement->bindValue(':email', $this->email);
317
            $statement->bindValue(':password', $this->password);
318
            $statement->bindValue(':status', $this->status);
319
            $statement->bindValue(':onwikiname', $this->onwikiname);
320
            $statement->bindValue(':welcome_sig', $this->welcome_sig);
321
            $statement->bindValue(':lastactive', $this->lastactive);
322
            $statement->bindValue(':forcelogout', $this->forcelogout);
323
            $statement->bindValue(':forceidentified', $this->forceidentified);
324
            $statement->bindValue(':welcome_template', $this->welcome_template);
325
            $statement->bindValue(':abortpref', $this->abortpref);
326
            $statement->bindValue(':confirmationdiff', $this->confirmationdiff);
327
            $statement->bindValue(':emailsig', $this->emailsig);
328
            $statement->bindValue(':ort', $this->oauthrequesttoken);
329
            $statement->bindValue(':ors', $this->oauthrequestsecret);
330
            $statement->bindValue(':oat', $this->oauthaccesstoken);
331
            $statement->bindValue(':oas', $this->oauthaccesssecret);
332
333
            if (!$statement->execute()) {
334
                throw new Exception($statement->errorInfo());
335
            }
336
337
            if ($statement->rowCount() !== 1) {
338
                throw new OptimisticLockFailedException();
339
            }
340
341
            $this->updateversion++;
342
        }
343
    }
344
345
    /**
346
     * Authenticates the user with the supplied password
347
     *
348
     * @param string $password
349
     *
350
     * @return bool
351
     * @throws Exception
352
     * @category Security-Critical
353
     */
354
    public function authenticate($password)
355
    {
356
        $result = AuthUtility::testCredentials($password, $this->password);
357
358
        if ($result === true) {
359
            // password version is out of date, update it.
360
            if (!AuthUtility::isCredentialVersionLatest($this->password)) {
361
                $this->password = AuthUtility::encryptPassword($password);
362
                $this->save();
363
            }
364
        }
365
366
        return $result;
367
    }
368
369
    #region properties
370
371
    /**
372
     * Gets the tool username
373
     * @return string
374
     */
375
    public function getUsername()
376
    {
377
        return $this->username;
378
    }
379
380
    /**
381
     * Sets the tool username
382
     *
383
     * @param string $username
384
     */
385
    public function setUsername($username)
386
    {
387
        $this->username = $username;
388
389
        // If this isn't a brand new user, then it's a rename, force the logout
390
        if (!$this->isNew()) {
391
            $this->forcelogout = 1;
392
        }
393
    }
394
395
    /**
396
     * Gets the user's email address
397
     * @return string
398
     */
399
    public function getEmail()
400
    {
401
        return $this->email;
402
    }
403
404
    /**
405
     * Sets the user's email address
406
     *
407
     * @param string $email
408
     */
409
    public function setEmail($email)
410
    {
411
        $this->email = $email;
412
    }
413
414
    /**
415
     * Sets the user's password
416
     *
417
     * @param string $password the plaintext password
418
     *
419
     * @category Security-Critical
420
     */
421
    public function setPassword($password)
422
    {
423
        $this->password = AuthUtility::encryptPassword($password);
424
    }
425
426
    /**
427
     * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
428
     * @return string
429
     */
430
    public function getStatus()
431
    {
432
        return $this->status;
433
    }
434
435
    /**
436
     * @param string $status
437
     */
438
    public function setStatus($status)
439
    {
440
        $this->status = $status;
441
    }
442
443
    /**
444
     * Gets the user's on-wiki name
445
     * @return string
446
     */
447
    public function getOnWikiName()
448
    {
449
        if ($this->oauthaccesstoken !== null) {
450
            try {
451
                return $this->getOAuthOnWikiName();
452
            }
453
            catch (Exception $ex) {
454
                // urm.. log this?
455
                return $this->onwikiname;
456
            }
457
        }
458
459
        return $this->onwikiname;
460
    }
461
462
    /**
463
     * This is probably NOT the function you want!
464
     *
465
     * Take a look at getOnWikiName() instead.
466
     * @return string
467
     */
468
    public function getStoredOnWikiName()
469
    {
470
        return $this->onwikiname;
471
    }
472
473
    /**
474
     * Sets the user's on-wiki name
475
     *
476
     * This can have interesting side-effects with OAuth.
477
     *
478
     * @param string $onWikiName
479
     */
480
    public function setOnWikiName($onWikiName)
481
    {
482
        $this->onwikiname = $onWikiName;
483
    }
484
485
    /**
486
     * Gets the welcome signature
487
     * @return string
488
     */
489
    public function getWelcomeSig()
490
    {
491
        return $this->welcome_sig;
492
    }
493
494
    /**
495
     * Sets the welcome signature
496
     *
497
     * @param string $welcomeSig
498
     */
499
    public function setWelcomeSig($welcomeSig)
500
    {
501
        $this->welcome_sig = $welcomeSig;
502
    }
503
504
    /**
505
     * Gets the last activity date for the user
506
     *
507
     * @return string
508
     * @todo This should probably return an instance of DateTime
509
     */
510
    public function getLastActive()
511
    {
512
        return $this->lastactive;
513
    }
514
515
    /**
516
     * Gets the user's forced logout status
517
     *
518
     * @return bool
519
     */
520
    public function getForceLogout()
521
    {
522
        return $this->forcelogout == 1;
523
    }
524
525
    /**
526
     * Sets the user's forced logout status
527
     *
528
     * @param bool $forceLogout
529
     */
530
    public function setForceLogout($forceLogout)
531
    {
532
        $this->forcelogout = $forceLogout ? 1 : 0;
533
    }
534
535
    /**
536
     * Returns the ID of the welcome template used.
537
     * @return int
538
     */
539
    public function getWelcomeTemplate()
540
    {
541
        return $this->welcome_template;
542
    }
543
544
    /**
545
     * Sets the ID of the welcome template used.
546
     *
547
     * @param int $welcomeTemplate
548
     */
549
    public function setWelcomeTemplate($welcomeTemplate)
550
    {
551
        $this->welcome_template = $welcomeTemplate;
552
    }
553
554
    /**
555
     * Gets the user's abort preference
556
     * @todo this is badly named too! Also a bool that's actually an int.
557
     * @return int
558
     */
559
    public function getAbortPref()
560
    {
561
        return $this->abortpref;
562
    }
563
564
    /**
565
     * Sets the user's abort preference
566
     * @todo rename, retype, and re-comment.
567
     *
568
     * @param int $abortPreference
569
     */
570
    public function setAbortPref($abortPreference)
571
    {
572
        $this->abortpref = $abortPreference;
573
    }
574
575
    /**
576
     * Gets the user's confirmation diff. Unused if OAuth is in use.
577
     * @return int the diff ID
578
     */
579
    public function getConfirmationDiff()
580
    {
581
        return $this->confirmationdiff;
582
    }
583
584
    /**
585
     * Sets the user's confirmation diff.
586
     *
587
     * @param int $confirmationDiff
588
     */
589
    public function setConfirmationDiff($confirmationDiff)
590
    {
591
        $this->confirmationdiff = $confirmationDiff;
592
    }
593
594
    /**
595
     * Gets the users' email signature used on outbound mail.
596
     * @todo rename me!
597
     * @return string
598
     */
599
    public function getEmailSig()
600
    {
601
        return $this->emailsig;
602
    }
603
604
    /**
605
     * Sets the user's email signature for outbound mail.
606
     *
607
     * @param string $emailSignature
608
     */
609
    public function setEmailSig($emailSignature)
610
    {
611
        $this->emailsig = $emailSignature;
612
    }
613
614
    /**
615
     * Gets the user's OAuth request token.
616
     *
617
     * @todo move me to a collaborator.
618
     * @return null|string
619
     */
620
    public function getOAuthRequestToken()
621
    {
622
        return $this->oauthrequesttoken;
623
    }
624
625
    /**
626
     * Sets the user's OAuth request token
627
     * @todo move me to a collaborator
628
     *
629
     * @param string $oAuthRequestToken
630
     */
631
    public function setOAuthRequestToken($oAuthRequestToken)
632
    {
633
        $this->oauthrequesttoken = $oAuthRequestToken;
634
    }
635
636
    /**
637
     * Gets the users OAuth request secret
638
     * @category Security-Critical
639
     * @todo     move me to a collaborator
640
     * @return null|string
641
     */
642
    public function getOAuthRequestSecret()
643
    {
644
        return $this->oauthrequestsecret;
645
    }
646
647
    /**
648
     * Sets the user's OAuth request secret
649
     * @todo move me to a collaborator
650
     *
651
     * @param string $oAuthRequestSecret
652
     */
653
    public function setOAuthRequestSecret($oAuthRequestSecret)
654
    {
655
        $this->oauthrequestsecret = $oAuthRequestSecret;
656
    }
657
658
    /**
659
     * Gets the user's access token
660
     * @category Security-Critical
661
     * @todo     move me to a collaborator
662
     * @return null|string
663
     */
664
    public function getOAuthAccessToken()
665
    {
666
        return $this->oauthaccesstoken;
667
    }
668
669
    /**
670
     * Sets the user's access token
671
     * @todo move me to a collaborator
672
     *
673
     * @param string $oAuthAccessToken
674
     */
675
    public function setOAuthAccessToken($oAuthAccessToken)
676
    {
677
        $this->oauthaccesstoken = $oAuthAccessToken;
678
    }
679
680
    /**
681
     * Gets the user's OAuth access secret
682
     * @category Security-Critical
683
     * @todo     move me to a collaborator
684
     * @return null|string
685
     */
686
    public function getOAuthAccessSecret()
687
    {
688
        return $this->oauthaccesssecret;
689
    }
690
691
    /**
692
     * Sets the user's OAuth access secret
693
     * @todo move me to a collaborator
694
     *
695
     * @param string $oAuthAccessSecret
696
     */
697
    public function setOAuthAccessSecret($oAuthAccessSecret)
698
    {
699
        $this->oauthaccesssecret = $oAuthAccessSecret;
700
    }
701
702
    #endregion
703
704
    #region user access checks
705
706
    public function isActive()
707
    {
708
        return $this->status == self::STATUS_ACTIVE;
709
    }
710
711
    /**
712
     * Tests if the user is identified
713
     *
714
     * @param IdentificationVerifier $iv
715
     *
716
     * @return bool
717
     * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
718
     *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
719
     *       to play it safe for now.
720
     * @category Security-Critical
721
     */
722
    public function isIdentified(IdentificationVerifier $iv)
723
    {
724
        if ($this->forceidentified === 0 || $this->forceidentified === "0") {
725
            // User forced to unidentified in the database.
726
            return false;
727
        }
728
        elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
729
            // User forced to identified in the database.
730
            return true;
731
        }
732
        else {
733
            // User not forced to any particular identified status; consult IdentificationVerifier
734
            return $iv->isUserIdentified($this->getOnWikiName());
735
        }
736
    }
737
738
    /**
739
     * Tests if the user is suspended
740
     * @return bool
741
     * @category Security-Critical
742
     */
743
    public function isSuspended()
744
    {
745
        return $this->status == self::STATUS_SUSPENDED;
746
    }
747
748
    /**
749
     * Tests if the user is new
750
     * @return bool
751
     * @category Security-Critical
752
     */
753
    public function isNewUser()
754
    {
755
        return $this->status == self::STATUS_NEW;
756
    }
757
758
    /**
759
     * Tests if the user has been declined access to the tool
760
     * @return bool
761
     * @category Security-Critical
762
     */
763
    public function isDeclined()
764
    {
765
        return $this->status == self::STATUS_DECLINED;
766
    }
767
768
    /**
769
     * Tests if the user is the community user
770
     *
771
     * @todo     decide if this means logged out. I think it usually does.
772
     * @return bool
773
     * @category Security-Critical
774
     */
775
    public function isCommunityUser()
776
    {
777
        return false;
778
    }
779
780
    #endregion 
781
782
    #region OAuth
783
784
    /**
785
     * @todo     move me to a collaborator
786
     *
787
     * @param bool $useCached
788
     *
789
     * @return mixed|null
790
     * @category Security-Critical
791
     */
792
    public function getOAuthIdentity($useCached = false)
793
    {
794
        if ($this->oauthaccesstoken === null) {
795
            $this->clearOAuthData();
796
        }
797
798
        global $oauthConsumerToken, $oauthMediaWikiCanonicalServer;
799
800
        if ($this->oauthidentitycache == null) {
801
            $this->identityCache = null;
802
        }
803
        else {
804
            $this->identityCache = unserialize($this->oauthidentitycache);
805
        }
806
807
        // check the cache
808
        if (
809
            $this->identityCache != null &&
810
            $this->identityCache->aud == $oauthConsumerToken &&
811
            $this->identityCache->iss == $oauthMediaWikiCanonicalServer
812
        ) {
813
            if (
814
                $useCached || (
815
                    DateTime::createFromFormat("U", $this->identityCache->iat) < new DateTime() &&
816
                    DateTime::createFromFormat("U", $this->identityCache->exp) > new DateTime()
817
                )
818
            ) {
819
                // Use cached value - it's either valid or we don't care.
820
                return $this->identityCache;
821
            }
822
            else {
823
                // Cache expired and not forcing use of cached value
824
                $this->getIdentityCache();
825
826
                return $this->identityCache;
827
            }
828
        }
829
        else {
830
            // Cache isn't ours or doesn't exist
831
            $this->getIdentityCache();
832
833
            return $this->identityCache;
834
        }
835
    }
836
837
    /**
838
     * @todo     move me to a collaborator
839
     *
840
     * @param mixed $useCached Set to false for everything where up-to-date data is important.
841
     *
842
     * @return mixed
843
     * @category Security-Critical
844
     */
845
    private function getOAuthOnWikiName($useCached = false)
846
    {
847
        $identity = $this->getOAuthIdentity($useCached);
848
        if ($identity !== null) {
849
            return $identity->username;
850
        }
851
852
        return false;
853
    }
854
855
    /**
856
     * @return bool
857
     * @todo move me to a collaborator
858
     */
859
    public function isOAuthLinked()
860
    {
861
        if ($this->onwikiname === "##OAUTH##") {
862
            return true; // special value. If an account must be oauth linked, this is true.
863
        }
864
865
        return $this->oauthaccesstoken !== null;
866
    }
867
868
    /**
869
     * @return null
870
     * @todo move me to a collaborator
871
     */
872
    public function clearOAuthData()
873
    {
874
        $this->identityCache = null;
875
        $this->oauthidentitycache = null;
876
        $clearCacheQuery = "UPDATE user SET oauthidentitycache = NULL WHERE id = :id;";
877
        $this->dbObject->prepare($clearCacheQuery)->execute(array(":id" => $this->id));
878
879
        return null;
880
    }
881
882
    /**
883
     * @throws Exception
884
     * @todo     move me to a collaborator
885
     * @category Security-Critical
886
     */
887
    private function getIdentityCache()
888
    {
889
        /** @var IOAuthHelper $oauthHelper */
890
        global $oauthHelper;
891
892
        try {
893
            $this->identityCache = $oauthHelper->getIdentityTicket($this->oauthaccesstoken, $this->oauthaccesssecret);
894
895
            $this->oauthidentitycache = serialize($this->identityCache);
896
            $this->dbObject->prepare("UPDATE user SET oauthidentitycache = :identity WHERE id = :id;")
897
                ->execute(array(":id" => $this->id, ":identity" => $this->oauthidentitycache));
898
        }
899
        catch (UnexpectedValueException $ex) {
900
            $this->identityCache = null;
901
            $this->oauthidentitycache = null;
902
            $this->dbObject->prepare("UPDATE user SET oauthidentitycache = NULL WHERE id = :id;")
903
                ->execute(array(":id" => $this->id));
904
905
            SessionAlert::warning("OAuth error getting identity from MediaWiki: " . $ex->getMessage());
906
        }
907
    }
908
909
    /**
910
     * @return bool
911
     * @todo move me to a collaborator
912
     */
913
    public function oauthCanUse()
914
    {
915
        try {
916
            return in_array('useoauth', $this->getOAuthIdentity()->grants);
917
        }
918
        catch (Exception $ex) {
919
            return false;
920
        }
921
    }
922
923
    /**
924
     * @return bool
925
     * @todo move me to a collaborator
926
     */
927 View Code Duplication
    public function oauthCanEdit()
0 ignored issues
show
Duplication introduced by
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...
928
    {
929
        try {
930
            return in_array('useoauth', $this->getOAuthIdentity()->grants)
931
            && in_array('createeditmovepage', $this->getOAuthIdentity()->grants)
932
            && in_array('createtalk', $this->getOAuthIdentity()->rights)
933
            && in_array('edit', $this->getOAuthIdentity()->rights)
934
            && in_array('writeapi', $this->getOAuthIdentity()->rights);
935
        }
936
        catch (Exception $ex) {
937
            return false;
938
        }
939
    }
940
941
    /**
942
     * @return bool
943
     * @todo move me to a collaborator
944
     */
945 View Code Duplication
    public function oauthCanCreateAccount()
0 ignored issues
show
Duplication introduced by
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...
946
    {
947
        try {
948
            return in_array('useoauth', $this->getOAuthIdentity()->grants)
949
            && in_array('createaccount', $this->getOAuthIdentity()->grants)
950
            && in_array('createaccount', $this->getOAuthIdentity()->rights)
951
            && in_array('writeapi', $this->getOAuthIdentity()->rights);
952
        }
953
        catch (Exception $ex) {
954
            return false;
955
        }
956
    }
957
958
    /**
959
     * @return bool
960
     * @todo     move me to a collaborator
961
     * @category Security-Critical
962
     */
963
    protected function oauthCanCheckUser()
964
    {
965
        if (!$this->isOAuthLinked()) {
966
            return false;
967
        }
968
969
        try {
970
            $identity = $this->getOAuthIdentity();
971
972
            return in_array('checkuser', $identity->rights);
973
        }
974
        catch (Exception $ex) {
975
            return false;
976
        }
977
    }
978
979
    #endregion
980
981
    /**
982
     * Gets a hash of data for the user to reset their password with.
983
     * @category Security-Critical
984
     * @return string
985
     */
986
    public function getForgottenPasswordHash()
987
    {
988
        return md5($this->username . $this->email . $this->welcome_template . $this->id . $this->password);
989
    }
990
991
    /**
992
     * Gets the approval date of the user
993
     * @return DateTime|false
994
     */
995
    public function getApprovalDate()
996
    {
997
        $query = $this->dbObject->prepare(<<<SQL
998
			SELECT timestamp 
999
			FROM log 
1000
			WHERE objectid = :userid
1001
				AND objecttype = 'User'
1002
				AND action = 'Approved' 
1003
			ORDER BY id DESC 
1004
			LIMIT 1;
1005
SQL
1006
        );
1007
        $query->execute(array(":userid" => $this->id));
1008
1009
        $data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
1010
        $query->closeCursor();
1011
1012
        return $data;
1013
    }
1014
}
1015