Passed
Pull Request — master (#53)
by Simon
04:11
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\ValidationException;
11
use SilverStripe\ORM\ValidationResult;
12
use SilverStripe\Security\Authenticator;
13
use SilverStripe\Security\Member;
14
use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator;
15
use SilverStripe\Security\PasswordEncryptor_NotFoundException;
16
use SilverStripe\Security\Security;
17
18
/**
19
 * Class BootstrapMFAAuthenticator
20
 * It needs to be instantiable, therefore it can't be an Abstract.
21
 *
22
 * @package Firesphere\BootstrapMFA\Authenticators
23
 * @method string getTokenField() Stub for child implementations
24
 */
25
class BootstrapMFAAuthenticator extends MemberAuthenticator
26
{
27
    /**
28
     * Key for array to be stored in between steps in the session
29
     */
30
    const SESSION_KEY = 'MFALogin';
31
32
    /**
33
     * @return int
34
     */
35
    public function supportedServices()
36
    {
37
        // Bitwise-OR of all the supported services in this Authenticator, to make a bitmask
38
        return Authenticator::LOGIN | Authenticator::LOGOUT | Authenticator::CHANGE_PASSWORD
39
            | Authenticator::RESET_PASSWORD | Authenticator::CHECK_PASSWORD;
40
    }
41
42
    /**
43
     * @param Member|MemberExtension $member
44
     * @param string $token
45
     * @param ValidationResult|null $result
46
     * @return bool|Member
47
     * @throws ValidationException
48
     * @throws PasswordEncryptor_NotFoundException
49
     */
50
    public function validateBackupCode($member, $token, &$result = null)
51
    {
52
        if (!$result) {
53
            $result = new ValidationResult();
54
        }
55
        $hashingMethod = Security::config()->get('password_encryption_algorithm');
56
        $token = Security::encrypt_password($token, $member->BackupSalt, $hashingMethod);
57
58
        /** @var BootstrapMFAProvider $provider */
59
        $provider = Injector::inst()->get(BootstrapMFAProvider::class);
60
        $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

60
        $provider->setMember(/** @scrutinizer ignore-type */ $member);
Loading history...
61
62
        /** @var DataList|BackupCode[] $backupCodes */
63
        $backupCodes = $provider->fetchToken($token);
0 ignored issues
show
Bug introduced by
$token of type array<string,SilverStrip...Encryptor|mixed|string> is incompatible with the type string expected by parameter $token of Firesphere\BootstrapMFA\...AProvider::fetchToken(). ( Ignorable by Annotation )

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

63
        $backupCodes = $provider->fetchToken(/** @scrutinizer ignore-type */ $token);
Loading history...
64
65
        // Stub, as the ValidationResult so far _could_ be valid, e.g. when not passed in
66
        $valid = false;
67
        foreach ($backupCodes as $backupCode) {
68
            /** @noinspection NotOptimalIfConditionsInspection */
69
            if ($backupCode &&
70
                $backupCode->exists() &&
71
                hash_equals($backupCode->Code, $token['password']) &&
72
                !$backupCode->Used
73
            ) {
74
                $backupCode->expire();
75
                // Reset the subclass authenticator results
76
                $result = ValidationResult::create();
77
78
                $valid = true;
79
            }
80
        }
81
82
        if ($valid) {
83
            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...
84
        }
85
86
        $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

86
        $member->/** @scrutinizer ignore-call */ 
87
                 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...
87
        $result->addError(_t(self::class . '.INVALIDTOKEN', 'Invalid token'));
88
89
        return false;
90
    }
91
92
    /**
93
     * @param string $link
94
     * @return BootstrapMFALoginHandler|static
95
     */
96
    public function getLoginHandler($link)
97
    {
98
        return BootstrapMFALoginHandler::create($link, $this);
99
    }
100
}
101