Failed Conditions
Push — newinternal ( 216d62...410e59 )
by Simon
05:28 queued 13s
created

ScratchTokenCredentialProvider::authenticate()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 17
c 2
b 0
f 0
dl 0
loc 32
rs 9.0777
cc 6
nc 8
nop 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\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;
0 ignored issues
show
Bug introduced by
The type Waca\SiteConfiguration was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always false.
Loading history...
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);
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