Passed
Pull Request — master (#63)
by Garion
01:59
created

EnforcementManager::isGracePeriodInEffect()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 4
nop 0
dl 0
loc 22
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\MFA\Service;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Core\Injector\Injectable;
7
use SilverStripe\MFA\Extension\MemberExtension;
8
use SilverStripe\MFA\Extension\SiteConfigExtension;
9
use SilverStripe\ORM\FieldType\DBDate;
10
use SilverStripe\Security\Member;
11
use SilverStripe\SiteConfig\SiteConfig;
12
13
/**
14
 * The EnforcementManager class is responsible for making decisions regarding multi factor authentication app flow,
15
 * e.g. "is MFA required", "should we redirect to the MFA section", "can the user skip MFA registration" etc.
16
 */
17
class EnforcementManager
18
{
19
    use Injectable;
20
21
    /**
22
     * Whether the current member can skip the multi factor authentication registration process.
23
     *
24
     * This is determined by a combination of:
25
     *  - Whether MFA is required or optional
26
     *  - If MFA is required, whether there is a grace period
27
     *  - If MFA is required and there is a grace period, whether we're currently within that timeframe
28
     *
29
     * @param Member&MemberExtension $member
30
     * @return bool
31
     */
32
    public function canSkipMFA(Member $member)
33
    {
34
        if ($this->isMFARequired()) {
35
            return false;
36
        }
37
38
        // If they've already registered MFA methods we will not allow them to skip the authentication process
39
        $registeredMethods = $member->RegisteredMFAMethods();
40
        if ($registeredMethods->exists()) {
41
            return false;
42
        }
43
44
        // MFA is optional, or is required but might be within a grace period (see isMFARequired)
45
        return true;
46
    }
47
48
    /**
49
     * Whether the authentication process should redirect the user to multi factor authentication registration or
50
     * login.
51
     *
52
     * This is determined by a combination of:
53
     *  - Whether MFA is required or optional
54
     *  - Whether the user has registered MFA methods already
55
     *  - If the user doesn't have any registered MFA methods already, and MFA is optional, whether the user has opted
56
     *    to skip the registration process
57
     *
58
     * Note that in determining this, we ignore whether or not MFA is enabled for the site in general.
59
     *
60
     * @param Member&MemberExtension $member
61
     * @return bool
62
     */
63
    public function shouldRedirectToMFA(Member $member)
64
    {
65
        if ($this->isMFARequired()) {
66
            return true;
67
        }
68
69
        if ($this->isGracePeriodInEffect()) {
70
            return true;
71
        }
72
73
        if (!$member->HasSkippedMFARegistration) {
74
            return true;
75
        }
76
77
        return false;
78
    }
79
80
    /**
81
     * Check if the provided member has registered the required MFA methods. This includes a "back-up" method set in
82
     * configuration plus at least one other method.
83
     * Note that this method returns true if there is no backup method registered (and they have one other method
84
     *
85
     * @param Member&MemberExtension $member
86
     * @return bool
87
     */
88
    public function hasCompletedRegistration(Member $member)
89
    {
90
        $methodCount = $member->RegisteredMFAMethods()->count();
91
92
        $backupMethod = Config::inst()->get(MethodRegistry::class, 'default_backup_method');
93
        if (!$backupMethod) {
94
            // Ensure they have at least one method
95
            return $methodCount > 0;
96
        }
97
98
        // Ensure they have the required backup method and at least 2 methods (the backup method plus one other)
99
        return ((bool) $member->RegisteredMFAMethods()->find('MethodClassName', $backupMethod)) && $methodCount > 1;
100
    }
101
102
    /**
103
     * Whether multi factor authentication is required for site members. This also takes into account whether a
104
     * grace period is set and whether we're currently inside the window for it.
105
     *
106
     * Note that in determining this, we ignore whether or not MFA is enabled for the site in general.
107
     *
108
     * @return bool
109
     */
110
    public function isMFARequired()
111
    {
112
        $siteConfig = SiteConfig::current_site_config();
113
114
        $isRequired = $siteConfig->MFARequired;
115
        if (!$isRequired) {
116
            return false;
117
        }
118
119
        $gracePeriod = $siteConfig->MFAGracePeriodExpires;
120
        if ($isRequired && !$gracePeriod) {
121
            return true;
122
        }
123
124
        /** @var DBDate $gracePeriodDate */
125
        $gracePeriodDate = $siteConfig->dbObject('MFAGracePeriodExpires');
126
        if ($isRequired && $gracePeriodDate->InPast()) {
127
            return true;
128
        }
129
130
        // MFA is required, a grace period is set, and it's in the future
131
        return false;
132
    }
133
134
    /**
135
     * Specifically determines whether the MFA Grace Period is currently active.
136
     *
137
     * @return bool
138
     */
139
    public function isGracePeriodInEffect()
140
    {
141
        /** @var SiteConfig&SiteConfigExtension $siteConfig */
142
        $siteConfig = SiteConfig::current_site_config();
143
144
        $isRequired = $siteConfig->MFARequired;
145
        if (!$isRequired) {
146
            return false;
147
        }
148
149
        $gracePeriod = $siteConfig->MFAGracePeriodExpires;
150
        if (!$gracePeriod) {
151
            return false;
152
        }
153
154
        /** @var DBDate $gracePeriodDate */
155
        $gracePeriodDate = $siteConfig->dbObject('MFAGracePeriodExpires');
156
        if ($gracePeriodDate->InPast()) {
157
            return false;
158
        }
159
160
        return true;
161
    }
162
}
163