ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
Consent.php
Go to the documentation of this file.
1<?php
2
3
13{
14
20 private $_focus = null;
21
27 private $_includeValues = false;
28
34 private $_checked = false;
35
41 private $_store = null;
42
48 private $_hiddenAttributes = array();
49
55 private $_noconsentattributes = array();
56
63
64
75 public function __construct($config, $reserved)
76 {
77 assert('is_array($config)');
78 parent::__construct($config, $reserved);
79
80 if (array_key_exists('includeValues', $config)) {
81 if (!is_bool($config['includeValues'])) {
83 'Consent: includeValues must be boolean. '.
84 var_export($config['includeValues'], true).' given.'
85 );
86 }
87 $this->_includeValues = $config['includeValues'];
88 }
89
90 if (array_key_exists('checked', $config)) {
91 if (!is_bool($config['checked'])) {
93 'Consent: checked must be boolean. '.
94 var_export($config['checked'], true).' given.'
95 );
96 }
97 $this->_checked = $config['checked'];
98 }
99
100 if (array_key_exists('focus', $config)) {
101 if (!in_array($config['focus'], array('yes', 'no'), true)) {
103 'Consent: focus must be a string with values `yes` or `no`. '.
104 var_export($config['focus'], true).' given.'
105 );
106 }
107 $this->_focus = $config['focus'];
108 }
109
110 if (array_key_exists('hiddenAttributes', $config)) {
111 if (!is_array($config['hiddenAttributes'])) {
113 'Consent: hiddenAttributes must be an array. '.
114 var_export($config['hiddenAttributes'], true).' given.'
115 );
116 }
117 $this->_hiddenAttributes = $config['hiddenAttributes'];
118 }
119
120 if (array_key_exists('noconsentattributes', $config)) {
121 if (!is_array($config['noconsentattributes'])) {
123 'Consent: noconsentattributes must be an array. '.
124 var_export($config['noconsentattributes'], true).' given.'
125 );
126 }
127 $this->_noconsentattributes = $config['noconsentattributes'];
128 }
129
130 if (array_key_exists('store', $config)) {
131 try {
132 $this->_store = sspmod_consent_Store::parseStoreConfig($config['store']);
133 } catch (Exception $e) {
135 'Consent: Could not create consent storage: '.
136 $e->getMessage()
137 );
138 }
139 }
140
141 if (array_key_exists('showNoConsentAboutService', $config)) {
142 if (!is_bool($config['showNoConsentAboutService'])) {
143 throw new SimpleSAML_Error_Exception('Consent: showNoConsentAboutService must be a boolean.');
144 }
145 $this->_showNoConsentAboutService = $config['showNoConsentAboutService'];
146 }
147 }
148
149
158 private static function checkDisable($option, $entityId)
159 {
160 if (is_array($option)) {
161 // Check if consent.disable array has one element that is an array
162 if (count($option) === count($option, COUNT_RECURSIVE)) {
163 // Array is not multidimensional. Simple in_array search suffices
164 return in_array($entityId, $option, true);
165 }
166
167 // Array contains at least one element that is an array, verify both possibilities
168 if (in_array($entityId, $option, true)) {
169 return true;
170 }
171
172 // Search in multidimensional arrays
173 foreach ($option as $optionToTest) {
174 if (!is_array($optionToTest)) {
175 continue; // bad option
176 }
177
178 if (!array_key_exists('type', $optionToTest)) {
179 continue; // option has no type
180 }
181
182 // Option has a type - switch processing depending on type value :
183 if ($optionToTest['type'] === 'regex') {
184 // regex-based consent disabling
185
186 if (!array_key_exists('pattern', $optionToTest)) {
187 continue; // no pattern defined
188 }
189
190 if (preg_match($optionToTest['pattern'], $entityId) === 1) {
191 return true;
192 }
193 } else {
194 // option type is not supported
195 continue;
196 }
197 } // end foreach
198
199 // Base case : no match
200 return false;
201 } else {
202 return (boolean) $option;
203 }
204 }
205
206
219 public function process(&$state)
220 {
221 assert('is_array($state)');
222 assert('array_key_exists("UserID", $state)');
223 assert('array_key_exists("Destination", $state)');
224 assert('array_key_exists("entityid", $state["Destination"])');
225 assert('array_key_exists("metadata-set", $state["Destination"])');
226 assert('array_key_exists("entityid", $state["Source"])');
227 assert('array_key_exists("metadata-set", $state["Source"])');
228
229 $spEntityId = $state['Destination']['entityid'];
230 $idpEntityId = $state['Source']['entityid'];
231
233
240 if (isset($state['saml:sp:IdP'])) {
241 $idpEntityId = $state['saml:sp:IdP'];
242 $idpmeta = $metadata->getMetaData($idpEntityId, 'saml20-idp-remote');
243 $state['Source'] = $idpmeta;
244 }
245
246 $statsData = array('spEntityID' => $spEntityId);
247
248 // Do not use consent if disabled
249 if (isset($state['Source']['consent.disable']) &&
250 self::checkDisable($state['Source']['consent.disable'], $spEntityId)
251 ) {
252 SimpleSAML\Logger::debug('Consent: Consent disabled for entity '.$spEntityId.' with IdP '.$idpEntityId);
253 SimpleSAML_Stats::log('consent:disabled', $statsData);
254 return;
255 }
256 if (isset($state['Destination']['consent.disable']) &&
257 self::checkDisable($state['Destination']['consent.disable'], $idpEntityId)
258 ) {
259 SimpleSAML\Logger::debug('Consent: Consent disabled for entity '.$spEntityId.' with IdP '.$idpEntityId);
260 SimpleSAML_Stats::log('consent:disabled', $statsData);
261 return;
262 }
263
264 if ($this->_store !== null) {
265 $source = $state['Source']['metadata-set'].'|'.$idpEntityId;
266 $destination = $state['Destination']['metadata-set'].'|'.$spEntityId;
267 $attributes = $state['Attributes'];
268
269 // Remove attributes that do not require consent
270 foreach ($attributes as $attrkey => $attrval) {
271 if (in_array($attrkey, $this->_noconsentattributes, true)) {
272 unset($attributes[$attrkey]);
273 }
274 }
275
276 SimpleSAML\Logger::debug('Consent: userid: '.$state['UserID']);
277 SimpleSAML\Logger::debug('Consent: source: '.$source);
278 SimpleSAML\Logger::debug('Consent: destination: '.$destination);
279
280 $userId = self::getHashedUserID($state['UserID'], $source);
281 $targetedId = self::getTargetedID($state['UserID'], $source, $destination);
282 $attributeSet = self::getAttributeHash($attributes, $this->_includeValues);
283
285 'Consent: hasConsent() ['.$userId.'|'.$targetedId.'|'.
286 $attributeSet.']'
287 );
288
289 try {
290 if ($this->_store->hasConsent($userId, $targetedId, $attributeSet)) {
291 // Consent already given
292 SimpleSAML\Logger::stats('consent found');
293 SimpleSAML_Stats::log('consent:found', $statsData);
294 return;
295 }
296
297 SimpleSAML\Logger::stats('consent notfound');
298 SimpleSAML_Stats::log('consent:notfound', $statsData);
299
300 $state['consent:store'] = $this->_store;
301 $state['consent:store.userId'] = $userId;
302 $state['consent:store.destination'] = $targetedId;
303 $state['consent:store.attributeSet'] = $attributeSet;
304 } catch (Exception $e) {
305 SimpleSAML\Logger::error('Consent: Error reading from storage: '.$e->getMessage());
306 SimpleSAML\Logger::stats('Ccnsent failed');
307 SimpleSAML_Stats::log('consent:failed', $statsData);
308 }
309 } else {
310 SimpleSAML\Logger::stats('consent nostorage');
311 SimpleSAML_Stats::log('consent:nostorage', $statsData);
312 }
313
314 $state['consent:focus'] = $this->_focus;
315 $state['consent:checked'] = $this->_checked;
316 $state['consent:hiddenAttributes'] = $this->_hiddenAttributes;
317 $state['consent:noconsentattributes'] = $this->_noconsentattributes;
318 $state['consent:showNoConsentAboutService'] = $this->_showNoConsentAboutService;
319
320 // user interaction necessary. Throw exception on isPassive request
321 if (isset($state['isPassive']) && $state['isPassive'] === true) {
322 SimpleSAML_Stats::log('consent:nopassive', $statsData);
323 throw new SimpleSAML_Error_NoPassive('Unable to give consent on passive request.');
324 }
325
326 // Save state and redirect
327 $id = SimpleSAML_Auth_State::saveState($state, 'consent:request');
328 $url = SimpleSAML\Module::getModuleURL('consent/getconsent.php');
330 }
331
332
341 public static function getHashedUserID($userid, $source)
342 {
343 return hash('sha1', $userid.'|'.SimpleSAML\Utils\Config::getSecretSalt().'|'.$source);
344 }
345
346
356 public static function getTargetedID($userid, $source, $destination)
357 {
358 return hash('sha1', $userid.'|'.SimpleSAML\Utils\Config::getSecretSalt().'|'.$source.'|'.$destination);
359 }
360
361
373 public static function getAttributeHash($attributes, $includeValues = false)
374 {
375 $hashBase = null;
376 if ($includeValues) {
377 ksort($attributes);
378 $hashBase = serialize($attributes);
379 } else {
380 $names = array_keys($attributes);
381 sort($names);
382 $hashBase = implode('|', $names);
383 }
384 return hash('sha1', $hashBase);
385 }
386}
$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 stats($string)
Definition: Logger.php:224
static error($string)
Definition: Logger.php:168
static debug($string)
Definition: Logger.php:213
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
Definition: Module.php:303
static redirectTrustedURL($url, $parameters=array())
This function redirects to the specified URL without performing any security checks.
Definition: HTTP.php:962
static saveState(&$state, $stage, $rawId=false)
Save the state.
Definition: State.php:194
Class SimpleSAML_Error_NoPassive.
Definition: NoPassive.php:12
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
$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
$attributes