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 
49 
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)) {
102  throw new SimpleSAML_Error_Exception(
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'])) {
112  throw new SimpleSAML_Error_Exception(
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'])) {
122  throw new SimpleSAML_Error_Exception(
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 }
static getMetadataHandler()
This function retrieves the current instance of the metadata handler.
$idpEntityId
Definition: prp.php:12
if(empty($userids)) $userid
static debug($string)
Definition: Logger.php:213
$spEntityId
$destination
if(!array_key_exists('StateId', $_REQUEST)) $id
$attributes
$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:962
$metadata['__DYNAMIC:1__']
Class SimpleSAML_Error_NoPassive.
Definition: NoPassive.php:12
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
Definition: Module.php:303
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
static stats($string)
Definition: Logger.php:224
Attribute-related utility methods.
static error($string)
Definition: Logger.php:168
Create styles array
The data for the language used.
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