Completed
Push — master ( 7d448a...f2ba2f )
by Robbie
13s queued 10s
created

EnforcementManager::hasCompletedRegistration()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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