Passed
Pull Request — master (#13)
by Simon
01:53
created

YubikeyAuthProvider   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Importance

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

7 Methods

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