Failed Conditions
Push — newinternal ( b66232...216d62 )
by Simon
16:33 queued 06:35
created

ScratchTokenCredentialProvider.php (1 issue)

Severity

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
 * 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\Security\CredentialProviders;
10
11
use Base32\Base32;
12
use Waca\DataObjects\User;
13
use Waca\Exceptions\ApplicationLogicException;
14
use Waca\Exceptions\OptimisticLockFailedException;
15
use Waca\PdoDatabase;
16
use Waca\Security\EncryptionHelper;
17
use Waca\SiteConfiguration;
18
19
class ScratchTokenCredentialProvider extends CredentialProviderBase
20
{
21
    /** @var EncryptionHelper */
22
    private $encryptionHelper;
23
    /** @var array the tokens generated in the last generation round. */
24
    private $generatedTokens;
25
26
    /**
27
     * ScratchTokenCredentialProvider constructor.
28
     *
29
     * @param PdoDatabase       $database
30
     * @param SiteConfiguration $configuration
31
     */
32
    public function __construct(PdoDatabase $database, SiteConfiguration $configuration)
33
    {
34
        parent::__construct($database, $configuration, 'scratch');
35
        $this->encryptionHelper = new EncryptionHelper($configuration);
36
    }
37
38
    /**
39
     * Validates a user-provided credential
40
     *
41
     * @param User   $user The user to test the authentication against
42
     * @param string $data The raw credential data to be validated
43
     *
44
     * @return bool
45
     * @throws ApplicationLogicException|OptimisticLockFailedException
46
     */
47
    public function authenticate(User $user, $data)
48
    {
49
        if (is_array($data)) {
50
            return false;
51
        }
52
53
        $storedData = $this->getCredentialData($user->getId());
54
55
        if ($storedData === null) {
56
            throw new ApplicationLogicException('Credential data not found');
57
        }
58
59
        $scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
60
61
        $usedToken = null;
62
        foreach ($scratchTokens as $scratchToken) {
63
            if (password_verify($data, $scratchToken)){
64
                $usedToken = $scratchToken;
65
                break;
66
            }
67
        }
68
69
        if($usedToken === null) {
70
            return false;
71
        }
72
73
        $scratchTokens = array_diff($scratchTokens, [$usedToken]);
74
75
        $storedData->setData($this->encryptionHelper->encryptData(serialize($scratchTokens)));
76
        $storedData->save();
77
78
        return true;
79
    }
80
81
    /**
82
     * @param User   $user   The user the credential belongs to
83
     * @param int    $factor The factor this credential provides
84
     * @param string $data   Unused.
85
     *
86
     * @throws OptimisticLockFailedException
87
     */
88
    public function setCredential(User $user, $factor, $data)
89
    {
90
        $plaintextScratch = array();
91
        $storedScratch = array();
92
        for ($i = 0; $i < 5; $i++) {
93
            $token = Base32::encode(openssl_random_pseudo_bytes(10));
94
            $plaintextScratch[] = $token;
95
96
            $storedScratch[] = password_hash(
97
                $token,
98
                PasswordCredentialProvider::PASSWORD_ALGO,
99
                array('cost' => PasswordCredentialProvider::PASSWORD_COST)
100
            );
101
        }
102
103
        $storedData = $this->getCredentialData($user->getId(), null);
0 ignored issues
show
null is of type null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
104
105
        if ($storedData !== null) {
106
            $storedData->delete();
107
        }
108
109
        $storedData = $this->createNewCredential($user);
110
111
        $storedData->setData($this->encryptionHelper->encryptData(serialize($storedScratch)));
112
        $storedData->setFactor($factor);
113
        $storedData->setVersion(1);
114
        $storedData->setPriority(9);
115
116
        $storedData->save();
117
        $this->generatedTokens = $plaintextScratch;
118
    }
119
120
    /**
121
     * Gets the count of remaining valid tokens
122
     *
123
     * @param int $userId
124
     *
125
     * @return int
126
     */
127
    public function getRemaining($userId)
128
    {
129
        $storedData = $this->getCredentialData($userId);
130
131
        if ($storedData === null) {
132
            return 0;
133
        }
134
135
        $scratchTokens = unserialize($this->encryptionHelper->decryptData($storedData->getData()));
136
137
        return count($scratchTokens);
138
    }
139
140
    /**
141
     * @return array
142
     */
143
    public function getTokens()
144
    {
145
        if ($this->generatedTokens != null) {
146
            return $this->generatedTokens;
147
        }
148
149
        return array();
150
    }
151
}
152