Issues (195)

CredentialProviders/U2FCredentialProvider.php (1 issue)

Severity
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 u2flib_server\Error;
13
use u2flib_server\U2F;
14
use Waca\DataObjects\User;
15
use Waca\Exceptions\ApplicationLogicException;
16
use Waca\Exceptions\OptimisticLockFailedException;
17
use Waca\PdoDatabase;
18
use Waca\SiteConfiguration;
19
use Waca\WebRequest;
20
21
class U2FCredentialProvider extends CredentialProviderBase
22
{
23
    /** @var U2F */
24
    private $u2f;
25
26
    /**
27
     * U2FCredentialProvider 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, 'u2f');
35
36
        $appId = 'https://' . WebRequest::httpHost();
37
        $this->u2f = new U2F($appId);
38
    }
39
40
    /**
41
     * Validates a user-provided credential
42
     *
43
     * @param User   $user The user to test the authentication against
44
     * @param string $data The raw credential data to be validated
45
     *
46
     * @return bool
47
     * @throws OptimisticLockFailedException
48
     */
49
    public function authenticate(User $user, $data)
50
    {
51
        if (!is_array($data)) {
0 ignored issues
show
The condition is_array($data) is always false.
Loading history...
52
            return false;
53
        }
54
55
        list($authenticate, $request, $isU2F) = $data;
56
57
        if ($isU2F !== 'u2f') {
58
            return false;
59
        }
60
61
        $storedData = $this->getCredentialData($user->getId(), false);
62
        $registrations = json_decode($storedData->getData());
63
64
        try {
65
            $updatedRegistration = $this->u2f->doAuthenticate($request, array($registrations), $authenticate);
66
            $storedData->setData(json_encode($updatedRegistration));
67
            $storedData->save();
68
        }
69
        catch (Error $ex) {
70
            return false;
71
        }
72
73
        return true;
74
    }
75
76
    public function enable(User $user, $request, $u2fData)
77
    {
78
        $registrationData = $this->u2f->doRegister($request, $u2fData);
79
80
        $storedData = $this->getCredentialData($user->getId(), true);
81
82
        if ($storedData === null) {
83
            throw new ApplicationLogicException('Credential data not found');
84
        }
85
86
        if ($storedData->getTimeout() > new DateTimeImmutable()) {
87
            $storedData->setData(json_encode($registrationData));
88
            $storedData->setDisabled(0);
89
            $storedData->setTimeout(null);
90
            $storedData->save();
91
        }
92
    }
93
94
    /**
95
     * @param User   $user   The user the credential belongs to
96
     * @param int    $factor The factor this credential provides
97
     * @param string $data   Unused here, due to multi-stage enrollment
98
     */
99
    public function setCredential(User $user, $factor, $data)
100
    {
101
        $storedData = $this->getCredentialData($user->getId(), null);
102
103
        if ($storedData !== null) {
104
            $storedData->delete();
105
        }
106
107
        $storedData = $this->createNewCredential($user);
108
109
        $storedData->setData(null);
110
        $storedData->setFactor($factor);
111
        $storedData->setTimeout(new DateTimeImmutable('+ 1 hour'));
112
        $storedData->setDisabled(1);
113
        $storedData->setPriority(4);
114
        $storedData->setVersion(1);
115
116
        $storedData->save();
117
    }
118
119
    public function isPartiallyEnrolled(User $user)
120
    {
121
        $storedData = $this->getCredentialData($user->getId(), true);
122
123
        if ($storedData->getTimeout() < new DateTimeImmutable()) {
124
            $storedData->delete();
125
126
            return false;
127
        }
128
129
        if ($storedData === null) {
130
            return false;
131
        }
132
133
        return true;
134
    }
135
136
    public function getRegistrationData()
137
    {
138
        return $this->u2f->getRegisterData();
139
    }
140
141
    public function getAuthenticationData(User $user)
142
    {
143
        $storedData = $this->getCredentialData($user->getId(), false);
144
        $registrations = json_decode($storedData->getData());
145
146
        $authenticateData = $this->u2f->getAuthenticateData(array($registrations));
147
148
        return $authenticateData;
149
    }
150
}
151