Completed
Push — master ( e81144...a25954 )
by Robbie
15s queued 15s
created

BootstrapMFAAuthenticator::validateBackupCode()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 40
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 21
nc 12
nop 3
dl 0
loc 40
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
namespace Firesphere\BootstrapMFA\Authenticators;
4
5
use Firesphere\BootstrapMFA\Extensions\MemberExtension;
6
use Firesphere\BootstrapMFA\Handlers\BootstrapMFALoginHandler;
7
use Firesphere\BootstrapMFA\Models\BackupCode;
8
use Firesphere\BootstrapMFA\Providers\BootstrapMFAProvider;
9
use SilverStripe\Core\Injector\Injector;
10
use SilverStripe\ORM\DataList;
11
use SilverStripe\ORM\ValidationException;
12
use SilverStripe\ORM\ValidationResult;
13
use SilverStripe\Security\Authenticator;
14
use SilverStripe\Security\Member;
15
use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator;
16
use SilverStripe\Security\PasswordEncryptor_NotFoundException;
17
use SilverStripe\Security\Security;
18
19
/**
20
 * Class BootstrapMFAAuthenticator
21
 * It needs to be instantiable, therefore it can't be an Abstract.
22
 *
23
 * @package Firesphere\BootstrapMFA\Authenticators
24
 * @method string getTokenField() Stub for child implementations
25
 */
26
class BootstrapMFAAuthenticator extends MemberAuthenticator
27
{
28
    /**
29
     * Key for array to be stored in between steps in the session
30
     */
31
    const SESSION_KEY = 'MFALogin';
32
33
    /**
34
     * @return int
35
     */
36
    public function supportedServices()
37
    {
38
        // Bitwise-OR of all the supported services in this Authenticator, to make a bitmask
39
        return Authenticator::LOGIN | Authenticator::LOGOUT | Authenticator::CHANGE_PASSWORD
40
            | Authenticator::RESET_PASSWORD | Authenticator::CHECK_PASSWORD;
41
    }
42
43
    /**
44
     * @param Member|MemberExtension $member
45
     * @param string $token
46
     * @param ValidationResult|null $result
47
     * @return bool|Member
48
     * @throws ValidationException
49
     * @throws PasswordEncryptor_NotFoundException
50
     */
51
    public function validateBackupCode($member, $token, &$result = null)
52
    {
53
        if (!$result) {
54
            $result = new ValidationResult();
55
        }
56
        $hashingMethod = Security::config()->get('password_encryption_algorithm');
57
        $token = Security::encrypt_password($token, $member->BackupCodeSalt, $hashingMethod);
58
59
        /** @var BootstrapMFAProvider $provider */
60
        $provider = Injector::inst()->get(BootstrapMFAProvider::class);
61
        $provider->setMember($member);
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type Firesphere\BootstrapMFA\Extensions\MemberExtension; however, parameter $member of Firesphere\BootstrapMFA\...FAProvider::setMember() does only seem to accept SilverStripe\Security\Member, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

61
        $provider->setMember(/** @scrutinizer ignore-type */ $member);
Loading history...
62
63
        /** @var DataList|BackupCode[] $backupCodes */
64
        $backupCodes = $provider->fetchToken($token['password']);
65
66
        // Stub, as the ValidationResult so far _could_ be valid, e.g. when not passed in
67
        $valid = false;
68
        foreach ($backupCodes as $backupCode) {
69
            /** @noinspection NotOptimalIfConditionsInspection */
70
            if ($backupCode &&
71
                $backupCode->exists() &&
72
                hash_equals($backupCode->Code, $token['password']) &&
73
                !$backupCode->Used
74
            ) {
75
                $backupCode->expire();
76
                // Reset the subclass authenticator results
77
                $result = ValidationResult::create();
78
79
                $valid = true;
80
            }
81
        }
82
83
        if ($valid) {
84
            return $member;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $member also could return the type Firesphere\BootstrapMFA\Extensions\MemberExtension which is incompatible with the documented return type SilverStripe\Security\Member|boolean.
Loading history...
85
        }
86
87
        $member->registerFailedLogin();
0 ignored issues
show
Bug introduced by
The method registerFailedLogin() does not exist on Firesphere\BootstrapMFA\Extensions\MemberExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

87
        $member->/** @scrutinizer ignore-call */ 
88
                 registerFailedLogin();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
88
        $result->addError(_t(self::class . '.INVALIDTOKEN', 'Invalid token'));
89
90
        return false;
91
    }
92
93
    /**
94
     * @param string $link
95
     * @return BootstrapMFALoginHandler|static
96
     */
97
    public function getLoginHandler($link)
98
    {
99
        return BootstrapMFALoginHandler::create($link, $this);
100
    }
101
}
102