TotpCredentialProvider::setCredential()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 14
dl 0
loc 22
rs 9.7998
c 2
b 0
f 0
cc 2
nc 2
nop 3
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 DateTimeImmutable;
12
use OTPHP\Factory;
13
use OTPHP\TOTP;
14
use Waca\DataObjects\User;
15
use Waca\Exceptions\ApplicationLogicException;
16
use Waca\PdoDatabase;
17
use Waca\Security\EncryptionHelper;
18
use Waca\SiteConfiguration;
19
20
class TotpCredentialProvider extends CredentialProviderBase
21
{
22
    /** @var EncryptionHelper */
23
    private $encryptionHelper;
24
25
    /**
26
     * TotpCredentialProvider constructor.
27
     *
28
     * @param PdoDatabase       $database
29
     * @param SiteConfiguration $configuration
30
     */
31
    public function __construct(PdoDatabase $database, SiteConfiguration $configuration)
32
    {
33
        parent::__construct($database, $configuration, 'totp');
34
        $this->encryptionHelper = new EncryptionHelper($configuration);
35
    }
36
37
    /**
38
     * Validates a user-provided credential
39
     *
40
     * @param User   $user The user to test the authentication against
41
     * @param string $data The raw credential data to be validated
42
     *
43
     * @return bool
44
     * @throws ApplicationLogicException
45
     */
46
    public function authenticate(User $user, $data)
47
    {
48
        if (is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always false.
Loading history...
49
            return false;
50
        }
51
52
        $storedData = $this->getCredentialData($user->getId());
53
54
        if ($storedData === null) {
55
            throw new ApplicationLogicException('Credential data not found');
56
        }
57
58
        $provisioningUrl = $this->encryptionHelper->decryptData($storedData->getData());
59
        $totp = Factory::loadFromProvisioningUri($provisioningUrl);
60
61
        return $totp->verify($data, null, 2);
62
    }
63
64
    public function verifyEnable(User $user, $data)
65
    {
66
        $storedData = $this->getCredentialData($user->getId(), true);
67
68
        if ($storedData === null) {
69
            throw new ApplicationLogicException('Credential data not found');
70
        }
71
72
        $provisioningUrl = $this->encryptionHelper->decryptData($storedData->getData());
73
        $totp = Factory::loadFromProvisioningUri($provisioningUrl);
74
75
        $result = $totp->verify($data, null, 2);
76
77
        if ($result && $storedData->getTimeout() > new DateTimeImmutable()) {
78
            $storedData->setDisabled(0);
79
            $storedData->setPriority(5);
80
            $storedData->setTimeout(null);
81
            $storedData->save();
82
        }
83
84
        return $result;
85
    }
86
87
    /**
88
     * @param User   $user   The user the credential belongs to
89
     * @param int    $factor The factor this credential provides
90
     * @param string $data   Unused here, due to there being no user-provided data. We provide the user with the secret.
91
     */
92
    public function setCredential(User $user, $factor, $data)
93
    {
94
        $issuer = 'ACC - ' . $this->getConfiguration()->getIrcNotificationsInstance();
95
        $totp = TOTP::create();
96
        $totp->setLabel($user->getUsername());
97
        $totp->setIssuer($issuer);
98
99
        $storedData = $this->getCredentialData($user->getId(), null);
100
101
        if ($storedData !== null) {
102
            $storedData->delete();
103
        }
104
105
        $storedData = $this->createNewCredential($user);
106
107
        $storedData->setData($this->encryptionHelper->encryptData($totp->getProvisioningUri()));
108
        $storedData->setFactor($factor);
109
        $storedData->setTimeout(new DateTimeImmutable('+ 1 hour'));
110
        $storedData->setDisabled(1);
111
        $storedData->setVersion(1);
112
113
        $storedData->save();
114
    }
115
116
    public function getProvisioningUrl(User $user)
117
    {
118
        $storedData = $this->getCredentialData($user->getId(), true);
119
120
        if ($storedData->getTimeout() < new DateTimeImmutable()) {
121
            $storedData->delete();
122
            $storedData = null;
123
        }
124
125
        if ($storedData === null) {
126
            throw new ApplicationLogicException('Credential data not found');
127
        }
128
129
        return $this->encryptionHelper->decryptData($storedData->getData());
130
    }
131
132
    public function isPartiallyEnrolled(User $user)
133
    {
134
        $storedData = $this->getCredentialData($user->getId(), true);
135
136
        if ($storedData->getTimeout() < new DateTimeImmutable()) {
137
            $storedData->delete();
138
139
            return false;
140
        }
141
142
        if ($storedData === null) {
143
            return false;
144
        }
145
146
        return true;
147
    }
148
149
    public function getSecret(User $user)
150
    {
151
        $totp = Factory::loadFromProvisioningUri($this->getProvisioningUrl($user));
152
153
        return $totp->getSecret();
154
    }
155
}
156