Passed
Branch master (1ad6c0)
by Simon
03:19
created

YubikeyAuthProvider   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 17
dl 0
loc 147
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A checkNoYubiAttempts() 0 8 2
A validateMemberID() 0 5 3
A validateFingerprint() 0 8 3
A checkNoYubiLogins() 0 18 3
A validateMemberCount() 0 13 2
A validateToken() 0 17 1
A checkNoYubiDays() 0 22 3
1
<?php
2
3
namespace Firesphere\YubiAuth\Providers;
4
5
use DateTime;
6
use Firesphere\BootstrapMFA\Providers\BootstrapMFAProvider;
7
use Firesphere\BootstrapMFA\Providers\MFAProvider;
8
use SilverStripe\Core\Config\Configurable;
9
use SilverStripe\ORM\DataList;
10
use SilverStripe\ORM\ValidationResult;
11
use SilverStripe\Security\Member;
12
13
/**
14
 * Class YubikeyAuthProvider
15
 *
16
 * @package Firesphere\YubiAuth
17
 */
18
class YubikeyAuthProvider extends BootstrapMFAProvider implements MFAProvider
19
{
20
    use Configurable;
21
22
    /**
23
     * @param Member $member
24
     * @return ValidationResult|Member
25
     */
26
    public function checkNoYubiAttempts(Member $member)
27
    {
28
        $noYubiLogins = $this->checkNoYubiLogins($member);
29
        if ($noYubiLogins instanceof Member) {
30
            return $this->checkNoYubiDays($member);
31
        }
32
33
        return $noYubiLogins;
34
    }
35
36
    /**
37
     * Check if a member is allowed to login without a yubikey
38
     *
39
     * @param  Member $member
40
     * @return ValidationResult|Member
41
     */
42
    public function checkNoYubiLogins(Member $member)
43
    {
44
        $maxNoYubi = static::config()->get('MaxNoYubiLogin');
45
        if ($maxNoYubi > 0 && $maxNoYubi <= $member->NoYubikeyCount) {
46
            $validationResult = ValidationResult::create();
47
            $validationResult->addError(
48
                _t(
49
                    'YubikeyAuthenticator.ERRORMAXYUBIKEY',
50
                    'Maximum login without yubikey exceeded'
51
                )
52
            );
53
54
            $member->registerFailedLogin();
55
56
            return $validationResult;
57
        }
58
59
        return $member;
60
    }
61
62
    /**
63
     * Check if the member is allowed login after so many days of not using a yubikey
64
     *
65
     * @param  Member $member
66
     * @return ValidationResult|Member
67
     */
68
    public function checkNoYubiDays(Member $member)
69
    {
70
        $date1 = new DateTime($member->Created);
71
        $date2 = new DateTime(date('Y-m-d'));
72
73
        $diff = $date2->diff($date1)->format("%a");
74
        $maxNoYubiDays = static::config()->get('MaxNoYubiLoginDays');
75
76
        if ($maxNoYubiDays > 0 && $diff >= $maxNoYubiDays) {
77
            $validationResult = ValidationResult::create();
78
            $validationResult->addError(
79
                _t(
80
                    'YubikeyAuthenticator.ERRORMAXYUBIKEYDAYS',
81
                    'Maximum days without yubikey exceeded'
82
                )
83
            );
84
            $member->registerFailedLogin();
85
86
            return $validationResult;
87
        }
88
89
        return $member;
90
    }
91
92
    /**
93
     * Check if the yubikey is unique and linked to the member trying to logon
94
     *
95
     * @param  Member $member
96
     * @param  string $yubiFingerprint
97
     * @return ValidationResult
98
     */
99
    public function validateToken(Member $member, $yubiFingerprint)
100
    {
101
        /** @var DataList|Member[] $yubikeyMembers */
102
        $yubikeyMembers = Member::get()->filter(['Yubikey' => $yubiFingerprint]);
103
104
        /** @var ValidationResult $validationResult */
105
        $validationResult = ValidationResult::create();
106
107
        $this->validateMemberCount($member, $yubikeyMembers, $validationResult);
108
        // Yubikeys have a unique fingerprint, if we find a different member with this yubikey ID, something's wrong
109
        $this->validateMemberID($member, $yubikeyMembers, $validationResult);
110
111
        // If the member has a yubikey ID set, compare it to the fingerprint.
112
        $this->validateFingerprint($member, $yubiFingerprint, $validationResult);
113
114
115
        return $validationResult;
116
    }
117
118
    /**
119
     * @param Member $member
120
     * @param DataList|Member[] $yubikeyMembers
121
     * @param ValidationResult $validationResult
122
     */
123
    protected function validateMemberCount(
124
        Member $member,
125
        DataList $yubikeyMembers,
126
        ValidationResult $validationResult
127
    ) {
128
        if ($yubikeyMembers->count() > 1) {
129
            $validationResult->addError(
130
                _t(
131
                    'YubikeyAuthenticator.DUPLICATE',
132
                    'Yubikey is duplicate, contact your administrator as soon as possible!'
133
                )
134
            );
135
            $member->registerFailedLogin();
136
        }
137
    }
138
139
    /**
140
     * @param Member $member
141
     * @param DataList|Member[] $yubikeyMembers
142
     * @param ValidationResult $validationResult
143
     */
144
    protected function validateMemberID(Member $member, DataList $yubikeyMembers, ValidationResult $validationResult)
145
    {
146
        if ((int)$yubikeyMembers->count() === 1 && (int)$yubikeyMembers->first()->ID !== (int)$member->ID) {
147
            $validationResult->addError(_t('YubikeyAuthenticator.NOMATCHID', 'Yubikey does not match found member ID'));
148
            $member->registerFailedLogin();
149
        }
150
    }
151
152
    /**
153
     * @param Member $member
154
     * @param $fingerPrint
155
     * @param ValidationResult $validationResult
156
     */
157
    protected function validateFingerprint(Member $member, $fingerPrint, ValidationResult $validationResult)
158
    {
159
        if ($member->Yubikey && strpos($fingerPrint, $member->Yubikey) !== 0) {
160
            $member->registerFailedLogin();
161
            $validationResult->addError(
162
                _t(
163
                    'YubikeyAuthenticator.NOMATCH',
164
                    'Yubikey fingerprint does not match found member'
165
                )
166
            );
167
        }
168
    }
169
}
170