diff --git a/modules/consent/default-disable b/modules/consent/default-disable new file mode 100644 index 0000000000000000000000000000000000000000..fa0bd82e2df7bd79d57593d35bc53c1f9d3ef71f --- /dev/null +++ b/modules/consent/default-disable @@ -0,0 +1,3 @@ +This file indicates that the default state of this module +is disabled. To enable, create a file named enable in the +same directory as this file. diff --git a/modules/consent/lib/Auth/Process/Consent.php b/modules/consent/lib/Auth/Process/Consent.php new file mode 100644 index 0000000000000000000000000000000000000000..e95b2aff098dfe4da20e62a01c5fc53c1d671824 --- /dev/null +++ b/modules/consent/lib/Auth/Process/Consent.php @@ -0,0 +1,145 @@ +<?php + +/** + * Filter for requiring the user to give consent before the attributes are released to the SP. + * + * The initial focus of the consent form can be set by setting the 'focus'-attribute to either + * 'yes' or 'no'. + * + * Different storage backends can be configured by setting the 'store'-attribute. The'store'-attribute + * is on the form <module>:<class>, and refers to the class sspmod_<module>_Consent_Store_<class>. For + * examples, see the built-in modules 'consent:Cookie' and 'consent:Database', which can be found + * under modules/consent/lib/Consent/Store. + * + * Example - minimal: + * <code> + * 'authproc' => array( + * 'consent:Consent', + * ), + * </code> + * + * Example - save in cookie: + * <code> + * 'authproc' => array( + * array( + * 'consent:Consent', + * 'store' => 'consent:Cookie', + * ), + * </code> + * + * Example - save in MySQL database: + * <code> + * 'authproc' => array( + * array( + * 'consent:Consent', + * 'store' => array( + * 'consent:Database', + * 'dsn' => 'mysql:host=db.example.org;dbname=simplesaml', + * 'username' => 'simplesaml', + * 'password' => 'secretpassword', + * ), + * ), + * ), + * </code> + * + * Example - initial focus on yes-button: + * <code> + * 'authproc' => array( + * array('consent:Consent', 'focus' => 'yes'), + * ), + * </code> + * + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilter { + + /** + * Where the focus should be in the form. Can be 'yesbutton', 'nobutton', or NULL. + */ + private $focus; + + + /** + * Consent store, if enabled. + */ + private $store; + + + /** + * Initialize consent filter. + * + * This is the constructor for the consent filter. It validates and parses the configuration. + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct($config, $reserved) { + parent::__construct($config, $reserved); + assert('is_array($config)'); + + if (array_key_exists('focus', $config)) { + $this->focus = $config['focus']; + if (!in_array($this->focus, array('yes', 'no'), TRUE)) { + throw new Exception('Invalid value for \'focus\'-parameter to' . + ' consent:Consent authentication filter: ' . var_export($this->focus, TRUE)); + } + } else { + $this->focus = NULL; + } + + if (array_key_exists('store', $config)) { + $this->store = sspmod_consent_Store::parseStoreConfig($config['store']); + } else { + $this->store = NULL; + } + + } + + + /** + * Process a authentication response. + * + * This function saves the state, and redirects the user to the page where the user + * can authorize the release of the attributes. + * + * @param array $state The state of the response. + */ + public function process(&$state) { + assert('is_array($state)'); + assert('array_key_exists("UserID", $state)'); + assert('array_key_exists("Destination", $state)'); + assert('array_key_exists("entityid", $state["Destination"])'); + assert('array_key_exists("metadata-set", $state["Destination"])'); + + if ($this->store !== NULL) { + $userId = sha1($state['UserID'] . SimpleSAML_Utilities::getSecretSalt());; + $destination = $state['Destination']['metadata-set'] . '/' . $state['Destination']['entityid']; + + $attributeSet = array_keys($state['Attributes']); + sort($attributeSet); + $attributeSet = implode(',', $attributeSet); + $attributeSet = sha1($attributeSet); + + if ($this->store->hasConsent($userId, $destination, $attributeSet)) { + /* Consent already given. */ + return; + } + + $state['consent:store'] = $this->store; + $state['consent:store.userId'] = $userId; + $state['consent:store.destination'] = $destination; + $state['consent:store.attributeSet'] = $attributeSet; + } + + $state['consent:focus'] = $this->focus; + + /* Save state and redirect. */ + $id = SimpleSAML_Auth_State::saveState($state, 'consent:request'); + $url = SimpleSAML_Module::getModuleURL('consent/getconsent.php'); + SimpleSAML_Utilities::redirect($url, array('StateId' => $id)); + } + +} + +?> \ No newline at end of file diff --git a/modules/consent/lib/Consent/Store/Cookie.php b/modules/consent/lib/Consent/Store/Cookie.php new file mode 100644 index 0000000000000000000000000000000000000000..4d96f893031544e602cd4550f9895995a7f8a364 --- /dev/null +++ b/modules/consent/lib/Consent/Store/Cookie.php @@ -0,0 +1,247 @@ +<?php + +/** + * Store consent in cookies. + * + * This class implements a consent store which stores the consent information in + * cookies on the users computer. + * + * Example - Consent module with cookie store: + * <code> + * 'authproc' => array( + * array( + * 'consent:Consent', + * 'store' => 'consent:Cookie', + * ), + * ), + * </code> + * + * + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_consent_Consent_Store_Cookie extends sspmod_consent_Store { + + + /** + * Check for consent. + * + * This function checks whether a given user has authorized the release of the attributes + * identified by $attributeSet from $source to $destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + * @param string $attributeSet A hash which identifies the attributes. + * @return bool TRUE if the user has given consent earlier, FALSE if not (or on error). + */ + public function hasConsent($userId, $destinationId, $attributeSet) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + assert('is_string($attributeSet)'); + + $cookieName = self::getCookieName($userId, $destinationId); + + if (!array_key_exists($cookieName, $_COOKIE)) { + SimpleSAML_Logger::debug('Consent cookie - no cookie with name \'' . $cookieName . '\'.'); + return FALSE; + } + if (!is_string($_COOKIE[$cookieName])) { + SimpleSAML_Logger::warning('Value of consent cookie wasn\'t a string. Was: ' . var_export($_COOKIE[$cookieName], TRUE)); + return FALSE; + } + + $data = $userId . ':' . $attributeSet . ':' . $destinationId; + $data = self::sign($data); + + if ($_COOKIE[$cookieName] !== $data) { + SimpleSAML_Logger::info('Attribute set changed from the last time consent was given.'); + return FALSE; + } + + SimpleSAML_Logger::debug('Consent cookie - found cookie with correct name and value.'); + + return TRUE; + } + + + /** + * Save consent. + * + * Called when the user asks for the consent to be saved. If consent information + * for the given user and destination already exists, it should be overwritten. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + * @param string $attributeSet A hash which identifies the attributes. + */ + public function saveConsent($userId, $destinationId, $attributeSet) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + assert('is_string($attributeSet)'); + + $name = self::getCookieName($userId, $destinationId); + $value = $userId . ':' . $attributeSet . ':' . $destinationId; + $value = self::sign($value); + $this->setConsentCookie($name, $value); + } + + + /** + * Delete consent. + * + * Called when a user revokes consent for a given destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + */ + public function deleteConsent($userId, $destinationId) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + + $name = self::getCookieName($userId, $destinationId); + $this->setConsentCookie($name, NULL); + + } + + + /** + * Retrieve consents. + * + * This function should return a list of consents the user has saved. + * + * @param string $userId The hash identifying the user at an IdP. + * @return array Array of all destination ids the user has given consent for. + */ + public function getConsents($userId) { + assert('is_string($userId)'); + + $ret = array(); + + $cookieNameStart = 'sspmod_consent:'; + $cookieNameStartLen = strlen($cookieNameStart); + foreach ($_COOKIE as $name => $value) { + + if (substr($name, 0, $cookieNameStartLen) !== $cookieNameStart) { + continue; + } + + $value = self::verify($value); + if ($value === FALSE) { + continue; + } + + $tmp = explode(':', $value, 3); + if (count($tmp) !== 3) { + SimpleSAML_Logger::warning('Consent cookie with invalid value: ' . $value); + continue; + } + + if ($userId !== $tmp[0]) { + /* Wrong user. */ + continue; + } + + $destination = $tmp[2]; + + + $ret[] = $destination; + } + + return $ret; + } + + + /** + * Calculate a signature of some data. + * + * This function calculates a signature of the data. + * + * @param string $data The data which should be signed. + * @return string The signed data. + */ + private static function sign($data) { + assert('is_string($data)'); + + $secretSalt = SimpleSAML_Utilities::getSecretSalt(); + + return sha1($secretSalt . $data . $secretSalt) . ':' . $data; + } + + + /** + * Verify signed data. + * + * This function verifies signed data. + * + * @param string $signedData The data which is signed. + * @return string|FALSE The data, or FALSE if the signature is invalid. + */ + private static function verify($signedData) { + assert('is_string($signedData)'); + + $data = explode(':', $signedData, 2); + if (count($data) !== 2) { + SimpleSAML_Logger::warning('Consent cookie: Missing signature.'); + return FALSE; + } + $data = $data[1]; + + $newSignedData = self::sign($data); + if ($newSignedData !== $signedData) { + SimpleSAML_Logger::warning('Consent cookie: Invalid signature.'); + return FALSE; + } + + return $data; + } + + + /** + * Get cookie name. + * + * This function gets the cookie name for the given user & destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + */ + private static function getCookieName($userId, $destinationId) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + + return 'sspmod_consent:' . sha1($userId . ':' . $destinationId); + } + + + /** + * Helper function for setting a cookie. + * + * @param string $name Name of the cookie. + * @param string|NULL $value Value of the cookie. Set this to NULL to delete the cookie. + */ + private function setConsentCookie($name, $value) { + assert('is_string($name)'); + assert('is_string($value)'); + + if ($value === NULL) { + $expire = 1; /* Delete by setting expiry in the past. */ + $value = ''; + } else { + $expire = time() + 90 * 24*60*60; + } + + if (SimpleSAML_Utilities::isHTTPS()) { + /* Enable secure cookie for https-requests. */ + $secure = TRUE; + } else { + $secure = FALSE; + } + + $globalConfig = SimpleSAML_Configuration::getInstance(); + $path = '/' . $globalConfig->getBaseURL(); + + setcookie($name, $value, $expire, $path, NULL, $secure); + } + +} + +?> \ No newline at end of file diff --git a/modules/consent/lib/Consent/Store/Database.php b/modules/consent/lib/Consent/Store/Database.php new file mode 100644 index 0000000000000000000000000000000000000000..337713aa46ee5df9379cfb2e95e13f88c63194ef --- /dev/null +++ b/modules/consent/lib/Consent/Store/Database.php @@ -0,0 +1,378 @@ +<?php + +/** + * Store consent in database. + * + * This class implements a consent store which stores the consent information in + * a database. It is tested, and should work against both MySQL and PostgreSQL. + * + * It has the following options: + * - dsn: The DSN which should be used to connect to the database server. Check the various + * database drivers in http://php.net/manual/en/pdo.drivers.php for a description of + * the various DSN formats. + * - username: The username which should be used when connecting to the database server. + * - password: The password which should be used when connecting to the database server. + * - table: The name of the table. Optional, defaults to 'ssp_consent'. + * + * Example - consent module with MySQL database: + * <code> + * 'authproc' => array( + * array( + * 'consent:Consent', + * 'store' => array( + * 'consent:Database', + * 'dsn' => 'mysql:host=db.example.org;dbname=simplesaml', + * 'username' => 'simplesaml', + * 'password' => 'secretpassword', + * ), + * ), + * ), + * </code> + * + * Example - consent module with PostgreSQL database: + * <code> + * 'authproc' => array( + * array( + * 'consent:Consent', + * 'store' => array( + * 'consent:Database', + * 'dsn' => 'pgsql:host=db.example.org;port=5432;dbname=simplesaml', + * 'username' => 'simplesaml', + * 'password' => 'secretpassword', + * ), + * ), + * ), + * </code> + * + * + * Table declaration: + * CREATE TABLE ssp_consent ( + * consentTime TIMESTAMP NOT NULL, + * lastUse TIMESTAMP NOT NULL, + * userId VARCHAR(80) NOT NULL, + * destinationId VARCHAR(255) NOT NULL, + * attributeSet VARCHAR(80) NOT NULL, + * UNIQUE (userId, destinationId) + * ); + * + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store { + + + /** + * DSN for the database. + */ + private $dsn; + + + /** + * Username for the database. + */ + private $username; + + + /** + * Password for the database; + */ + private $password; + + + /** + * Table with consent. + */ + private $table; + + + /** + * Database handle. + * + * This variable can't be serialized. + */ + private $db; + + + /** + * Parse configuration. + * + * This constructor parses the configuration. + * + * @param array $config Configuration for database consent store. + */ + public function __construct($config) { + parent::__construct($config); + + foreach (array('dsn', 'username', 'password') as $id) { + if (!array_key_exists($id, $config)) { + throw new Exception('consent:Database - Missing required option \'' . $id . '\'.'); + } + if (!is_string($config[$id])) { + throw new Exception('consent:Database - \'' . $id . '\' is supposed to be a string.'); + } + } + + $this->dsn = $config['dsn']; + $this->username = $config['username']; + $this->password = $config['password']; + + if (array_key_exists('table', $config)) { + if (!is_string($config['table'])) { + throw new Exception('consent:Database - \'table\' is supposed to be a string.'); + } + $this->table = $config['table']; + } else { + $this->table = 'ssp_consent'; + } + } + + + /** + * Called before serialization. + * + * @return array The variables which should be serialized. + */ + public function __sleep() { + + return array( + 'dsn', + 'username', + 'password', + 'table', + ); + } + + + /** + * Check for consent. + * + * This function checks whether a given user has authorized the release of the attributes + * identified by $attributeSet from $source to $destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + * @param string $attributeSet A hash which identifies the attributes. + * @return bool TRUE if the user has given consent earlier, FALSE if not (or on error). + */ + public function hasConsent($userId, $destinationId, $attributeSet) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + assert('is_string($attributeSet)'); + + $st = $this->execute('UPDATE ' . $this->table . ' SET lastUse = NOW() WHERE userId = ? AND destinationId = ? AND attributeSet = ?', + array($userId, $destinationId, $attributeSet)); + if ($st === FALSE) { + return FALSE; + } + + $rowCount = $st->rowCount(); + if ($rowCount === 0) { + SimpleSAML_Logger::debug('consent:Database - No consent found.'); + return FALSE; + } else { + SimpleSAML_Logger::debug('consent:Database - Consent found.'); + return TRUE; + } + + } + + + /** + * Save consent. + * + * Called when the user asks for the consent to be saved. If consent information + * for the given user and destination already exists, it should be overwritten. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + * @param string $attributeSet A hash which identifies the attributes. + */ + public function saveConsent($userId, $destinationId, $attributeSet) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + assert('is_string($attributeSet)'); + + /* Check for old consent (with different attribute set). */ + $st = $this->execute('UPDATE ' . $this->table . ' SET consentTime = NOW(), lastUse = NOW(), attributeSet = ? WHERE userId = ? AND destinationId = ?', + array($attributeSet, $userId, $destinationId)); + if ($st === FALSE) { + return; + } + if ($st->rowCount() > 0) { + /* We had already stored consent for the given destination in the database. */ + SimpleSAML_Logger::debug('consent:Database - Updated old consent.'); + return; + } + + /* Add new consent. We don't check for error since there is nothing we can do if one occurs. */ + $st = $this->execute('INSERT INTO ' . $this->table . ' (consentTime, lastUse, userId, destinationId, attributeSet) VALUES(NOW(),NOW(),?,?,?)', + array($userId, $destinationId, $attributeSet)); + if ($st !== FALSE) { + SimpleSAML_Logger::debug('consent:Database - Saved new consent.'); + } + } + + + /** + * Delete consent. + * + * Called when a user revokes consent for a given destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifies the destination. + */ + public function deleteConsent($userId, $destinationId) { + assert('is_string($userId)'); + assert('is_string($destinationId)'); + + $st = $this->execute('DELETE FROM ' . $this->table . ' WHERE userId = ? and destinationId = ?', + array($userId, $destinationId)); + if ($st === FALSE) { + return; + } + + if ($st->rowCount() > 0) { + SimpleSAML_Logger::debug('consent:Database - Deleted consent.'); + } else { + SimpleSAML_Logger::warning('consent:Database - Attempted to delete nonexistent consent'); + } + } + + + /** + * Retrieve consents. + * + * This function should return a list of consents the user has saved. + * + * @param string $userId The hash identifying the user at an IdP. + * @return array Array of all destination ids the user has given consent for. + */ + public function getConsents($userId) { + assert('is_string($userId)'); + + $ret = array(); + + $st = $this->execute('SELECT destinationId FROM ' . $this->table . ' WHERE userId = ?', + array($userId)); + if ($st === FALSE) { + return array(); + } + + while ($row = $st->fetch(PDO::FETCH_NUM)) { + $ret[] = $row[0]; + } + + return $ret; + } + + + /** + * Prepare and execute statement. + * + * This function prepares and executes a statement. On error, FALSE will be returned. + * + * @param string $statement The statement which should be executed. + * @param array $parameters Parameters for the statement. + * @return PDOStatement|FALSE The statement, or FALSE if execution failed. + */ + private function execute($statement, $parameters) { + assert('is_string($statement)'); + assert('is_array($parameters)'); + + $db = $this->getDB(); + if ($db === FALSE) { + return FALSE; + } + + $st = $db->prepare($statement); + if ($st === FALSE) { + if ($st === FALSE) { + SimpleSAML_Logger::error('consent:Database - Error preparing statement \'' . + $statement . '\': ' . self::formatError($db->errorInfo())); + return FALSE; + } + } + + if ($st->execute($parameters) !== TRUE) { + SimpleSAML_Logger::error('consent:Database - Error executing statement \'' . + $statement . '\': ' . self::formatError($st->errorInfo())); + return FALSE; + } + + return $st; + } + + + /** + * Create consent table. + * + * This function creates the table with consent data. + * + * @return TRUE if successful, FALSE if not. + */ + private function createTable() { + + $db = $this->getDB(); + if ($db === FALSE) { + return FALSE; + } + + $res = $this->db->exec( + 'CREATE TABLE ' . $this->table . ' (' . + 'consentTime TIMESTAMP NOT NULL,' . + 'lastUse TIMESTAMP NOT NULL,' . + 'userId VARCHAR(80) NOT NULL,' . + 'destinationId VARCHAR(255) NOT NULL,' . + 'attributeSet VARCHAR(80) NOT NULL,' . + 'UNIQUE (userId, destinationId)' . + ')'); + if ($res === FALSE) { + SimpleSAML_Logger::error('consent:Database - Failed to create table \'' . $this->table . '\'.'); + return FALSE; + } + + return TRUE; + } + + + /** + * Get database handle. + * + * @return PDO|FALSE Database handle, or FALSE if we fail to connect. + */ + private function getDB() { + + if ($this->db !== NULL) { + return $this->db; + } + + try { + $this->db = new PDO($this->dsn, $this->username, $this->password); + } catch (PDOException $e) { + SimpleSAML_Logger::error('consent:Database - Failed to connect to \'' . + $this->dsn . '\': '. $e->getMessage()); + $this->db = FALSE; + } + + return $this->db; + } + + + /** + * Format PDO error. + * + * This function formats a PDO error, as returned from errorInfo. + * + * @param array $error The error information. + * @return string Error text. + */ + private static function formatError($error) { + assert('is_array($error)'); + assert('count($error) >= 3'); + + return $error[0] . ' - ' . $error[2] . ' (' . $error[1] . ')'; + } + +} + +?> \ No newline at end of file diff --git a/modules/consent/lib/Store.php b/modules/consent/lib/Store.php new file mode 100644 index 0000000000000000000000000000000000000000..c4ffe233f330b70373766c9b2758314696c49be3 --- /dev/null +++ b/modules/consent/lib/Store.php @@ -0,0 +1,107 @@ +<?php + +/** + * Base class for consent storage handlers. + * + * @package simpleSAMLphp + * @version $Id$ + */ +abstract class sspmod_consent_Store { + + + /** + * Constructor for the base class. + * + * This constructor should always be called first in any class which implements + * this class. + * + * @param array &$config The configuration for this storage handler.. + */ + protected function __construct(&$config) { + assert('is_array($config)'); + } + + + /** + * Check for consent. + * + * This function checks whether a given user has authorized the release of the attributes + * identified by $attributeSet from $source to $destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifyes the destination. + * @param string $attributeSet A hash which identifies the attributes. + * @return bool TRUE if the user has given consent earlier, FALSE if not (or on error). + */ + abstract public function hasConsent($userId, $destinationId, $attributeSet); + + + /** + * Save consent. + * + * Called when the user asks for the consent to be saved. If consent information + * for the given user and destination already exists, it should be overwritten. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifyes the destination. + * @param string $attributeSet A hash which identifies the attributes. + */ + abstract public function saveConsent($userId, $destinationId, $attributeSet); + + + /** + * Delete consent. + * + * Called when a user revokes consent for a given destination. + * + * @param string $userId The hash identifying the user at an IdP. + * @param string $destinationId A string which identifyes the destination. + */ + abstract public function deleteConsent($userId, $destinationId); + + + /** + * Retrieve consents. + * + * This function should return a list of consents the user has saved. + * + * @param string $userId The hash identifying the user at an IdP. + * @return array Array of all destination ids the user has given consent for. + */ + abstract public function getConsents($userId); + + + /** + * Parse consent storage configuration. + * + * This function parses the configuration for a consent storage method. An exception + * will be thrown if configuration parsing fails. + * + * @param mixed $config The configuration. + * @return sspmod_consent_Store An object which implements of the sspmod_consent_Store class. + */ + public static function parseStoreConfig($config) { + + if (is_string($config)) { + $config = array($config); + } + + if (!is_array($config)) { + throw new Exception('Invalid configuration for consent store option: ' . + var_export($config, TRUE)); + } + + if (!array_key_exists(0, $config)) { + throw new Exception('Consent store without name given.'); + } + + $className = SimpleSAML_Module::resolveClass($config[0], 'Consent_Store', + 'sspmod_consent_Store'); + + unset($config[0]); + return new $className($config); + } + +} + +?> \ No newline at end of file diff --git a/modules/consent/templates/default/consentform.php b/modules/consent/templates/default/consentform.php new file mode 100644 index 0000000000000000000000000000000000000000..6931dfd4353f804d79530f51c045be2f79174399 --- /dev/null +++ b/modules/consent/templates/default/consentform.php @@ -0,0 +1,117 @@ +<?php + +/** + * Template form for giving consent. + * + * Parameters: + * - 'srcMetadata': Metadata/configuration for the source. + * - 'dstMetadata': Metadata/configuration for the destination. + * - 'yesTarget': Target URL for the yes-button. This URL will receive a POST request. + * - 'yesData': Parameters which should be included in the yes-request. + * - 'noTarget': Target URL for the no-button. This URL will receive a GET request. + * - 'noData': Parameters which should be included in the no-request. + * - 'attributes': The attributes which are about to be released. + * - 'sppp': URL to the privacy policy of the destination, or FALSE. + * + * @package simpleSAMLphp + * @version $Id$ + */ +assert('is_array($this->data["srcMetadata"])'); +assert('is_array($this->data["dstMetadata"])'); +assert('is_string($this->data["yesTarget"])'); +assert('is_array($this->data["yesData"])'); +assert('is_string($this->data["noTarget"])'); +assert('is_array($this->data["noData"])'); +assert('is_array($this->data["attributes"])'); +assert('$this->data["sppp"] === FALSE || is_string($this->data["spp"])'); + + +/* Parse parameters. */ + +if (array_key_exists('name', $this->data['srcMetadata'])) { + $srcName = $this->data['srcMetadata']['name']; + if (is_array($srcName)) { + $srcName = $this->t($srcName); + } +} else { + $srcName = $this->data['srcMetadata']['entityid']; +} + +if (array_key_exists('name', $this->data['dstMetadata'])) { + $dstName = $this->data['dstMetadata']['name']; + if (is_array($dstName)) { + $dstName = $this->t($dstName); + } +} else { + $dstName = $this->data['dstMetadata']['entityid']; +} + +$attributes = $this->data['attributes']; + + +$this->data['header'] = 'Consent'; /* TODO: translation */ +$this->includeAtTemplateBase('includes/header.php'); +?> +<div id="content"> + +<p> +<?php echo $this->t('{consent:consent_notice}'); ?> <strong><?php echo htmlspecialchars($dstName); ?></strong>. +<?php echo $this->t('{consent:consent_accept}', array('IDPNAME' => htmlspecialchars($srcName))) ?> +</p> + +<?php +if ($this->data['sppp'] !== FALSE) { + echo "<p>" . htmlspecialchars($this->t('consent_privacypolicy')) . " "; + echo "<a target='_new_window' href='" . htmlspecialchars($this->data['sppp']) . "'>" . htmlspecialchars($dstName) . "</a>"; + echo "</p>"; +} +?> + +<form style="display: inline" action="<?php echo htmlspecialchars($this->data['yesTarget']); ?>"> + <input type="submit" name="yes" id="yesbutton" value="<?php echo $this->t('{consent:yes}') ?>" /> +<?php +foreach ($this->data['yesData'] as $name => $value) { + echo('<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />'); +} +if ($this->data['usestorage']) { + echo('<input type="checkbox" name="saveconsent" value="1" /> ' . $this->t('{consent:remember}')); +} +?> +</form> + +<form style="display: inline; margin-left: .5em;" action="<?php echo htmlspecialchars($this->data['noTarget']); ?>" method="GET"> +<?php +foreach ($this->data['noData'] as $name => $value) { + echo('<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />'); +} +?> + <input type="submit" id="nobutton" value="<?php echo htmlspecialchars($this->t('{consent:no}')) ?>" /> +</form> + +<p> +<table style="font-size: x-small"> +<?php +foreach ($attributes as $name => $value) { + $nameTag = '{attributes:attribute_' . strtolower($name) . '}'; + if ($this->getTag($nameTag) !== NULL) { + $name = $this->t($nameTag); + } + + if (sizeof($value) > 1) { + echo '<tr><td>' . htmlspecialchars($name) . '</td><td><ul>'; + foreach ($value AS $v) { + echo '<li>' . htmlspecialchars($v) . '</li>'; + } + echo '</ul></td></tr>'; + } else { + echo '<tr><td>' . htmlspecialchars($name) . '</td><td>' . htmlspecialchars($value[0]) . '</td></tr>'; + } +} + +?> +</table> +</p> + +<?php +$this->includeAtTemplateBase('includes/footer.php'); +?> \ No newline at end of file diff --git a/modules/consent/templates/default/noconsent.php b/modules/consent/templates/default/noconsent.php new file mode 100644 index 0000000000000000000000000000000000000000..b865cbef1525ca1195fdbe69a33079bd0cf1e01b --- /dev/null +++ b/modules/consent/templates/default/noconsent.php @@ -0,0 +1,21 @@ +<?php + $this->data['header'] = $this->t('{consent:noconsent_title}');; + $this->data['icon'] = 'bomb_l.png'; + $this->includeAtTemplateBase('includes/header.php'); +?> + + +<div id="content"> + + <h2><?php echo($this->data['header']); ?></h2> + <p><?php echo($this->t('{consent:noconsent_text}')); ?></p> + +<?php + if($this->data['resumeFrom']) { + echo('<p><a href="' . htmlspecialchars($this->data['resumeFrom']) . '">'); + echo($this->t('{consent:noconsent_return}')); + echo('</a></p>'); + } +?> + +<?php $this->includeAtTemplateBase('includes/footer.php'); ?> \ No newline at end of file diff --git a/modules/consent/www/getconsent.php b/modules/consent/www/getconsent.php new file mode 100644 index 0000000000000000000000000000000000000000..284f904fcfe192d7413f2e4b8c89c5add2df2446 --- /dev/null +++ b/modules/consent/www/getconsent.php @@ -0,0 +1,82 @@ +<?php + +/** + * This script displays a page to the user, which requests that the user + * authorizes the release of attributes. + * + * @package simpleSAMLphp + * @version $Id$ + */ + +if (!array_key_exists('StateId', $_REQUEST)) { + throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); +} + +$id = $_REQUEST['StateId']; +$state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); + + +if (array_key_exists('yes', $_REQUEST)) { + /* The user has pressed the yes-button. */ + + if (array_key_exists('consent:store', $state) && array_key_exists('saveconsent', $_REQUEST) + && $_REQUEST['saveconsent'] === '1') { + + /* Save consent. */ + $store = $state['consent:store']; + $userId = $state['consent:store.userId']; + $destination = $state['consent:store.destination']; + $attributeSet = $state['consent:store.attributeSet']; + $store->saveConsent($userId, $destination, $attributeSet); + } + + SimpleSAML_Auth_ProcessingChain::resumeProcessing($state); +} + + +/* Show consent form. */ + +$globalConfig = SimpleSAML_Configuration::getInstance(); +$t = new SimpleSAML_XHTML_Template($globalConfig, 'consent:consentform.php'); +$t->data['srcMetadata'] = $state['Source']; +$t->data['dstMetadata'] = $state['Destination']; +$t->data['yesTarget'] = SimpleSAML_Module::getModuleURL('consent/getconsent.php'); +$t->data['yesData'] = array('StateId' => $id); +$t->data['noTarget'] = SimpleSAML_Module::getModuleURL('consent/noconsent.php'); +$t->data['noData'] = array('StateId' => $id); +$t->data['attributes'] = $state['Attributes']; + +if (array_key_exists('privacypolicy', $state['Destination'])) { + $privacypolicy = $state['Destination']['privacypolicy']; +} elseif (array_key_exists('privacypolicy', $state['Source'])) { + $privacypolicy = $state['Source']['privacypolicy']; +} else { + $privacypolicy = FALSE; +} +if($privacypolicy !== FALSE) { + $privacypolicy = str_replace('%SPENTITYID%', urlencode($spentityid), + $privacypolicy); +} +$t->data['sppp'] = $privacypolicy; + +switch ($state['consent:focus']) { +case NULL: + break; +case 'yes': + $t->data['autofocus'] = 'yesbutton'; + break; +case 'no': + $t->data['autofocus'] = 'nobutton'; + break; +} + +if (array_key_exists('consent:store', $state)) { + $t->data['usestorage'] = TRUE; +} else { + $t->data['usestorage'] = FALSE; +} + +$t->show(); +exit; + +?> \ No newline at end of file diff --git a/modules/consent/www/noconsent.php b/modules/consent/www/noconsent.php new file mode 100644 index 0000000000000000000000000000000000000000..c84e5060693e5519ed2b7cd9f4ec8c562e84fa6e --- /dev/null +++ b/modules/consent/www/noconsent.php @@ -0,0 +1,27 @@ +<?php + +/** + * This is the page the user lands on when choosing "no" in the consent form. + * + * @package simpleSAMLphp + * @version $Id$ + */ + +if (!array_key_exists('StateId', $_REQUEST)) { + throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); +} + +$id = $_REQUEST['StateId']; +$state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); + +$resumeFrom = SimpleSAML_Module::getModuleURL('consent/getconsent.php'); +$resumeFrom = SimpleSAML_Utilities::addURLParameter($resumeFrom, array('StateId' => $id)); + +$globalConfig = SimpleSAML_Configuration::getInstance(); + +$t = new SimpleSAML_XHTML_Template($globalConfig, 'consent:noconsent.php'); +$t->data['dstMetadata'] = $state['Destination']; +$t->data['resumeFrom'] = $resumeFrom; +$t->show(); + +?> \ No newline at end of file