SchemaGenerator   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 50
c 8
b 0
f 0
dl 0
loc 132
rs 10
wmc 13

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getBackupMethod() 0 11 3
A getDefaultMethod() 0 4 2
A getSchema() 0 31 2
A getRegisteredMethods() 0 10 2
A getAvailableMethods() 0 19 3
A getResources() 0 9 1
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
     * Provides the full schema 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
        $registeredMethods = $this->getRegisteredMethods($member);
35
36
        // Skip registration details if the user has already registered this method
37
        $exclude = array_map(function (RegisteredMethodDetailsInterface $methodDetails) {
38
            return $methodDetails->jsonSerialize()['urlSegment'];
39
        }, $registeredMethods);
40
41
        // Also skip the backup method
42
        $backupMethod = MethodRegistry::singleton()->getBackupMethod();
43
        if ($backupMethod) {
44
            $exclude = array_merge($exclude, [$backupMethod->getURLSegment()]);
45
        }
46
47
        $schema = [
48
            'registeredMethods' => $registeredMethods,
49
            'availableMethods' => $this->getAvailableMethods($exclude),
50
            'allMethods' => $this->getAvailableMethods(),
51
            'defaultMethod' => $this->getDefaultMethod($member),
52
            'backupMethod' => $this->getBackupMethod(),
53
            'canSkip' => $enforcementManager->canSkipMFA($member),
54
            'isFullyRegistered' => $enforcementManager->hasCompletedRegistration($member),
55
            'resources' => $this->getResources(),
56
            'shouldRedirect' => $enforcementManager->shouldRedirectToMFA($member),
57
        ];
58
59
        $this->extend('updateSchema', $schema);
60
61
        return $schema;
62
    }
63
64
    /**
65
     * Get details in a list for all available methods, optionally excluding those with urlSegments provided in
66
     * $exclude
67
     *
68
     * @param array $exclude
69
     * @return AvailableMethodDetailsInterface[]
70
     */
71
    public function getAvailableMethods(array $exclude = [])
72
    {
73
        // Prepare an array to hold details for methods available to register
74
        $availableMethods = [];
75
76
        // Get all methods enabled on the site
77
        $methodRegistry = MethodRegistry::singleton();
78
        $allMethods = $methodRegistry->getMethods();
79
80
        // Compile details for methods that aren't already registered to the user
81
        foreach ($allMethods as $method) {
82
            // Omit specified exclusions or methods that are configured as back-up methods
83
            if (in_array($method->getURLSegment(), $exclude)) {
84
                continue;
85
            }
86
            $availableMethods[] = Injector::inst()->create(AvailableMethodDetailsInterface::class, $method);
87
        }
88
89
        return $availableMethods;
90
    }
91
92
    /**
93
     * Get a list of methods registered to the user
94
     *
95
     * @param Member&MemberExtension $member
96
     * @return RegisteredMethodDetailsInterface[]
97
     */
98
    protected function getRegisteredMethods(Member $member)
99
    {
100
        $registeredMethodDetails = [];
101
        foreach ($member->RegisteredMFAMethods() as $registeredMethod) {
102
            $registeredMethodDetails[] = Injector::inst()->create(
103
                RegisteredMethodDetailsInterface::class,
104
                $registeredMethod->getMethod()
105
            );
106
        }
107
        return $registeredMethodDetails;
108
    }
109
110
    /**
111
     * Get the URL Segment for the configured default method on the current member, or null if none is configured
112
     *
113
     * @param Member&MemberExtension $member
114
     * @return string|null
115
     */
116
    protected function getDefaultMethod(Member $member)
117
    {
118
        $defaultMethod = $member->DefaultRegisteredMethod;
119
        return $defaultMethod ? $defaultMethod->getMethod()->getURLSegment() : null;
120
    }
121
122
    /**
123
     * Get the "details" of the configured back-up method (if set)
124
     *
125
     * @return AvailableMethodDetailsInterface|null
126
     */
127
    protected function getBackupMethod()
128
    {
129
        $methodClass = Config::inst()->get(MethodRegistry::class, 'default_backup_method');
130
        if (!$methodClass) {
131
            return null;
132
        }
133
134
        /** @var MethodInterface $method */
135
        $method = Injector::inst()->create($methodClass);
136
137
        return $method ? Injector::inst()->create(AvailableMethodDetailsInterface::class, $method) : null;
0 ignored issues
show
introduced by
$method is of type SilverStripe\MFA\Method\MethodInterface, thus it always evaluated to true.
Loading history...
138
    }
139
140
    /**
141
     * Provide URLs for resources such as images and help articles
142
     */
143
    protected function getResources()
144
    {
145
        $module = ModuleLoader::getModule('silverstripe/mfa');
146
147
        return [
148
            'user_help_link' => Config::inst()->get(LoginHandler::class, 'user_help_link'),
149
            'extra_factor_image_url' => $module->getResource('client/dist/images/extra-protection.svg')->getURL(),
150
            'unique_image_url' => $module->getResource('client/dist/images/unique.svg')->getURL(),
151
            'more_options_image_url' => $module->getResource('client/dist/images/more-options.svg')->getURL(),
152
        ];
153
    }
154
}
155