1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace SimpleSAML\Module\consentAdmin\Controller; |
||||
6 | |||||
7 | use Exception; |
||||
8 | use SimpleSAML\Auth; |
||||
9 | use SimpleSAML\Configuration; |
||||
10 | use SimpleSAML\Logger; |
||||
11 | use SimpleSAML\Metadata\MetaDataStorageHandler; |
||||
12 | use SimpleSAML\Module\consent\Auth\Process\Consent; |
||||
13 | use SimpleSAML\Module\consent\Store; |
||||
14 | use SimpleSAML\Session; |
||||
15 | use SimpleSAML\XHTML\Template; |
||||
16 | use Symfony\Component\HttpFoundation\Request; |
||||
17 | |||||
18 | use function array_key_exists; |
||||
19 | use function in_array; |
||||
20 | use function is_array; |
||||
21 | use function sprintf; |
||||
22 | use function strpos; |
||||
23 | use function substr; |
||||
24 | |||||
25 | /** |
||||
26 | * Controller class for the consentadmin module. |
||||
27 | * |
||||
28 | * This class serves the different views available in the module. |
||||
29 | * |
||||
30 | * @package simplesamlphp/simplesamlphp-module-consentAdmin |
||||
31 | */ |
||||
32 | class ConsentAdmin |
||||
33 | { |
||||
34 | /** @var \SimpleSAML\Configuration */ |
||||
35 | protected Configuration $config; |
||||
36 | |||||
37 | /** @var \SimpleSAML\Configuration */ |
||||
38 | protected Configuration $moduleConfig; |
||||
39 | |||||
40 | /** @var \SimpleSAML\Session */ |
||||
41 | protected Session $session; |
||||
42 | |||||
43 | /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */ |
||||
44 | protected MetaDataStorageHandler $metadataStorageHandler; |
||||
45 | |||||
46 | /** |
||||
47 | * @var \SimpleSAML\Auth\Simple|string |
||||
48 | * @psalm-var \SimpleSAML\Auth\Simple|class-string |
||||
49 | */ |
||||
50 | protected $authSimple = Auth\Simple::class; |
||||
51 | |||||
52 | /** |
||||
53 | * @var \SimpleSAML\Module\consent\Auth\Process\Consent|string |
||||
54 | * @psalm-var \SimpleSAML\Module\consent\Auth\Process\Consent|class-string |
||||
55 | */ |
||||
56 | protected $consent = Consent::class; |
||||
57 | |||||
58 | /** |
||||
59 | * @var \SimpleSAML\Module\consent\Store|string |
||||
60 | * @psalm-var \SimpleSAML\Module\consent\Store|class-string |
||||
61 | */ |
||||
62 | protected $store = Store::class; |
||||
63 | |||||
64 | |||||
65 | /** |
||||
66 | * Controller constructor. |
||||
67 | * |
||||
68 | * It initializes the global configuration and session for the controllers implemented here. |
||||
69 | * |
||||
70 | * @param \SimpleSAML\Configuration $config The configuration to use by the controllers. |
||||
71 | * @param \SimpleSAML\Session $session The session to use by the controllers. |
||||
72 | * |
||||
73 | * @throws \Exception |
||||
74 | */ |
||||
75 | public function __construct( |
||||
76 | Configuration $config, |
||||
77 | Session $session, |
||||
78 | ) { |
||||
79 | $this->config = $config; |
||||
80 | $this->moduleConfig = Configuration::getConfig('module_consentAdmin.php'); |
||||
81 | $this->metadataStorageHandler = MetaDataStorageHandler::getMetadataHandler(); |
||||
82 | $this->session = $session; |
||||
83 | } |
||||
84 | |||||
85 | |||||
86 | /** |
||||
87 | * Inject the \SimpleSAML\Module\consent\Store dependency. |
||||
88 | * |
||||
89 | * @param \SimpleSAML\Module\consent\Store $store |
||||
90 | */ |
||||
91 | public function setStore(Store $store): void |
||||
92 | { |
||||
93 | $this->store = $store; |
||||
94 | } |
||||
95 | |||||
96 | |||||
97 | /** |
||||
98 | * Inject the \SimpleSAML\Auth\Simple dependency. |
||||
99 | * |
||||
100 | * @param \SimpleSAML\Auth\Simple $authSimple |
||||
101 | */ |
||||
102 | public function setAuthSimple(Auth\Simple $authSimple): void |
||||
103 | { |
||||
104 | $this->authSimple = $authSimple; |
||||
105 | } |
||||
106 | |||||
107 | |||||
108 | /** |
||||
109 | * Inject the \SimpleSAML\Metadata\MetaDataStorageHandler dependency. |
||||
110 | * |
||||
111 | * @param \SimpleSAML\Metadata\MetaDataStorageHandler $handler |
||||
112 | */ |
||||
113 | public function setMetadataStorageHandler(MetadataStorageHandler $handler): void |
||||
114 | { |
||||
115 | $this->metadataStorageHandler = $handler; |
||||
116 | } |
||||
117 | |||||
118 | |||||
119 | /** |
||||
120 | * Inject the \SimpleSAML\Module\consent\Auth\Process\Consent dependency. |
||||
121 | * |
||||
122 | * @param \SimpleSAML\Module\consent\Auth\Process\Consent $consent |
||||
123 | */ |
||||
124 | public function setConsent(Consent $consent): void |
||||
125 | { |
||||
126 | $this->consent = $consent; |
||||
127 | } |
||||
128 | |||||
129 | |||||
130 | /** |
||||
131 | * @param \Symfony\Component\HttpFoundation\Request $request The current request. |
||||
132 | * |
||||
133 | * @return void |
||||
134 | */ |
||||
135 | public function logout(Request $request): void |
||||
0 ignored issues
–
show
|
|||||
136 | { |
||||
137 | $authority = $this->moduleConfig->getValue('authority'); |
||||
138 | $as = new $this->authSimple($authority); |
||||
139 | |||||
140 | $returnURL = $this->moduleConfig->getValue('returnURL'); |
||||
141 | $as->logout($returnURL); |
||||
142 | } |
||||
143 | |||||
144 | |||||
145 | /** |
||||
146 | * @param \Symfony\Component\HttpFoundation\Request $request The current request. |
||||
147 | * |
||||
148 | * @return \SimpleSAML\XHTML\Template |
||||
149 | */ |
||||
150 | public function main(Request $request): Template |
||||
151 | { |
||||
152 | $hashAttributes = $this->moduleConfig->getOptionalValue('attributes.hash', false); |
||||
153 | $excludeAttributes = $this->moduleConfig->getOptionalValue('attributes.exclude', []); |
||||
154 | |||||
155 | // Check if valid local session exists |
||||
156 | $authority = $this->moduleConfig->getValue('authority'); |
||||
157 | $as = new $this->authSimple($authority); |
||||
158 | $as->requireAuth(); |
||||
159 | |||||
160 | // Get released attributes |
||||
161 | $attributes = $as->getAttributes(); |
||||
162 | |||||
163 | // Get metadata storage handler |
||||
164 | $metadata = $this->metadataStorageHandler; |
||||
165 | |||||
166 | /* |
||||
167 | * Get IdP id and metadata |
||||
168 | */ |
||||
169 | $idp_entityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); |
||||
170 | $idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-hosted'); |
||||
171 | |||||
172 | // Calc correct source |
||||
173 | if ($as->getAuthData('saml:sp:IdP') !== null) { |
||||
174 | // from a remote idp (as bridge) |
||||
175 | $source = 'saml20-idp-remote|' . $as->getAuthData('saml:sp:IdP'); |
||||
176 | } else { |
||||
177 | // from the local idp |
||||
178 | $source = $idp_metadata['metadata-set'] . '|' . $idp_entityid; |
||||
179 | } |
||||
180 | |||||
181 | // Get user ID |
||||
182 | $userid_attributename = $this->moduleConfig->getOptionalString( |
||||
183 | 'identifyingAttribute', |
||||
184 | 'eduPersonPrincipalName', |
||||
185 | ); |
||||
186 | $userids = $attributes[$userid_attributename]; |
||||
187 | |||||
188 | if (empty($userids)) { |
||||
189 | throw new Exception(sprintf( |
||||
190 | 'Could not generate useridentifier for storing consent. Attribute [%s] was not available.', |
||||
191 | $userid_attributename, |
||||
192 | )); |
||||
193 | } |
||||
194 | |||||
195 | $userid = $userids[0]; |
||||
196 | |||||
197 | // Get all SP metadata |
||||
198 | $all_sp_metadata = $metadata->getList('saml20-sp-remote'); |
||||
199 | |||||
200 | $sp_entityid = $request->query->get('cv'); |
||||
201 | $action = $request->query->get('action'); |
||||
202 | |||||
203 | Logger::notice('consentAdmin: sp: ' . $sp_entityid . ' action: ' . $action); |
||||
204 | |||||
205 | // Remove services, whitch have consent disabled |
||||
206 | if (isset($idp_metadata['consent.disable'])) { |
||||
207 | foreach ($idp_metadata['consent.disable'] as $disable) { |
||||
208 | if (array_key_exists($disable, $all_sp_metadata)) { |
||||
209 | unset($all_sp_metadata[$disable]); |
||||
210 | } |
||||
211 | } |
||||
212 | } |
||||
213 | |||||
214 | Logger::info('consentAdmin: ' . $idp_entityid); |
||||
215 | |||||
216 | // Parse consent config |
||||
217 | $consent_storage = $this->store::parseStoreConfig($this->moduleConfig->getValue('consentadmin')); |
||||
218 | |||||
219 | // Calc correct user ID hash |
||||
220 | $hashed_user_id = $this->consent::getHashedUserID($userid, $source); |
||||
221 | |||||
222 | // If a checkbox have been clicked |
||||
223 | if ($action !== null && $sp_entityid !== null) { |
||||
224 | // init template to enable translation of status messages |
||||
225 | $template = new Template( |
||||
226 | $this->config, |
||||
227 | 'consentAdmin:consentadminajax.twig', |
||||
228 | 'consentAdmin:consentadmin', |
||||
0 ignored issues
–
show
The call to
SimpleSAML\XHTML\Template::__construct() has too many arguments starting with 'consentAdmin:consentadmin' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
229 | ); |
||||
230 | |||||
231 | // Get SP metadata |
||||
232 | $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote'); |
||||
233 | |||||
234 | // Run AuthProc filters |
||||
235 | list($targeted_id, $attribute_hash, $attributes) = $this->driveProcessingChain( |
||||
236 | $idp_metadata, |
||||
237 | $source, |
||||
238 | $sp_metadata, |
||||
239 | $sp_entityid, |
||||
240 | $attributes, |
||||
241 | $userid, |
||||
242 | $hashAttributes, |
||||
243 | $excludeAttributes, |
||||
244 | ); |
||||
245 | |||||
246 | // Add a consent (or update if attributes have changed and old consent for SP and IdP exists) |
||||
247 | if ($action == 'true') { |
||||
248 | $isStored = $consent_storage->saveConsent($hashed_user_id, $targeted_id, $attribute_hash); |
||||
249 | } else { |
||||
250 | if ($action == 'false') { |
||||
251 | // Got consent, so this is a request to remove it |
||||
252 | $consent_storage->deleteConsent($hashed_user_id, $targeted_id); |
||||
253 | $isStored = false; |
||||
254 | } else { |
||||
255 | Logger::info('consentAdmin: unknown action'); |
||||
256 | $isStored = null; |
||||
257 | } |
||||
258 | } |
||||
259 | $template->data['isStored'] = $isStored; |
||||
260 | return $template; |
||||
261 | } |
||||
262 | |||||
263 | // Get all consents for user |
||||
264 | $user_consent_list = $consent_storage->getConsents($hashed_user_id); |
||||
265 | |||||
266 | // Parse list of consents |
||||
267 | $user_consent = []; |
||||
268 | foreach ($user_consent_list as $c) { |
||||
269 | $user_consent[$c[0]] = $c[1]; |
||||
270 | } |
||||
271 | |||||
272 | // Init template |
||||
273 | $template = new Template($this->config, 'consentAdmin:consentadmin.twig', 'consentAdmin:consentadmin'); |
||||
274 | $template->getLocalization()->addAttributeDomains(); |
||||
275 | |||||
276 | $sp_list = []; |
||||
277 | |||||
278 | // Process consents for all SP |
||||
279 | foreach ($all_sp_metadata as $sp_entityid => $sp_values) { |
||||
280 | // Get metadata for SP |
||||
281 | $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote'); |
||||
282 | |||||
283 | // Run attribute filters |
||||
284 | list($targeted_id, $attribute_hash, $attributes) = $this->driveProcessingChain( |
||||
285 | $idp_metadata, |
||||
286 | $source, |
||||
287 | $sp_metadata, |
||||
288 | $sp_entityid, |
||||
289 | $attributes, |
||||
290 | $userid, |
||||
291 | $hashAttributes, |
||||
292 | $excludeAttributes, |
||||
293 | ); |
||||
294 | |||||
295 | // Check if consent exists |
||||
296 | if (array_key_exists($targeted_id, $user_consent)) { |
||||
297 | $sp_status = "changed"; |
||||
298 | Logger::info('consentAdmin: changed'); |
||||
299 | // Check if consent is valid. (Possible that attributes has changed) |
||||
300 | if ($user_consent[$targeted_id] == $attribute_hash) { |
||||
301 | Logger::info('consentAdmin: ok'); |
||||
302 | $sp_status = "ok"; |
||||
303 | } |
||||
304 | // Consent does not exist |
||||
305 | } else { |
||||
306 | Logger::info('consentAdmin: none'); |
||||
307 | $sp_status = "none"; |
||||
308 | } |
||||
309 | |||||
310 | // Set description of SP |
||||
311 | $sp_description = null; |
||||
312 | if (!empty($sp_metadata['description']) && is_array($sp_metadata['description'])) { |
||||
313 | $sp_description = $sp_metadata['description']; |
||||
314 | } |
||||
315 | |||||
316 | // Add a URL to the service if present in metadata |
||||
317 | $sp_service_url = isset($sp_metadata['ServiceURL']) ? $sp_metadata['ServiceURL'] : null; |
||||
318 | |||||
319 | // Fill out array for the template |
||||
320 | $sp_list[$sp_entityid] = [ |
||||
321 | 'spentityid' => $sp_entityid, |
||||
322 | 'name' => $template->getEntityDisplayName($sp_metadata), |
||||
323 | 'description' => $sp_description, |
||||
324 | 'consentStatus' => $sp_status, |
||||
325 | 'consentValue' => $sp_entityid, |
||||
326 | 'attributes_by_sp' => $attributes, |
||||
327 | 'serviceurl' => $sp_service_url, |
||||
328 | ]; |
||||
329 | } |
||||
330 | |||||
331 | $template->data['header'] = 'Consent Administration'; |
||||
332 | $template->data['spList'] = $sp_list; |
||||
333 | $template->data['showDescription'] = $this->moduleConfig->getBoolean('showDescription'); |
||||
334 | |||||
335 | return $template; |
||||
336 | } |
||||
337 | |||||
338 | |||||
339 | /** |
||||
340 | * Runs the processing chain and ignores all filter which have user |
||||
341 | * interaction. |
||||
342 | * |
||||
343 | * @param array $idp_metadata |
||||
344 | * @param string $source |
||||
345 | * @param array $sp_metadata |
||||
346 | * @param string $sp_entityid |
||||
347 | * @param array $attributes |
||||
348 | * @param string $userid |
||||
349 | * @param bool $hashAttributes |
||||
350 | * @param array $excludeAttributes |
||||
351 | * @return array |
||||
352 | */ |
||||
353 | private function driveProcessingChain( |
||||
354 | array $idp_metadata, |
||||
355 | string $source, |
||||
356 | array $sp_metadata, |
||||
357 | string $sp_entityid, |
||||
358 | array $attributes, |
||||
359 | string $userid, |
||||
360 | bool $hashAttributes, |
||||
361 | array $excludeAttributes, |
||||
362 | ): array { |
||||
363 | /* |
||||
364 | * Create a new processing chain |
||||
365 | */ |
||||
366 | $pc = new Auth\ProcessingChain($idp_metadata, $sp_metadata, 'idp'); |
||||
367 | |||||
368 | /* |
||||
369 | * Construct the state. |
||||
370 | * REMEMBER: Do not set Return URL if you are calling processStatePassive |
||||
371 | */ |
||||
372 | $authProcState = [ |
||||
373 | 'Attributes' => $attributes, |
||||
374 | 'Destination' => $sp_metadata, |
||||
375 | 'SPMetadata' => $sp_metadata, |
||||
376 | 'Source' => $idp_metadata, |
||||
377 | 'IdPMetadata' => $idp_metadata, |
||||
378 | 'isPassive' => true, |
||||
379 | ]; |
||||
380 | |||||
381 | /* we're being bridged, so add that info to the state */ |
||||
382 | if (strpos($source, '-idp-remote|') !== false) { |
||||
383 | /** @var int $i */ |
||||
384 | $i = strpos($source, '|'); |
||||
385 | $authProcState['saml:sp:IdP'] = substr($source, $i + 1); |
||||
386 | } |
||||
387 | |||||
388 | /* |
||||
389 | * Call processStatePAssive. |
||||
390 | * We are not interested in any user interaction, only modifications to the attributes |
||||
391 | */ |
||||
392 | $pc->processStatePassive($authProcState); |
||||
393 | |||||
394 | $attributes = $authProcState['Attributes']; |
||||
395 | // Remove attributes that do not require consent/should be excluded |
||||
396 | foreach ($attributes as $attrkey => $attrval) { |
||||
397 | if (in_array($attrkey, $excludeAttributes)) { |
||||
398 | unset($attributes[$attrkey]); |
||||
399 | } |
||||
400 | } |
||||
401 | |||||
402 | /* |
||||
403 | * Generate identifiers and hashes |
||||
404 | */ |
||||
405 | $destination = $sp_metadata['metadata-set'] . '|' . $sp_entityid; |
||||
406 | |||||
407 | $targeted_id = $this->consent::getTargetedID($userid, $source, $destination); |
||||
408 | $attribute_hash = $this->consent::getAttributeHash($attributes, $hashAttributes); |
||||
409 | |||||
410 | Logger::info('consentAdmin: user: ' . $userid); |
||||
411 | Logger::info('consentAdmin: target: ' . $targeted_id); |
||||
412 | Logger::info('consentAdmin: attribute: ' . $attribute_hash); |
||||
413 | |||||
414 | // Return values |
||||
415 | return [$targeted_id, $attribute_hash, $attributes]; |
||||
416 | } |
||||
417 | } |
||||
418 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.