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)) {
101  throw new SimpleSAML_Error_Exception(
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'])) {
111  throw new SimpleSAML_Error_Exception(
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'])) {
121  throw new SimpleSAML_Error_Exception(
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'])) {
130  throw new SimpleSAML_Error_Exception(
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 }
static getMetadataHandler()
This function retrieves the current instance of the metadata handler.
$config
Definition: bootstrap.php:15
$idpEntityId
Definition: prp.php:12
if(empty($userids)) $userid
static debug($string)
Definition: Logger.php:211
$spEntityId
$destination
if(!array_key_exists('StateId', $_REQUEST)) $id
$idpmeta
Definition: metadata.php:20
static redirectTrustedURL($url, $parameters=array())
This function redirects to the specified URL without performing any security checks.
Definition: HTTP.php:959
$metadata['__DYNAMIC:1__']
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
Definition: Module.php:220
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
static stats($string)
Definition: Logger.php:222
Attribute-related utility methods.
static warning($string)
Definition: Logger.php:177
$values
static error($string)
Definition: Logger.php:166
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
if($source===null) if(!($source instanceof sspmod_saml_Auth_Source_SP)) $entityId
Definition: metadata.php:22
$url
$source
Definition: linkback.php:22
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
static saveState(&$state, $stage, $rawId=false)
Save the state.
Definition: State.php:194
static log($event, array $data=array())
Notify about an event.
Definition: Stats.php:71