ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Consent.php
Go to the documentation of this file.
1<?php
2
3
13{
19 private $_focus = null;
20
26 private $_includeValues = false;
27
33 private $_checked = false;
34
40 private $_store = null;
41
47 private $_hiddenAttributes = array();
48
54 private $_noconsentattributes = array();
55
62
63
74 public function __construct($config, $reserved)
75 {
76 assert(is_array($config));
77 parent::__construct($config, $reserved);
78
79 if (array_key_exists('includeValues', $config)) {
80 if (!is_bool($config['includeValues'])) {
82 'Consent: includeValues must be boolean. '.
83 var_export($config['includeValues'], true).' given.'
84 );
85 }
86 $this->_includeValues = $config['includeValues'];
87 }
88
89 if (array_key_exists('checked', $config)) {
90 if (!is_bool($config['checked'])) {
92 'Consent: checked must be boolean. '.
93 var_export($config['checked'], true).' given.'
94 );
95 }
96 $this->_checked = $config['checked'];
97 }
98
99 if (array_key_exists('focus', $config)) {
100 if (!in_array($config['focus'], array('yes', 'no'), true)) {
102 'Consent: focus must be a string with values `yes` or `no`. '.
103 var_export($config['focus'], true).' given.'
104 );
105 }
106 $this->_focus = $config['focus'];
107 }
108
109 if (array_key_exists('hiddenAttributes', $config)) {
110 if (!is_array($config['hiddenAttributes'])) {
112 'Consent: hiddenAttributes must be an array. '.
113 var_export($config['hiddenAttributes'], true).' given.'
114 );
115 }
116 $this->_hiddenAttributes = $config['hiddenAttributes'];
117 }
118
119 if (array_key_exists('attributes.exclude', $config)) {
120 if (!is_array($config['attributes.exclude'])) {
122 'Consent: attributes.exclude must be an array. '.
123 var_export($config['attributes.exclude'], true).' given.'
124 );
125 }
126 $this->_noconsentattributes = $config['attributes.exclude'];
127 } elseif (array_key_exists('noconsentattributes', $config)) {
128 SimpleSAML\Logger::warning("The 'noconsentattributes' option has been deprecated in favour of 'attributes.exclude'.");
129 if (!is_array($config['noconsentattributes'])) {
131 'Consent: noconsentattributes must be an array. '.
132 var_export($config['noconsentattributes'], true).' given.'
133 );
134 }
135 $this->_noconsentattributes = $config['noconsentattributes'];
136 }
137
138 if (array_key_exists('store', $config)) {
139 try {
140 $this->_store = sspmod_consent_Store::parseStoreConfig($config['store']);
141 } catch (Exception $e) {
143 'Consent: Could not create consent storage: '.
144 $e->getMessage()
145 );
146 }
147 }
148
149 if (array_key_exists('showNoConsentAboutService', $config)) {
150 if (!is_bool($config['showNoConsentAboutService'])) {
151 throw new SimpleSAML_Error_Exception('Consent: showNoConsentAboutService must be a boolean.');
152 }
153 $this->_showNoConsentAboutService = $config['showNoConsentAboutService'];
154 }
155 }
156
157
166 private static function checkDisable($option, $entityId)
167 {
168 if (is_array($option)) {
169 // Check if consent.disable array has one element that is an array
170 if (count($option) === count($option, COUNT_RECURSIVE)) {
171 // Array is not multidimensional. Simple in_array search suffices
172 return in_array($entityId, $option, true);
173 }
174
175 // Array contains at least one element that is an array, verify both possibilities
176 if (in_array($entityId, $option, true)) {
177 return true;
178 }
179
180 // Search in multidimensional arrays
181 foreach ($option as $optionToTest) {
182 if (!is_array($optionToTest)) {
183 continue; // bad option
184 }
185
186 if (!array_key_exists('type', $optionToTest)) {
187 continue; // option has no type
188 }
189
190 // Option has a type - switch processing depending on type value :
191 if ($optionToTest['type'] === 'regex') {
192 // regex-based consent disabling
193
194 if (!array_key_exists('pattern', $optionToTest)) {
195 continue; // no pattern defined
196 }
197
198 if (preg_match($optionToTest['pattern'], $entityId) === 1) {
199 return true;
200 }
201 } else {
202 // option type is not supported
203 continue;
204 }
205 } // end foreach
206
207 // Base case : no match
208 return false;
209 } else {
210 return (boolean) $option;
211 }
212 }
213
214
227 public function process(&$state)
228 {
229 assert(is_array($state));
230 assert(array_key_exists('UserID', $state));
231 assert(array_key_exists('Destination', $state));
232 assert(array_key_exists('entityid', $state['Destination']));
233 assert(array_key_exists('metadata-set', $state['Destination']));
234 assert(array_key_exists('entityid', $state['Source']));
235 assert(array_key_exists('metadata-set', $state['Source']));
236
237 $spEntityId = $state['Destination']['entityid'];
238 $idpEntityId = $state['Source']['entityid'];
239
241
248 if (isset($state['saml:sp:IdP'])) {
249 $idpEntityId = $state['saml:sp:IdP'];
250 $idpmeta = $metadata->getMetaData($idpEntityId, 'saml20-idp-remote');
251 $state['Source'] = $idpmeta;
252 }
253
254 $statsData = array('spEntityID' => $spEntityId);
255
256 // Do not use consent if disabled
257 if (isset($state['Source']['consent.disable']) &&
258 self::checkDisable($state['Source']['consent.disable'], $spEntityId)
259 ) {
260 SimpleSAML\Logger::debug('Consent: Consent disabled for entity '.$spEntityId.' with IdP '.$idpEntityId);
261 SimpleSAML_Stats::log('consent:disabled', $statsData);
262 return;
263 }
264 if (isset($state['Destination']['consent.disable']) &&
265 self::checkDisable($state['Destination']['consent.disable'], $idpEntityId)
266 ) {
267 SimpleSAML\Logger::debug('Consent: Consent disabled for entity '.$spEntityId.' with IdP '.$idpEntityId);
268 SimpleSAML_Stats::log('consent:disabled', $statsData);
269 return;
270 }
271
272 if ($this->_store !== null) {
273 $source = $state['Source']['metadata-set'].'|'.$idpEntityId;
274 $destination = $state['Destination']['metadata-set'].'|'.$spEntityId;
275 $attributes = $state['Attributes'];
276
277 // Remove attributes that do not require consent
278 foreach ($attributes as $attrkey => $attrval) {
279 if (in_array($attrkey, $this->_noconsentattributes, true)) {
280 unset($attributes[$attrkey]);
281 }
282 }
283
284 SimpleSAML\Logger::debug('Consent: userid: '.$state['UserID']);
285 SimpleSAML\Logger::debug('Consent: source: '.$source);
286 SimpleSAML\Logger::debug('Consent: destination: '.$destination);
287
288 $userId = self::getHashedUserID($state['UserID'], $source);
289 $targetedId = self::getTargetedID($state['UserID'], $source, $destination);
290 $attributeSet = self::getAttributeHash($attributes, $this->_includeValues);
291
293 'Consent: hasConsent() ['.$userId.'|'.$targetedId.'|'.
294 $attributeSet.']'
295 );
296
297 try {
298 if ($this->_store->hasConsent($userId, $targetedId, $attributeSet)) {
299 // Consent already given
300 SimpleSAML\Logger::stats('consent found');
301 SimpleSAML_Stats::log('consent:found', $statsData);
302 return;
303 }
304
305 SimpleSAML\Logger::stats('consent notfound');
306 SimpleSAML_Stats::log('consent:notfound', $statsData);
307
308 $state['consent:store'] = $this->_store;
309 $state['consent:store.userId'] = $userId;
310 $state['consent:store.destination'] = $targetedId;
311 $state['consent:store.attributeSet'] = $attributeSet;
312 } catch (Exception $e) {
313 SimpleSAML\Logger::error('Consent: Error reading from storage: '.$e->getMessage());
314 SimpleSAML\Logger::stats('Ccnsent failed');
315 SimpleSAML_Stats::log('consent:failed', $statsData);
316 }
317 } else {
318 SimpleSAML\Logger::stats('consent nostorage');
319 SimpleSAML_Stats::log('consent:nostorage', $statsData);
320 }
321
322 $state['consent:focus'] = $this->_focus;
323 $state['consent:checked'] = $this->_checked;
324 $state['consent:hiddenAttributes'] = $this->_hiddenAttributes;
325 $state['consent:noconsentattributes'] = $this->_noconsentattributes;
326 $state['consent:showNoConsentAboutService'] = $this->_showNoConsentAboutService;
327
328 // user interaction necessary. Throw exception on isPassive request
329 if (isset($state['isPassive']) && $state['isPassive'] === true) {
330 SimpleSAML_Stats::log('consent:nopassive', $statsData);
332 \SAML2\Constants::STATUS_REQUESTER,
333 'Unable to give consent on passive request.'
334 );
335 }
336
337 // Save state and redirect
338 $id = SimpleSAML_Auth_State::saveState($state, 'consent:request');
339 $url = SimpleSAML\Module::getModuleURL('consent/getconsent.php');
341 }
342
343
352 public static function getHashedUserID($userid, $source)
353 {
354 return hash('sha1', $userid.'|'.SimpleSAML\Utils\Config::getSecretSalt().'|'.$source);
355 }
356
357
367 public static function getTargetedID($userid, $source, $destination)
368 {
369 return hash('sha1', $userid.'|'.SimpleSAML\Utils\Config::getSecretSalt().'|'.$source.'|'.$destination);
370 }
371
372
384 public static function getAttributeHash($attributes, $includeValues = false)
385 {
386 if ($includeValues) {
387 foreach ($attributes as &$values) {
388 sort($values);
389 }
390 ksort($attributes);
391 $hashBase = serialize($attributes);
392 } else {
393 $names = array_keys($attributes);
394 sort($names);
395 $hashBase = implode('|', $names);
396 }
397 return hash('sha1', $hashBase);
398 }
399}
$metadata['__DYNAMIC:1__']
$spEntityId
$source
Definition: linkback.php:22
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
An exception for terminatinating execution or to throw for unit testing.
static warning($string)
Definition: Logger.php:177
static stats($string)
Definition: Logger.php:222
static error($string)
Definition: Logger.php:166
static debug($string)
Definition: Logger.php:211
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
Definition: Module.php:220
static redirectTrustedURL($url, $parameters=array())
This function redirects to the specified URL without performing any security checks.
Definition: HTTP.php:959
static saveState(&$state, $stage, $rawId=false)
Save the state.
Definition: State.php:194
static getMetadataHandler()
This function retrieves the current instance of the metadata handler.
static log($event, array $data=array())
Notify about an event.
Definition: Stats.php:71
if(empty($userids)) $userid
if(!array_key_exists('StateId', $_REQUEST)) $id
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
$config
Definition: bootstrap.php:15
$idpmeta
Definition: metadata.php:20
if( $source===null) if(!($source instanceof sspmod_saml_Auth_Source_SP)) $entityId
Definition: metadata.php:22
$destination
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
Attribute-related utility methods.
$url
$idpEntityId
Definition: prp.php:12
$values