Total Complexity | 43 |
Total Lines | 411 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like Consent often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Consent, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class Consent extends Auth\ProcessingFilter |
||
28 | { |
||
29 | /** |
||
30 | * Button to receive focus |
||
31 | * |
||
32 | * @var string|null |
||
33 | */ |
||
34 | private ?string $focus = null; |
||
35 | |||
36 | /** |
||
37 | * Include attribute values |
||
38 | * |
||
39 | * @var bool |
||
40 | */ |
||
41 | private bool $includeValues = false; |
||
42 | |||
43 | /** |
||
44 | * Check remember consent |
||
45 | * |
||
46 | * @var bool |
||
47 | */ |
||
48 | private bool $checked = false; |
||
49 | |||
50 | /** |
||
51 | * Consent backend storage configuration |
||
52 | * |
||
53 | * @var \SimpleSAML\Module\consent\Store|null |
||
54 | */ |
||
55 | private ?Store $store = null; |
||
56 | |||
57 | /** |
||
58 | * Attributes where the value should be hidden |
||
59 | * |
||
60 | * @var array |
||
61 | */ |
||
62 | private array $hiddenAttributes = []; |
||
63 | |||
64 | /** |
||
65 | * Attributes which should not require consent |
||
66 | * |
||
67 | * @var array |
||
68 | */ |
||
69 | private array $noconsentattributes = []; |
||
70 | |||
71 | /** |
||
72 | * Whether we should show the "about service"-link on the no consent page. |
||
73 | * |
||
74 | * @var bool |
||
75 | */ |
||
76 | private bool $showNoConsentAboutService = true; |
||
77 | |||
78 | /** |
||
79 | * The name of the attribute that holds a unique identifier for the user |
||
80 | * |
||
81 | * @var string |
||
82 | */ |
||
83 | private string $identifyingAttribute; |
||
84 | |||
85 | |||
86 | /** |
||
87 | * Initialize consent filter. |
||
88 | * |
||
89 | * Validates and parses the configuration. |
||
90 | * |
||
91 | * @param array $config Configuration information. |
||
92 | * @param mixed $reserved For future use. |
||
93 | * |
||
94 | * @throws \SimpleSAML\Error\Exception if the configuration is not valid. |
||
95 | */ |
||
96 | public function __construct(array $config, $reserved) |
||
97 | { |
||
98 | parent::__construct($config, $reserved); |
||
99 | |||
100 | if (array_key_exists('includeValues', $config)) { |
||
101 | if (!is_bool($config['includeValues'])) { |
||
102 | throw new Error\Exception( |
||
103 | 'Consent: includeValues must be boolean. ' . |
||
104 | var_export($config['includeValues'], true) . ' given.', |
||
105 | ); |
||
106 | } |
||
107 | $this->includeValues = $config['includeValues']; |
||
108 | } |
||
109 | |||
110 | if (array_key_exists('checked', $config)) { |
||
111 | if (!is_bool($config['checked'])) { |
||
112 | throw new Error\Exception( |
||
113 | 'Consent: checked must be boolean. ' . |
||
114 | var_export($config['checked'], true) . ' given.', |
||
115 | ); |
||
116 | } |
||
117 | $this->checked = $config['checked']; |
||
118 | } |
||
119 | |||
120 | if (array_key_exists('focus', $config)) { |
||
121 | if (!in_array($config['focus'], ['yes', 'no'], true)) { |
||
122 | throw new Error\Exception( |
||
123 | 'Consent: focus must be a string with values `yes` or `no`. ' . |
||
124 | var_export($config['focus'], true) . ' given.', |
||
125 | ); |
||
126 | } |
||
127 | $this->focus = $config['focus']; |
||
128 | } |
||
129 | |||
130 | if (array_key_exists('hiddenAttributes', $config)) { |
||
131 | if (!is_array($config['hiddenAttributes'])) { |
||
132 | throw new Error\Exception( |
||
133 | 'Consent: hiddenAttributes must be an array. ' . |
||
134 | var_export($config['hiddenAttributes'], true) . ' given.', |
||
135 | ); |
||
136 | } |
||
137 | $this->hiddenAttributes = $config['hiddenAttributes']; |
||
138 | } |
||
139 | |||
140 | if (array_key_exists('attributes.exclude', $config)) { |
||
141 | if (!is_array($config['attributes.exclude'])) { |
||
142 | throw new Error\Exception( |
||
143 | 'Consent: attributes.exclude must be an array. ' . |
||
144 | var_export($config['attributes.exclude'], true) . ' given.', |
||
145 | ); |
||
146 | } |
||
147 | $this->noconsentattributes = $config['attributes.exclude']; |
||
148 | } |
||
149 | |||
150 | if (array_key_exists('store', $config)) { |
||
151 | try { |
||
152 | $this->store = \SimpleSAML\Module\consent\Store::parseStoreConfig($config['store']); |
||
153 | } catch (Exception $e) { |
||
154 | Logger::error( |
||
155 | 'Consent: Could not create consent storage: ' . |
||
156 | $e->getMessage(), |
||
157 | ); |
||
158 | } |
||
159 | } |
||
160 | |||
161 | if (array_key_exists('showNoConsentAboutService', $config)) { |
||
162 | if (!is_bool($config['showNoConsentAboutService'])) { |
||
163 | throw new Error\Exception('Consent: showNoConsentAboutService must be a boolean.'); |
||
164 | } |
||
165 | $this->showNoConsentAboutService = $config['showNoConsentAboutService']; |
||
166 | } |
||
167 | |||
168 | Assert::keyExists( |
||
169 | $config, |
||
170 | 'identifyingAttribute', |
||
171 | "Consent: Missing mandatory 'identifyingAttribute' config setting.", |
||
172 | ); |
||
173 | Assert::stringNotEmpty( |
||
174 | $config['identifyingAttribute'], |
||
175 | "Consent: 'identifyingAttribute' must be a non-empty string.", |
||
176 | ); |
||
177 | $this->identifyingAttribute = $config['identifyingAttribute']; |
||
178 | } |
||
179 | |||
180 | |||
181 | /** |
||
182 | * Helper function to check whether consent is disabled. |
||
183 | * |
||
184 | * @param mixed $option The consent.disable option. Either an array of array, an array or a boolean. |
||
185 | * @param string $entityId The entityID of the SP/IdP. |
||
186 | * |
||
187 | * @return boolean True if disabled, false if not. |
||
188 | */ |
||
189 | private static function checkDisable($option, string $entityId): bool |
||
190 | { |
||
191 | if (is_array($option)) { |
||
192 | // Check if consent.disable array has one element that is an array |
||
193 | if (count($option) === count($option, COUNT_RECURSIVE)) { |
||
194 | // Array is not multidimensional. Simple in_array search suffices |
||
195 | return in_array($entityId, $option, true); |
||
196 | } |
||
197 | |||
198 | // Array contains at least one element that is an array, verify both possibilities |
||
199 | if (in_array($entityId, $option, true)) { |
||
200 | return true; |
||
201 | } |
||
202 | |||
203 | // Search in multidimensional arrays |
||
204 | foreach ($option as $optionToTest) { |
||
205 | if (!is_array($optionToTest)) { |
||
206 | continue; // bad option |
||
207 | } |
||
208 | |||
209 | if (!array_key_exists('type', $optionToTest)) { |
||
210 | continue; // option has no type |
||
211 | } |
||
212 | |||
213 | // Option has a type - switch processing depending on type value : |
||
214 | if ($optionToTest['type'] === 'regex') { |
||
215 | // regex-based consent disabling |
||
216 | |||
217 | if (!array_key_exists('pattern', $optionToTest)) { |
||
218 | continue; // no pattern defined |
||
219 | } |
||
220 | |||
221 | if (preg_match($optionToTest['pattern'], $entityId) === 1) { |
||
222 | return true; |
||
223 | } |
||
224 | } else { |
||
225 | // option type is not supported |
||
226 | continue; |
||
227 | } |
||
228 | } // end foreach |
||
229 | |||
230 | // Base case : no match |
||
231 | return false; |
||
232 | } else { |
||
233 | return (bool) $option; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | |||
238 | /** |
||
239 | * Process a authentication response |
||
240 | * |
||
241 | * This function saves the state, and redirects the user to the page where the user can authorize the release of |
||
242 | * the attributes. If storage is used and the consent has already been given the user is passed on. |
||
243 | * |
||
244 | * @param array &$state The state of the response. |
||
245 | * |
||
246 | * |
||
247 | * @throws \SimpleSAML\Module\saml\Error\NoPassive if the request was passive and consent is needed. |
||
248 | */ |
||
249 | public function process(array &$state): void |
||
379 | } |
||
380 | |||
381 | |||
382 | /** |
||
383 | * Generate a unique identifier of the user. |
||
384 | * |
||
385 | * @param string $userid The user id. |
||
386 | * @param string $source The source id. |
||
387 | * |
||
388 | * @return string SHA1 of the user id, source id and salt. |
||
389 | */ |
||
390 | public static function getHashedUserID(string $userid, string $source): string |
||
394 | } |
||
395 | |||
396 | |||
397 | /** |
||
398 | * Generate a unique targeted identifier. |
||
399 | * |
||
400 | * @param string $userid The user id. |
||
401 | * @param string $source The source id. |
||
402 | * @param string $destination The destination id. |
||
403 | * |
||
404 | * @return string SHA1 of the user id, source id, destination id and salt. |
||
405 | */ |
||
406 | public static function getTargetedID(string $userid, string $source, string $destination): string |
||
410 | } |
||
411 | |||
412 | |||
413 | /** |
||
414 | * Generate unique identifier for attributes. |
||
415 | * |
||
416 | * Create a hash value for the attributes that changes when attributes are added or removed. If the attribute |
||
417 | * values are included in the hash, the hash will change if the values change. |
||
418 | * |
||
419 | * @param array $attributes The attributes. |
||
420 | * @param bool $includeValues Whether or not to include the attribute value in the generation of the hash. |
||
421 | * |
||
422 | * @return string SHA1 of the user id, source id, destination id and salt. |
||
423 | */ |
||
424 | public static function getAttributeHash(array $attributes, bool $includeValues = false): string |
||
438 | } |
||
439 | } |
||
440 |