1<?php 2/* 3 * consentAdmin - Consent administration module 4 * 5 * This module enables the user to add and remove consents given for a given 6 * Service Provider. 7 * 8 * The module relies on methods and functions from the Consent module and can 9 * not be user without it. 10 * 11 * Author: Mads Freek <freek@ruc.dk>, Jacob Christiansen <jach@wayf.dk> 12 */ 13 14/** 15 * Runs the processing chain and ignores all filter which have user 16 * interaction. 17 * 18 * @param array $idp_metadata 19 * @param string $source 20 * @param array $sp_metadata 21 * @param string $sp_entityid 22 * @param array $attributes 23 * @param string $userid 24 * @param bool $hashAttributes 25 * @param array $excludeAttributes 26 * @return array 27 */ 28function driveProcessingChain( 29 $idp_metadata, 30 $source, 31 $sp_metadata, 32 $sp_entityid, 33 $attributes, 34 $userid, 35 $hashAttributes = false, 36 $excludeAttributes = [] 37) { 38 /* 39 * Create a new processing chain 40 */ 41 $pc = new \SimpleSAML\Auth\ProcessingChain($idp_metadata, $sp_metadata, 'idp'); 42 43 /* 44 * Construct the state. 45 * REMEMBER: Do not set Return URL if you are calling processStatePassive 46 */ 47 $authProcState = [ 48 'Attributes' => $attributes, 49 'Destination' => $sp_metadata, 50 'SPMetadata' => $sp_metadata, 51 'Source' => $idp_metadata, 52 'IdPMetadata' => $idp_metadata, 53 'isPassive' => true, 54 ]; 55 /* we're being bridged, so add that info to the state */ 56 if (strpos($source, '-idp-remote|') !== false) { 57 /** @var int $i */ 58 $i = strpos($source, '|'); 59 $authProcState['saml:sp:IdP'] = substr($source, $i + 1); 60 } 61 62 /* 63 * Call processStatePAssive. 64 * We are not interested in any user interaction, only modifications to the attributes 65 */ 66 $pc->processStatePassive($authProcState); 67 68 $attributes = $authProcState['Attributes']; 69 // Remove attributes that do not require consent/should be excluded 70 foreach ($attributes as $attrkey => $attrval) { 71 if (in_array($attrkey, $excludeAttributes)) { 72 unset($attributes[$attrkey]); 73 } 74 } 75 76 /* 77 * Generate identifiers and hashes 78 */ 79 $destination = $sp_metadata['metadata-set'].'|'.$sp_entityid; 80 81 $targeted_id = \SimpleSAML\Module\consent\Auth\Process\Consent::getTargetedID($userid, $source, $destination); 82 $attribute_hash = \SimpleSAML\Module\consent\Auth\Process\Consent::getAttributeHash($attributes, $hashAttributes); 83 84 \SimpleSAML\Logger::info('consentAdmin: user: '.$userid); 85 \SimpleSAML\Logger::info('consentAdmin: target: '.$targeted_id); 86 \SimpleSAML\Logger::info('consentAdmin: attribute: '.$attribute_hash); 87 88 // Return values 89 return [$targeted_id, $attribute_hash, $attributes]; 90} 91 92// Get config object 93$config = \SimpleSAML\Configuration::getInstance(); 94$cA_config = \SimpleSAML\Configuration::getConfig('module_consentAdmin.php'); 95$authority = $cA_config->getValue('authority'); 96 97$as = new \SimpleSAML\Auth\Simple($authority); 98 99// If request is a logout request 100if (array_key_exists('logout', $_REQUEST)) { 101 $returnURL = $cA_config->getValue('returnURL'); 102 $as->logout($returnURL); 103} 104 105$hashAttributes = $cA_config->getValue('attributes.hash'); 106 107$excludeAttributes = $cA_config->getValue('attributes.exclude', []); 108 109// Check if valid local session exists 110$as->requireAuth(); 111 112// Get released attributes 113$attributes = $as->getAttributes(); 114 115// Get metadata storage handler 116$metadata = \SimpleSAML\Metadata\MetaDataStorageHandler::getMetadataHandler(); 117 118/* 119 * Get IdP id and metadata 120 */ 121 122$idp_entityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); 123$idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-hosted'); 124 125// Calc correct source 126if ($as->getAuthData('saml:sp:IdP') !== null) { 127 // from a remote idp (as bridge) 128 $source = 'saml20-idp-remote|'.$as->getAuthData('saml:sp:IdP'); 129} else { 130 // from the local idp 131 $source = $idp_metadata['metadata-set'].'|'.$idp_entityid; 132} 133 134// Get user ID 135if (isset($idp_metadata['userid.attribute']) && is_string($idp_metadata['userid.attribute'])) { 136 $userid_attributename = $idp_metadata['userid.attribute']; 137} else { 138 $userid_attributename = 'eduPersonPrincipalName'; 139} 140 141$userids = $attributes[$userid_attributename]; 142 143if (empty($userids)) { 144 throw new \Exception('Could not generate useridentifier for storing consent. Attribute ['. 145 $userid_attributename.'] was not available.'); 146} 147 148$userid = $userids[0]; 149 150// Get all SP metadata 151$all_sp_metadata = $metadata->getList('saml20-sp-remote'); 152 153// Parse action, if any 154$action = null; 155$sp_entityid = null; 156if (!empty($_GET['cv'])) { 157 $sp_entityid = $_GET['cv']; 158} 159if (!empty($_GET['action'])) { 160 $action = $_GET["action"]; 161} 162 163\SimpleSAML\Logger::critical('consentAdmin: sp: '.$sp_entityid.' action: '.$action); 164 165// Remove services, whitch have consent disabled 166if (isset($idp_metadata['consent.disable'])) { 167 foreach ($idp_metadata['consent.disable'] as $disable) { 168 if (array_key_exists($disable, $all_sp_metadata)) { 169 unset($all_sp_metadata[$disable]); 170 } 171 } 172} 173 174\SimpleSAML\Logger::info('consentAdmin: '.$idp_entityid); 175 176// Parse consent config 177$consent_storage = \SimpleSAML\Module\consent\Store::parseStoreConfig($cA_config->getValue('consentadmin')); 178 179// Calc correct user ID hash 180$hashed_user_id = \SimpleSAML\Module\consent\Auth\Process\Consent::getHashedUserID($userid, $source); 181 182// If a checkbox have been clicked 183if ($action !== null && $sp_entityid !== null) { 184 // init template to enable translation of status messages 185 $template = new \SimpleSAML\XHTML\Template( 186 $config, 187 'consentAdmin:consentadminajax.php', 188 'consentAdmin:consentadmin' 189 ); 190 $translator = $template->getTranslator(); 191 192 // Get SP metadata 193 $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote'); 194 195 // Run AuthProc filters 196 list($targeted_id, $attribute_hash, $attributes_new) = driveProcessingChain( 197 $idp_metadata, 198 $source, 199 $sp_metadata, 200 $sp_entityid, 201 $attributes, 202 $userid, 203 $hashAttributes, 204 $excludeAttributes 205 ); 206 207 // Add a consent (or update if attributes have changed and old consent for SP and IdP exists) 208 if ($action == 'true') { 209 $isStored = $consent_storage->saveConsent($hashed_user_id, $targeted_id, $attribute_hash); 210 if ($isStored) { 211 $res = $translator->t("added"); 212 } else { 213 $res = $translator->t("updated"); 214 } 215 // Remove consent 216 } else { 217 if ($action == 'false') { 218 // Got consent, so this is a request to remove it 219 $rowcount = $consent_storage->deleteConsent($hashed_user_id, $targeted_id); 220 if ($rowcount > 0) { 221 $res = $translator->t("removed"); 222 } else { 223 throw new \Exception("Unknown action (should not happen)"); 224 } 225 } else { 226 \SimpleSAML\Logger::info('consentAdmin: unknown action'); 227 $res = $translator->t("unknown"); 228 } 229 } 230 $template->data['res'] = $res; 231 $template->show(); 232 exit; 233} 234 235// Get all consents for user 236$user_consent_list = $consent_storage->getConsents($hashed_user_id); 237 238// Parse list of consents 239$user_consent = []; 240foreach ($user_consent_list as $c) { 241 $user_consent[$c[0]] = $c[1]; 242} 243 244$template_sp_content = []; 245 246// Init template 247$template = new \SimpleSAML\XHTML\Template($config, 'consentAdmin:consentadmin.php', 'consentAdmin:consentadmin'); 248$translator = $template->getTranslator(); 249$translator->includeLanguageFile('attributes'); // attribute listings translated by this dictionary 250 251$sp_empty_description = $translator->getTag('sp_empty_description'); 252$sp_list = []; 253 254// Process consents for all SP 255foreach ($all_sp_metadata as $sp_entityid => $sp_values) { 256 // Get metadata for SP 257 $sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote'); 258 259 // Run attribute filters 260 list($targeted_id, $attribute_hash, $attributes_new) = driveProcessingChain( 261 $idp_metadata, 262 $source, 263 $sp_metadata, 264 $sp_entityid, 265 $attributes, 266 $userid, 267 $hashAttributes, 268 $excludeAttributes 269 ); 270 271 // Translate attribute-names 272 foreach ($attributes_new as $orig_name => $value) { 273 if (isset($template->data['attribute_'.htmlspecialchars(strtolower($orig_name))])) { 274 $old_name = $template->data['attribute_'.htmlspecialchars(strtolower($orig_name))]; 275 } 276 $name = $translator->getAttributeTranslation(strtolower($orig_name)); // translate 277 278 $attributes_new[$name] = $value; 279 unset($attributes_new[$orig_name]); 280 } 281 282 // Check if consent exists 283 if (array_key_exists($targeted_id, $user_consent)) { 284 $sp_status = "changed"; 285 \SimpleSAML\Logger::info('consentAdmin: changed'); 286 // Check if consent is valid. (Possible that attributes has changed) 287 if ($user_consent[$targeted_id] == $attribute_hash) { 288 \SimpleSAML\Logger::info('consentAdmin: ok'); 289 $sp_status = "ok"; 290 } 291 // Consent does not exist 292 } else { 293 SimpleSAML\Logger::info('consentAdmin: none'); 294 $sp_status = "none"; 295 } 296 297 // Set name of SP 298 if (isset($sp_values['name']) && is_array($sp_values['name'])) { 299 $sp_name = $sp_metadata['name']; 300 } else { 301 if (isset($sp_values['name']) && is_string($sp_values['name'])) { 302 $sp_name = $sp_metadata['name']; 303 } elseif (isset($sp_values['OrganizationDisplayName']) && is_array($sp_values['OrganizationDisplayName'])) { 304 $sp_name = $sp_metadata['OrganizationDisplayName']; 305 } else { 306 $sp_name = $sp_entityid; 307 } 308 } 309 310 // Set description of SP 311 if (empty($sp_metadata['description']) || !is_array($sp_metadata['description'])) { 312 $sp_description = $sp_empty_description; 313 } else { 314 $sp_description = $sp_metadata['description']; 315 } 316 317 // Add a URL to the service if present in metadata 318 $sp_service_url = isset($sp_metadata['ServiceURL']) ? $sp_metadata['ServiceURL'] : null; 319 320 // Translate SP name and description 321 $translator->includeInlineTranslation('spname', $sp_name); 322 $translator->includeInlineTranslation('spdescription', $sp_description); 323 324 $sp_name = $translator->getPreferredTranslation($translator->getTag('spname')); 325 $sp_description = $translator->getPreferredTranslation($translator->getTag('spdescription')); 326 327 // Fill out array for the template 328 $sp_list[$sp_entityid] = [ 329 'spentityid' => $sp_entityid, 330 'name' => $sp_name, 331 'description' => $sp_description, 332 'consentStatus' => $sp_status, 333 'consentValue' => $sp_entityid, 334 'attributes_by_sp' => $attributes_new, 335 'serviceurl' => $sp_service_url, 336 ]; 337} 338 339$template->data['header'] = 'Consent Administration'; 340$template->data['spList'] = $sp_list; 341$template->data['showDescription'] = $cA_config->getValue('showDescription'); 342$template->show(); 343