Authorize::__construct()   F
last analyzed

Complexity

Conditions 18
Paths 512

Size

Total Lines 63
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 35
c 1
b 0
f 0
dl 0
loc 63
rs 1.3777
cc 18
nc 512
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\authorize\Auth\Process;
6
7
use Exception;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\Auth;
10
use SimpleSAML\Module;
11
use SimpleSAML\Utils;
12
13
use function array_diff;
14
use function array_key_exists;
15
use function array_keys;
16
use function array_push;
17
use function implode;
18
use function is_array;
19
use function is_bool;
20
use function is_string;
21
use function preg_match;
22
use function var_export;
23
24
/**
25
 * Filter to authorize only certain users.
26
 * See docs directory.
27
 *
28
 * @package SimpleSAMLphp
29
 */
30
31
class Authorize extends Auth\ProcessingFilter
32
{
33
    /**
34
     * Flag to deny/unauthorize the user a attribute filter IS found
35
     *
36
     * @var bool
37
     */
38
    protected bool $deny = false;
39
40
    /**
41
     * Flag to turn the REGEX pattern matching on or off
42
     *
43
     * @var bool
44
     */
45
    protected bool $regex = true;
46
47
    /**
48
     * Array of localised rejection messages
49
     *
50
     * @var array
51
     */
52
    protected array $reject_msg = [];
53
54
    /**
55
     * Flag to toggle generation of errorURL
56
     *
57
     * @var bool
58
     */
59
    protected bool $errorURL = true;
60
61
    /**
62
     * Array of valid users. Each element is a regular expression. You should
63
     * user \ to escape special chars, like '.' etc.
64
     *
65
     * @param array
66
     */
67
    protected array $valid_attribute_values = [];
68
69
    /**
70
     * Flag to allow re-authentication when user is not authorized
71
     * @var bool
72
     */
73
    protected bool $allow_reauthentication = false;
74
75
    /**
76
     * The attribute to show in the error page
77
     * @var string|null
78
     */
79
    protected ?string $show_user_attribute = null;
80
81
    /**
82
     * Initialize this filter.
83
     * Validate configuration parameters.
84
     *
85
     * @param array $config  Configuration information about this filter.
86
     * @param mixed $reserved  For future use.
87
     */
88
    public function __construct(array $config, $reserved)
89
    {
90
        parent::__construct($config, $reserved);
91
92
        // Check for the deny option
93
        // Must be bool specifically, if not, it might be for an attrib filter below
94
        if (isset($config['deny']) && is_bool($config['deny'])) {
95
            $this->deny = $config['deny'];
96
            unset($config['deny']);
97
        }
98
99
        // Check for the regex option
100
        // Must be bool specifically, if not, it might be for an attrib filter below
101
        if (isset($config['regex']) && is_bool($config['regex'])) {
102
            $this->regex = $config['regex'];
103
            unset($config['regex']);
104
        }
105
106
        // Check for the reject_msg option; Must be array of languages
107
        if (isset($config['reject_msg']) && is_array($config['reject_msg'])) {
108
            $this->reject_msg = $config['reject_msg'];
109
            unset($config['reject_msg']);
110
        }
111
112
        // Check for the errorURL option
113
        // Must be bool specifically, if not, it might be for an attrib filter below
114
        if (isset($config['errorURL']) && is_bool($config['errorURL'])) {
115
            $this->errorURL = $config['errorURL'];
116
            unset($config['errorURL']);
117
        }
118
119
        if (isset($config['allow_reauthentication']) && is_bool($config['allow_reauthentication'])) {
120
            $this->allow_reauthentication = $config['allow_reauthentication'];
121
            unset($config['allow_reauthentication']);
122
        }
123
124
        if (isset($config['show_user_attribute']) && is_string($config['show_user_attribute'])) {
125
            $this->show_user_attribute = $config['show_user_attribute'];
126
            unset($config['show_user_attribute']);
127
        }
128
129
        foreach ($config as $attribute => $values) {
130
            if (is_string($values)) {
131
                $arrayUtils = new Utils\Arrays();
132
                $values = $arrayUtils->arrayize($values);
133
            } elseif (!is_array($values)) {
134
                throw new Exception(sprintf(
135
                    'Filter Authorize: Attribute values is neither string nor array: %s',
136
                    var_export($attribute, true),
137
                ));
138
            }
139
140
            foreach ($values as $value) {
141
                if (!is_string($value)) {
142
                    throw new Exception(sprintf(
143
                        'Filter Authorize: Each value should be a string for attribute: %s value: %s config: %s',
144
                        var_export($attribute, true),
145
                        var_export($value, true),
146
                        var_export($config, true),
147
                    ));
148
                }
149
            }
150
            $this->valid_attribute_values[$attribute] = $values;
151
        }
152
    }
153
154
155
    /**
156
     * Apply filter to validate attributes.
157
     *
158
     * @param array &$state  The current request
159
     */
160
    public function process(array &$state): void
161
    {
162
        Assert::keyExists($state, 'Attributes');
163
164
        $authorize = $this->deny;
165
        $attributes = &$state['Attributes'];
166
        $ctx = [];
167
168
        // Store the rejection message array in the $state
169
        if (!empty($this->reject_msg)) {
170
            $state['authprocAuthorize_reject_msg'] = $this->reject_msg;
171
        }
172
        $state['authprocAuthorize_errorURL'] = $this->errorURL;
173
        $state['authprocAuthorize_allow_reauthentication'] = $this->allow_reauthentication;
174
        $arrayUtils = new Utils\Arrays();
175
        foreach ($this->valid_attribute_values as $name => $patterns) {
176
            if (array_key_exists($name, $attributes)) {
177
                foreach ($patterns as $pattern) {
178
                    $values = $arrayUtils->arrayize($attributes[$name]);
179
                    foreach ($values as $value) {
180
                        if ($this->regex) {
181
                            $matched = preg_match($pattern, $value);
182
                        } else {
183
                            $matched = ($value === $pattern);
184
                        }
185
186
                        if ($matched) {
187
                            $authorize = ($this->deny ? false : true);
188
                            array_push($ctx, $name);
189
                            break 3;
190
                        }
191
                    }
192
                }
193
            }
194
        }
195
196
        if (!$authorize) {
197
            if ($this->show_user_attribute !== null && array_key_exists($this->show_user_attribute, $attributes)) {
198
                $userAttribute =  $attributes[$this->show_user_attribute][0] ?? null;
199
                if ($userAttribute !== null) {
200
                    $state['authprocAuthorize_user_attribute'] = $userAttribute;
201
                }
202
            }
203
204
            // Try to hint at which attributes may have failed as context for errorURL processing
205
            if ($this->deny) {
206
                $state['authprocAuthorize_ctx'] = implode(' ', $ctx);
207
            } else {
208
                $state['authprocAuthorize_ctx'] = implode(
209
                    ' ',
210
                    array_diff(array_keys($this->valid_attribute_values), $ctx),
211
                );
212
            }
213
            $this->unauthorized($state);
214
        }
215
    }
216
217
218
    /**
219
     * When the process logic determines that the user is not
220
     * authorized for this service, then forward the user to
221
     * an 403 unauthorized page.
222
     *
223
     * Separated this code into its own method so that child
224
     * classes can override it and change the action. Forward
225
     * thinking in case a "chained" ACL is needed, more complex
226
     * permission logic.
227
     *
228
     * @param array $state
229
     */
230
    protected function unauthorized(array &$state): void
231
    {
232
        // Save state and redirect to 403 page
233
        $id = Auth\State::saveState($state, 'authorize:Authorize');
234
        $url = Module::getModuleURL('authorize/error/forbidden');
235
        $httpUtils = new Utils\HTTP();
236
        $httpUtils->redirectTrustedURL($url, ['StateId' => $id]);
237
    }
238
}
239