YubikeyMemberAuthenticator   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
dl 0
loc 129
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A supportedServices() 0 5 1
A setProvider() 0 5 1
A authenticateNoYubikey() 0 10 2
A getLoginHandler() 0 3 1
A getProvider() 0 3 1
B validateToken() 0 26 5
A get_name() 0 3 1
A __construct() 0 4 2
1
<?php
2
3
namespace Firesphere\YubiAuth\Authenticators;
4
5
use Firesphere\BootstrapMFA\Authenticators\BootstrapMFAAuthenticator;
6
use Firesphere\YubiAuth\Handlers\YubikeyLoginHandler;
7
use Firesphere\YubiAuth\Providers\YubikeyAuthProvider;
8
use SilverStripe\Control\HTTPRequest;
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\LoginHandler;
15
16
/**
17
 * Class YubikeyAuthenticator
18
 *
19
 * Enable Yubikey Authentication for SilverStripe CMS and member-protected pages.
20
 */
21
class YubikeyMemberAuthenticator extends BootstrapMFAAuthenticator
22
{
23
24
    /**
25
     * @var YubikeyAuthProvider
26
     */
27
    protected $provider;
28
29
    /**
30
     * @var string
31
     */
32
    private $authenticatorName = 'yubiauth';
0 ignored issues
show
introduced by
The private property $authenticatorName is not used, and could be removed.
Loading history...
33
34
    /**
35
     * Set the provider to a YubikeyAuthProvider instance
36
     *
37
     * YubikeyMemberAuthenticator constructor.
38
     */
39
    public function __construct()
40
    {
41
        if (!$this->provider) {
42
            $this->provider = Injector::inst()->get(YubikeyAuthProvider::class);
43
        }
44
    }
45
46
    /**
47
     * Name of this authenticator
48
     *
49
     * @return string
50
     */
51
    public static function get_name()
52
    {
53
        return _t(self::class . '.TITLE', 'Yubikey 2 factor login');
54
    }
55
56
    /**
57
     * @return YubikeyAuthProvider
58
     */
59
    public function getProvider()
60
    {
61
        return $this->provider;
62
    }
63
64
    /**
65
     * @param YubikeyAuthProvider $provider
66
     * @return $this
67
     */
68
    public function setProvider($provider)
69
    {
70
        $this->provider = $provider;
71
72
        return $this;
73
    }
74
75
    /**
76
     * @return int
77
     */
78
    public function supportedServices()
79
    {
80
        // Bitwise-OR of all the supported services in this Authenticator, to make a bitmask
81
        return Authenticator::LOGIN | Authenticator::LOGOUT | Authenticator::CHANGE_PASSWORD
82
            | Authenticator::RESET_PASSWORD | Authenticator::CHECK_PASSWORD;
83
    }
84
85
    /**
86
     * @inheritdoc
87
     *
88
     * @param array $data
89
     * @param HTTPRequest $request
90
     * @param ValidationResult $validationResult
91
     *
92
     * @return ValidationResult|Member
93
     * @throws ValidationException
94
     */
95
    public function validateToken($data, $request, &$validationResult = null)
96
    {
97
        if (!$validationResult instanceof ValidationResult) {
98
            $validationResult = ValidationResult::create();
99
        }
100
101
        $memberID = $request->getSession()->get(BootstrapMFAAuthenticator::SESSION_KEY . '.MemberID');
102
        // First, let's see if we know the member
103
        /** @var Member|null $member */
104
        $member = Member::get()->filter(['ID' => $memberID])->first();
105
106
        // Continue if we have a valid member
107
        if ($member instanceof Member) {
108
109
            // We do not have to check the YubiAuth for this situation.
110
            if (!$member->MFAEnabled && empty($data['yubiauth'])) {
111
                return $this->authenticateNoYubikey($member);
112
            }
113
114
            // If we know the member, and it's YubiAuth enabled, continue.
115
            $member = $this->provider->checkYubikey($data, $member, $validationResult);
116
        }
117
118
        $validationResult->addError(_t(self::class . '.MEMBERNOTFOUND', 'Could not identify member'));
119
120
        return $member;
121
    }
122
123
    /**
124
     * Handle login if the user did not enter a Yubikey string.
125
     * Will break out and return NULL if the member should use their Yubikey
126
     *
127
     * @param  Member $member
128
     * @return ValidationResult|Member
129
     * @throws ValidationException
130
     */
131
    private function authenticateNoYubikey($member)
132
    {
133
        ++$member->NoYubikeyCount;
134
        $member->write();
135
        $yubiAuthNoYubi = $this->provider->checkNoYubiAttempts($member);
136
        if ($yubiAuthNoYubi instanceof ValidationResult) {
137
            return $yubiAuthNoYubi;
138
        }
139
140
        return $member;
141
    }
142
143
    /**
144
     * @param string $link
145
     * @return LoginHandler|static
146
     */
147
    public function getLoginHandler($link)
148
    {
149
        return YubikeyLoginHandler::create($link, $this);
150
    }
151
}
152