Completed
Push — master ( 876cd5...e4e843 )
by Antonio Carlos
15s queued 11s
created

Google2FA::minutesSinceLastActivity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PragmaRX\Google2FALaravel;
4
5
use Carbon\Carbon;
6
use Illuminate\Http\Request as IlluminateRequest;
7
use PragmaRX\Google2FALaravel\Events\LoggedOut;
8
use PragmaRX\Google2FALaravel\Events\OneTimePasswordExpired;
9
use PragmaRX\Google2FALaravel\Exceptions\InvalidSecretKey;
10
use PragmaRX\Google2FALaravel\Support\Auth;
11
use PragmaRX\Google2FALaravel\Support\Config;
12
use PragmaRX\Google2FALaravel\Support\Constants;
13
use PragmaRX\Google2FALaravel\Support\Request;
14
use PragmaRX\Google2FALaravel\Support\Session;
15
use PragmaRX\Google2FAQRCode\Google2FA as Google2FAService;
16
17
class Google2FA extends Google2FAService
18
{
19
    use Auth;
20
    use Config;
21
    use Request;
22
    use Session;
23
    protected $qrCodeBackend;
24
25
    /**
26
     * Construct the correct backend.
27
     */
28 15
    protected function constructBackend(): void
29
    {
30 15
        switch ($this->getQRCodeBackend()) {
31 15
            case Constants::QRCODE_IMAGE_BACKEND_SVG:
32
                parent::__construct(new \BaconQrCode\Renderer\Image\SvgImageBackEnd());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (__construct() instead of constructBackend()). Are you sure this is correct? If so, you might want to change this to $this->__construct().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
33
                break;
34
35 15
            case Constants::QRCODE_IMAGE_BACKEND_EPS:
36
                parent::__construct(new \BaconQrCode\Renderer\Image\EpsImageBackEnd());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (__construct() instead of constructBackend()). Are you sure this is correct? If so, you might want to change this to $this->__construct().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
37
                break;
38
39 15
            case Constants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK:
40
            default:
41 15
                parent::__construct();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (__construct() instead of constructBackend()). Are you sure this is correct? If so, you might want to change this to $this->__construct().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
42 15
                break;
43
        }
44 15
    }
45
46
    /**
47
     * Set the QRCode Backend.
48
     *
49
     * @param string $qrCodeBackend
50
     *
51
     * @return self
52
     */
53 1
    public function setQrCodeBackend(string $qrCodeBackend)
54
    {
55 1
        $this->qrCodeBackend = $qrCodeBackend;
56
57 1
        return $this;
58
    }
59
60
    /**
61
     * Authenticator constructor.
62
     *
63
     * @param IlluminateRequest $request
64
     */
65 15
    public function __construct(IlluminateRequest $request)
66
    {
67 15
        $this->boot($request);
68
69 15
        $this->constructBackend();
70 15
    }
71
72
    /**
73
     * Authenticator boot.
74
     *
75
     * @param $request
76
     *
77
     * @return Google2FA
78
     */
79 15
    public function boot($request)
80
    {
81 15
        $this->setRequest($request);
82
83 15
        return $this;
84
    }
85
86
    /**
87
     * The QRCode Backend.
88
     *
89
     * @return mixed
90
     */
91 15
    public function getQRCodeBackend()
92
    {
93 15
        return $this->qrCodeBackend
94 15
            ?: $this->config('qrcode_image_backend', Constants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK);
95
    }
96
97
    /**
98
     * Get the user Google2FA secret.
99
     *
100
     * @throws InvalidSecretKey
101
     *
102
     * @return mixed
103
     */
104 10
    protected function getGoogle2FASecretKey()
105
    {
106 10
        return $this->getUser()->{$this->config('otp_secret_column')};
107
    }
108
109
    /**
110
     * Check if the 2FA is activated for the user.
111
     *
112
     * @return bool
113
     */
114 10
    public function isActivated()
115
    {
116 10
        $secret = $this->getGoogle2FASecretKey();
117
118 10
        return !is_null($secret) && !empty($secret);
119
    }
120
121
    /**
122
     * Store the old OTP timestamp.
123
     *
124
     * @param $key
125
     *
126
     * @return mixed
127
     */
128 5
    protected function storeOldTimestamp($key)
129
    {
130 5
        return $this->config('forbid_old_passwords') === true
131 1
            ? $this->sessionPut(Constants::SESSION_OTP_TIMESTAMP, $key)
132 5
            : $key;
133
    }
134
135
    /**
136
     * Get the previous OTP timestamp.
137
     *
138
     * @return null|mixed
139
     */
140 6
    protected function getOldTimestamp()
141
    {
142 6
        return $this->config('forbid_old_passwords') === true
143 1
            ? $this->sessionGet(Constants::SESSION_OTP_TIMESTAMP)
144 6
            : null;
145
    }
146
147
    /**
148
     * Keep this OTP session alive.
149
     */
150 4
    protected function keepAlive()
151
    {
152 4
        if ($this->config('keep_alive')) {
153 4
            $this->updateCurrentAuthTime();
154
        }
155 4
    }
156
157
    /**
158
     * Get minutes since last activity.
159
     *
160
     * @return int
161
     */
162 1
    protected function minutesSinceLastActivity()
163
    {
164 1
        return Carbon::now()->diffInMinutes(
165 1
            $this->sessionGet(Constants::SESSION_AUTH_TIME)
166
        );
167
    }
168
169
    /**
170
     * Check if no user is authenticated using OTP.
171
     *
172
     * @return bool
173
     */
174 9
    protected function noUserIsAuthenticated()
175
    {
176 9
        return is_null($this->getUser());
177
    }
178
179
    /**
180
     * Check if OTP has expired.
181
     *
182
     * @return bool
183
     */
184 4
    protected function passwordExpired()
185
    {
186 4
        if (($minutes = $this->config('lifetime')) !== 0 && $this->minutesSinceLastActivity() > $minutes) {
187 1
            event(new OneTimePasswordExpired($this->getUser()));
188
189 1
            $this->logout();
190
191 1
            return true;
192
        }
193
194 4
        $this->keepAlive();
195
196 4
        return false;
197
    }
198
199
    /**
200
     * Verifies, in the current session, if a 2fa check has already passed.
201
     *
202
     * @return bool
203
     */
204 9
    protected function twoFactorAuthStillValid()
205
    {
206
        return
207 9
            (bool) $this->sessionGet(Constants::SESSION_AUTH_PASSED, false) &&
208 9
            !$this->passwordExpired();
209
    }
210
211
    /**
212
     * Check if the module is enabled.
213
     *
214
     * @return mixed
215
     */
216 9
    protected function isEnabled()
217
    {
218 9
        return $this->config('enabled');
219
    }
220
221
    /**
222
     * Set current auth as valid.
223
     */
224 5
    public function login()
225
    {
226 5
        $this->sessionPut(Constants::SESSION_AUTH_PASSED, true);
227
228 5
        $this->updateCurrentAuthTime();
229 5
    }
230
231
    /**
232
     * OTP logout.
233
     */
234 3
    public function logout()
235
    {
236 3
        $user = $this->getUser();
237
238 3
        $this->sessionForget();
239
240 3
        event(new LoggedOut($user));
241 3
    }
242
243
    /**
244
     * Update the current auth time.
245
     */
246 5
    protected function updateCurrentAuthTime()
247
    {
248 5
        $this->sessionPut(Constants::SESSION_AUTH_TIME, Carbon::now());
249 5
    }
250
251
    /**
252
     * Verify the OTP.
253
     *
254
     * @param $secret
255
     * @param $one_time_password
256
     *
257
     * @return mixed
258
     */
259 6
    public function verifyGoogle2FA($secret, $one_time_password)
260
    {
261 6
        return $this->verifyKey(
262 6
                $secret,
263
                $one_time_password,
264 6
                $this->config('window'),
265 6
                null, // $timestamp
266 6
                $this->getOldTimestamp() ?: null
267
        );
268
    }
269
270
    /**
271
     * Verify the OTP and store the timestamp.
272
     *
273
     * @param $one_time_password
274
     *
275
     * @return mixed
276
     */
277 5
    protected function verifyAndStoreOneTimePassword($one_time_password)
278
    {
279 5
        return $this->storeOldTimeStamp(
280 5
            $this->verifyGoogle2FA(
281 5
                $this->getGoogle2FASecretKey(),
282
                $one_time_password
283
            )
284
        );
285
    }
286
}
287