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

SchemaGenerator::getSchema()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 1
dl 0
loc 25
rs 9.7333
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\Extensible;
7
use SilverStripe\Core\Injector\Injectable;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Core\Manifest\ModuleLoader;
10
use SilverStripe\MFA\Authenticator\LoginHandler;
11
use SilverStripe\MFA\Extension\MemberExtension;
12
use SilverStripe\MFA\Method\MethodInterface;
13
use SilverStripe\MFA\State\AvailableMethodDetailsInterface;
14
use SilverStripe\MFA\State\RegisteredMethodDetailsInterface;
15
use SilverStripe\Security\Member;
16
17
/**
18
 * Generates a multi-factor authentication frontend app schema from the given request
19
 */
20
class SchemaGenerator
21
{
22
    use Extensible;
23
    use Injectable;
24
25
    /**
26
     * Gets the schema data for the multi factor authentication app, using the current Member as context
27
     *
28
     * @param Member&MemberExtension $member
29
     * @return array
30
     */
31
    public function getSchema(Member $member)
32
    {
33
        $enforcementManager = EnforcementManager::singleton();
34
35
        $registeredMethods = $this->getRegisteredMethods($member);
36
37
        // Skip registration details if the user has already registered this method
38
        $exclude = array_map(function (RegisteredMethodDetailsInterface $methodDetails) {
39
            return $methodDetails->getURLSegment();
40
        }, $registeredMethods);
41
42
        $schema = [
43
            'registeredMethods' => $registeredMethods,
44
            'availableMethods' => $this->getAvailableMethods($exclude),
45
            'defaultMethod' => $this->getDefaultMethod($member),
46
            'backupMethod' => $this->getBackupMethod(),
47
            'canSkip' => $enforcementManager->canSkipMFA($member),
48
            'isFullyRegistered' => $enforcementManager->hasCompletedRegistration($member),
49
            'resources' => $this->getResources(),
50
            'shouldRedirect' => $enforcementManager->shouldRedirectToMFA($member),
51
        ];
52
53
        $this->extend('updateSchema', $schema);
54
55
        return $schema;
56
    }
57
58
    /**
59
     * Get a list of methods registered to the user
60
     *
61
     * @param Member&MemberExtension $member
62
     * @return RegisteredMethodDetailsInterface[]
63
     */
64
    protected function getRegisteredMethods(Member $member)
65
    {
66
        $registeredMethodDetails = [];
67
        foreach ($member->RegisteredMFAMethods() as $registeredMethod) {
68
            $registeredMethodDetails[] = $registeredMethod->getDetails();
69
        }
70
        return $registeredMethodDetails;
71
    }
72
73
    /**
74
     * Get details in a list for all available methods, optionally excluding those with urlSegments provided in
75
     * $exclude
76
     *
77
     * @param array $exclude
78
     * @return AvailableMethodDetailsInterface[]
79
     */
80
    protected function getAvailableMethods(array $exclude = [])
81
    {
82
        // Prepare an array to hold details for methods available to register
83
        $availableMethods = [];
84
85
        // Get all methods enabled on the site
86
        $methodRegistry = MethodRegistry::singleton();
87
        $allMethods = $methodRegistry->getMethods();
88
89
        // Compile details for methods that aren't already registered to the user
90
        foreach ($allMethods as $method) {
91
            // Omit specified exclusions or methods that are configured as back-up methods
92
            if (in_array($method->getURLSegment(), $exclude) || $methodRegistry->isBackupMethod($method)) {
93
                continue;
94
            }
95
            $availableMethods[] = $method->getDetails();
96
        }
97
98
        return $availableMethods;
99
    }
100
101
    /**
102
     * Get the URL Segment for the configured default method on the current member, or null if none is configured
103
     *
104
     * @param Member&MemberExtension $member
105
     * @return string|null
106
     */
107
    protected function getDefaultMethod(Member $member)
108
    {
109
        $defaultMethod = $member->DefaultRegisteredMethod;
110
        return $defaultMethod ? $defaultMethod->getMethod()->getURLSegment() : null;
111
    }
112
113
    /**
114
     * Get the "details" of the configured back-up method (if set)
115
     *
116
     * @return AvailableMethodDetailsInterface|null
117
     */
118
    protected function getBackupMethod()
119
    {
120
        $methodClass = Config::inst()->get(MethodRegistry::class, 'default_backup_method');
121
        if (!$methodClass) {
122
            return null;
123
        }
124
125
        /** @var MethodInterface $method */
126
        $method = Injector::inst()->create($methodClass);
127
128
        return $method ? $method->getDetails() : null;
0 ignored issues
show
introduced by
$method is of type SilverStripe\MFA\Method\MethodInterface, thus it always evaluated to true.
Loading history...
129
    }
130
131
    /**
132
     * Provide URLs for resources such as images and help articles
133
     */
134
    protected function getResources()
135
    {
136
        $module = ModuleLoader::getModule('silverstripe/mfa');
137
138
        return [
139
            'user_docs_url' => Config::inst()->get(LoginHandler::class, 'user_docs_url'),
140
            'extra_factor_image_url' => $module->getResource('client/dist/images/extra-protection.svg')->getURL(),
141
            'unique_image_url' => $module->getResource('client/dist/images/unique.svg')->getURL(),
142
        ];
143
    }
144
}
145