From 3b3dfadb7297c3cd019acd267f64d3708cc510ca Mon Sep 17 00:00:00 2001
From: Mads Freek Petersen <freek@wayf.dk>
Date: Sat, 7 Feb 2009 10:57:14 +0000
Subject: [PATCH] Added the wayf.dk consentAdmin functionality as a module. We
 have used this in wayf.dk for giving the users a way to see -, give - and
 withdraw consent for all service providers in one place. The work of adapting
 the code into a module mostly done by Jacob Christiansen - (jach@wayf.dk).

It depends on the Consent module for computing hashes and
accessing the consent database. It also depends on the new
isPassive functionality in the auth processing filters ie. a
filter must throw a SimpleSAML_Error_NoPassive exception if it
demands user interaction to be compatible with this module.

In addition Jacob has fixet a bug in the Consent module so that
i computes the correct targetedId when running on a bridge/proxy.

The database access in the Consent module has been updated with
some extra funtionality for consent administration use.



git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1247 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/consent/lib/Auth/Process/Consent.php  |  15 +-
 .../consent/lib/Consent/Store/Database.php    |   7 +-
 modules/consentAdmin/.project                 |  17 ++
 modules/consentAdmin/default-enable           |   0
 .../dictionaries/consentadmin.php             | 177 ++++++++++++
 modules/consentAdmin/docs/ConsentDocs.txt.old | 128 +++++++++
 modules/consentAdmin/hooks/hook_frontpage.php |  16 ++
 .../consentAdmin/templates/consentadmin.php   | 132 +++++++++
 .../templates/consentadminajax.php            |   3 +
 modules/consentAdmin/www/consentAdmin.php     | 261 ++++++++++++++++++
 .../www/includes/consentSimpleAjax.js         |  70 +++++
 templates/admin-metadatalist.php              |   7 +-
 12 files changed, 826 insertions(+), 7 deletions(-)
 create mode 100644 modules/consentAdmin/.project
 create mode 100644 modules/consentAdmin/default-enable
 create mode 100644 modules/consentAdmin/dictionaries/consentadmin.php
 create mode 100644 modules/consentAdmin/docs/ConsentDocs.txt.old
 create mode 100644 modules/consentAdmin/hooks/hook_frontpage.php
 create mode 100755 modules/consentAdmin/templates/consentadmin.php
 create mode 100644 modules/consentAdmin/templates/consentadminajax.php
 create mode 100644 modules/consentAdmin/www/consentAdmin.php
 create mode 100755 modules/consentAdmin/www/includes/consentSimpleAjax.js

diff --git a/modules/consent/lib/Auth/Process/Consent.php b/modules/consent/lib/Auth/Process/Consent.php
index 26a35327b..08df9f13d 100644
--- a/modules/consent/lib/Auth/Process/Consent.php
+++ b/modules/consent/lib/Auth/Process/Consent.php
@@ -133,6 +133,18 @@ class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilt
 		assert('array_key_exists("entityid", $state["Source"])');
 		assert('array_key_exists("metadata-set", $state["Source"])');
 
+		$session = SimpleSAML_Session::getInstance(); 
+		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+		/* If the consent module is active on a bridge $session->getIdP() will contain
+		 * an entry id for the remote IdP. If $session->getIdP() is NULL, then the
+		 * consent module is active on a local IdP and nothing needs to be done.
+		 */
+		if($session->getIdP() != null) {
+			$idpmeta = $metadata->getMetaData($session->getIdP(), 'saml20-idp-remote');
+			$state['Source'] = $idpmeta;
+		}
+		
 		if ($this->store !== NULL) {
 
 			$source = $state['Source']['metadata-set'] . '|' . $state['Source']['entityid'];
@@ -141,8 +153,7 @@ class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilt
 			SimpleSAML_Logger::debug('Consent - userid : ' . $state['UserID']);
 			SimpleSAML_Logger::debug('Consent - source : ' . $source);
 			SimpleSAML_Logger::debug('Consent - destination : ' . $destination);
-			
-
+	
 			$userId = self::getHashedUserID($state['UserID'], $source);
 			$targetedId = self::getTargetedID($state['UserID'], $source, $destination);
 			$attributeSet = self::getAttributeHash($state['Attributes'], $this->includeValues);
diff --git a/modules/consent/lib/Consent/Store/Database.php b/modules/consent/lib/Consent/Store/Database.php
index e9ea1edaf..7138a9971 100644
--- a/modules/consent/lib/Consent/Store/Database.php
+++ b/modules/consent/lib/Consent/Store/Database.php
@@ -212,6 +212,7 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store {
 		if ($st !== FALSE) {
 			SimpleSAML_Logger::debug('consent:Database - Saved new consent.');
 		}
+		return TRUE;
 	}
 
 
@@ -235,6 +236,7 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store {
 
 		if ($st->rowCount() > 0) {
 			SimpleSAML_Logger::debug('consent:Database - Deleted consent.');
+			return $st->rowCount();
 		} else {
 			SimpleSAML_Logger::warning('consent:Database - Attempted to delete nonexistent consent');
 		}
@@ -254,14 +256,14 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store {
 
 		$ret = array();
 
-		$st = $this->execute('SELECT service_id FROM ' . $this->table . ' WHERE hashed_user_id = ?',
+		$st = $this->execute('SELECT service_id, attribute FROM ' . $this->table . ' WHERE hashed_user_id = ?',
 			array($userId));
 		if ($st === FALSE) {
 			return array();
 		}
 
 		while ($row = $st->fetch(PDO::FETCH_NUM)) {
-			$ret[] = $row[0];
+			$ret[] = $row;
 		}
 
 		return $ret;
@@ -343,7 +345,6 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store {
 	 * @return PDO|FALSE  Database handle, or FALSE if we fail to connect.
 	 */
 	private function getDB() {
-
 		if ($this->db !== NULL) {
 			return $this->db;
 		}
diff --git a/modules/consentAdmin/.project b/modules/consentAdmin/.project
new file mode 100644
index 000000000..1c44219bd
--- /dev/null
+++ b/modules/consentAdmin/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>Consent admin - WAYF</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>net.sourceforge.phpeclipse.parserbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>net.sourceforge.phpeclipse.phpnature</nature>
+	</natures>
+</projectDescription>
diff --git a/modules/consentAdmin/default-enable b/modules/consentAdmin/default-enable
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/consentAdmin/dictionaries/consentadmin.php b/modules/consentAdmin/dictionaries/consentadmin.php
new file mode 100644
index 000000000..a11f8ff20
--- /dev/null
+++ b/modules/consentAdmin/dictionaries/consentadmin.php
@@ -0,0 +1,177 @@
+<?php
+
+// consentadmin dictionary
+
+/*
+	'' => array(
+		'en' => '',
+		'no' => '',
+		'nn' => '',
+        'da' => '',
+		'es' => '',
+		'fr' => '',
+		'de' => '',
+		'nl' => '',
+		'lu' => '',
+		'sl' => '',
+	),
+
+
+* 
+* */
+
+
+$lang = array(
+
+
+// WAYF: Additional attributes START
+
+	'attribute_org' => array(
+		'en' => 'Organisation',
+		'da' => 'Organisation',
+	),
+
+	'added' => array(
+		'en' => 'Consent Added',
+		'da' => 'Samtykke givet',
+	),
+
+	'removed' => array(
+		'en' => 'Consent Removed',
+		'da' => 'Samtykke slettet',
+	),
+
+	'updated' => array(
+		'en' => 'Consent Updated',
+		'da' => 'Samtykke Opdateret!!!',
+	),
+
+	'unknown' => array(
+		'en' => 'Unknown ...',
+		'da' => 'Ukendt ...',
+	),
+
+	'attribute_id' => array(
+		'en' => 'Identity',
+		'da' => 'Identitet',
+	),
+
+	'attribute_injected' => array(
+		'en' => 'Injected attribut',
+		'da' => 'Injiceret attribut',
+	),
+	
+// WAYF: Additional attributes END
+	
+
+// Text
+
+	'show' => array(
+		'en' => 'Show',
+        'da' => 'Vis',
+	),
+
+	'hide' => array(
+		'en' => 'Hide',
+        'da' => 'Skjul',
+	),
+	
+	'attributes_text' => array(
+		'en' => 'attributes',
+        'da' => 'attributter',
+	),
+	
+
+	'consentadmin_header' => array(
+		'en' => 'Consent Administration',
+        'da' => 'Administrer dine samtykker',
+	),
+
+	'consentadmin_description1' => array(
+		'en' => 'Here you can view and edit your consent for the Service Providers.',
+
+        'da' => '
+ WAYF videregiver kun oplysninger til eksterne tjenester, hvis du giver dit samtykke til det. Hvilke oplysninger det drejer sig om, varierer alt efter hvad tjenesteudbyderen har behov for. Det kan for eksempel være:
+<ul>
+<li>	Dit navn
+<li>	Din e-mail-adresse
+<li>	Din institution
+<li>	Etc.
+</ul>
+
+Hvis du sætter et flueben ud for <b>Husk dette samtykke</b>, vil du ikke blive spurgt, næste gang du besøger tjenesteudbyderen. 
+SĂĄ husker WAYF, at du allerede har givet samtykke til at videregive oplysninger til tjenesteudbyderen. 
+<p>Nedenfor er opført de tjenester, som du for øjeblikket har givet løbende samtykke til:</a>
+', //da
+		),
+
+		'consentadmin_description2' => array(
+		'en' => '
+<h3>How to delete your consent</h3>
+Uncheck the box corresponding to the service provider
+
+<h3>Links</h3>
+<ul>
+<li><a href="https://www.wayf.dk">Start</a> </li>
+
+<li><a href="https://www.wayf.dk/FAQ">FAQ</a> </li>
+</ul>
+', // en
+		        'da' => '
+<h3>SĂĄdan sletter du et samtykke</h3>
+Fjern fluebenet ud for tjenesten, samtykket tilhører.
+<h3>Hvilke data gemmer WAYF om dig?</h3>
+<ul>
+<li>	NĂĄr du giver dit samtykke, henter WAYF dine oplysninger fra din institution og sender de relevante videre til tjenesteudbyderen
+<li>	Ingen af oplysningerne gemmes af WAYF
+<li>	Hvis du har bedt WAYF huske dit samtykke, gemmes personhenførbare data heller ikke hos WAYF. Oplysningen om, at du har givet dit samtykke, gemmes på en ikke-personhenførbar måde
+</ul>
+
+<h3>Hvilke rettigheder har du?</h3>
+Du har ret til at trække et samtykke tilbage.
+<h3>Hvor længe gemmes dine samtykker?</h3>
+Et samtykke slettes tre ĂĄr efter, at du sidst har benyttet det.
+<h3>Hvordan beskyttes mine oplysninger?</h3>
+WAYF foretager behandlinger af personoplysninger i henhold til persondataloven (lov nr. 429 af 31. maj 2000 med senere ændringer). Du kan læse nærmere om registreredes rettigheder i persondatalovens afsnit III.
+<a href="http://www.datatilsynet.dk/lovgivning/persondataloven/">Persondataloven</a>
+
+<h3>Links</h3>
+<ul>
+<li><a href="https://www.wayf.dk">Start</a> </li>
+
+<li><a href="https://www.wayf.dk/FAQ">FAQ</a> </li>
+</ul>
+', // da
+	),	
+		
+		
+	'login' => array(
+		'en' => 'login',
+        'da' => 'login',
+	),
+		   		
+	'service_providers_for' => array(
+		'en' => 'Service Providers for',
+        'da' => 'Service Providers for',
+		),
+		
+  
+  
+  'service_provider_header' => array(
+		'en' => 'Service Provider',
+        'da' => 'Service Provider',
+		),
+		
+	'status_header' => array(
+		'en' => 'Consent status',
+        'da' => 'Samtykke status',		
+		),
+		
+	'show_hide_attributes' => array(
+		'en' => 'show/hide attributes',
+        'da' => 'vis/skjul attributter',		
+		),
+		
+);
+
+
diff --git a/modules/consentAdmin/docs/ConsentDocs.txt.old b/modules/consentAdmin/docs/ConsentDocs.txt.old
new file mode 100644
index 000000000..58254a98b
--- /dev/null
+++ b/modules/consentAdmin/docs/ConsentDocs.txt.old
@@ -0,0 +1,128 @@
+Using the Consent and ConsentAdmin modules
+
+When the Consent module is active, the end user must give his consent when attributes are sent to an SP.
+The Consent module can be configured to save the given consents to a database. 
+The user is then given the option to store the consent for later. Then the next time the attributes will be sent right away.
+
+When the Consent module is using persistent storage, you can activate the optional ConsentAdmin module.
+With this, the user can view and remove his consents for various SP's. 
+
+Installing the Consent module
+
+The Consent module is part of the simplesamlphp standard sources.
+
+It consists of the following files
+
+dictionaries/consent.php
+templates/default/consent.php
+lib/SimpleSAML/Consent/Consent.php
+lib/SimpleSAML/Consent/ConsentStorage.php
+
+Create the Consent database.
+
+Activate Consent module on IdP.
+
+Edit metadata/saml20-idp-hosted.php and set
+	requireconsent
+to true
+
+Configuring persistent storage of the Consent module.
+
+Create a database with the Consent schema and necessary user rights.
+For now, the examples assume you are using the MySQL database.
+Consult the database documentation to see how you grant permission to the database user which the Consent module must use.
+  
+The database schema is shown in the lib/SimpleSAML/Consent/ConsentStorage.php file:
+
+/**
+ * The Consent Storage class is used for storing Attribute Release consents.
+ *
+ * CREATE TABLE consent ( 
+ *	hashed_user_id varchar(128) NOT NULL, 
+ *	service_id varchar(128) NOT NULL, 
+ *	attribute varchar(128) NOT NULL, 
+ *	consent_date datetime NOT NULL, 
+ *	usage_date datetime NOT NULL, 
+ *	PRIMARY KEY USING BTREE (hashed_user_id, service_id) 
+ * );
+ *
+
+Edit the Consent section of config/config.php and set the database and user information.
+Remember to activate persistent Consent storage by setting the
+	consent_usestorage
+attribute to true
+
+/*
+	 * Configuration of Consent storage used for attribute consent.
+	 * connect, user and passwd is used with PDO (in example Mysql)
+	 */
+	'consent_usestorage' => true,
+	'consent_userid' => 'eduPersonPrincipalName',
+	'consent_salt' => 'sdkfjhsidu87werwe8r79w8e7r',
+	'consent_pdo_connect' => 'mysql:host=sql.example.org;dbname=simplesamlconsent',
+	'consent_pdo_user' => 'simplesamluser',
+	'consent_pdo_passwd' => 'xxxx',
+
+Installing the ConsentAdmin module
+The ConsentAdmin module adds a user interface to SimpleSaml which allows the end user to edit his/her consents for all the Service Providers.
+ 
+The ConsentAdmin module consists of the following files in the SimpleSaml directory structure:
+ 
+dictionaries/consentadmin.php
+docs/ConsentDocs.txt
+templates/default/consentadmin.php
+www/consent/ConsentAdminLib.php
+www/consent/consentAdmin.php
+www/consent/ConsentLib.php
+www/consent/consentSubmit.php
+www/consent/includes/
+www/consent/includes/consentSimpleAjax.js
+
+Dictionaries
+
+As you can see in the Consent file listing, the consent module uses two of its own dictionaries for translations, consent.php and consentadmin.php.
+The dictionaries are work-in-progress!
+Additionally, it uses the attributes.php dictionary for showing localised attribute names.	
+
+From the ConsentAdmin source files, you can copy these manually to the right places. 
+If you received the ConsentAdmin as a .tgz archive you can untar them directly into the SimpleSaml source files with these commands
+
+	$ cd <your simplesaml source root>
+	$ tar -wkzxvf <ConsentAdmin.tgz>
+  
+Configuring the Idp
+The ConsentAdmin module uses the IdP's list of remote SP's (configured in metadata/saml20-sp-remote.php). It uses the attributes 'name' and 'description'.
+Both these attributes must specify an array for language translations.
+Example:
+
+		saml2sp.example.org' => array(
+ 			'AssertionConsumerService' => 'https://saml2sp.example.org/simplesaml/saml2/sp/AssertionConsumerService.php', 
+ 			'SingleLogoutService'      => 'https://saml2sp.example.org/simplesaml/saml2/sp/SingleLogoutService.php',
+ 
+	        'name' => array(
+				'en' => 'Saml2 SP english',
+				'no' => 'Saml2 SP bokmĂĄl',
+		  		'nn' => 'Saml2 SP nynorsk',
+				'da' => 'Saml2 SP dansk',
+		  		'es' => 'Saml2 SP español',
+    	  		'fr' => 'Saml2 SP français',
+		  		'de' => 'Saml2 SP deutsch',
+		  		'nl' => 'Saml2 SP dutch',
+		  		'lu' => 'Saml2 SP Luxembourgish',
+		  		'sl' => 'Saml2 SP Slovenščina',
+         	),
+	        'description' => array(
+				'en' => 'Saml2 SP description english',
+				'no' => 'Saml2 SP description bokmĂĄl',
+		  		'nn' => 'Saml2 SP description nynorsk',
+				'da' => 'Saml2 SP description dansk',
+		  		'es' => 'Saml2 SP description español',
+    	  		'fr' => 'Saml2 SP description français',
+		  		'de' => 'Saml2 SP description deutsch',
+		  		'nl' => 'Saml2 SP description dutch',
+		  		'lu' => 'Saml2 SP description Luxembourgish',
+		  		'sl' => 'Saml2 SP description Slovenščina',
+         	),
+ 		),
+
+ 		
\ No newline at end of file
diff --git a/modules/consentAdmin/hooks/hook_frontpage.php b/modules/consentAdmin/hooks/hook_frontpage.php
new file mode 100644
index 000000000..7bf7138f3
--- /dev/null
+++ b/modules/consentAdmin/hooks/hook_frontpage.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Hook to add the consentAdmin module to the frontpage.
+ *
+ * @param array &$links  The links on the frontpage, split into sections.
+ */
+function consentAdmin_hook_frontpage(&$links) {
+	assert('is_array($links)');
+	assert('array_key_exists("links", $links)');
+
+	$links['links'][] = array(
+		'href' => SimpleSAML_Module::getModuleURL('consentAdmin/consentAdmin.php'),
+		'text' => '{consentAdmin:consentadmin:consentadmin_header}',
+	);
+}
+?>
diff --git a/modules/consentAdmin/templates/consentadmin.php b/modules/consentAdmin/templates/consentadmin.php
new file mode 100755
index 000000000..b7bdc3ed3
--- /dev/null
+++ b/modules/consentAdmin/templates/consentadmin.php
@@ -0,0 +1,132 @@
+<?php $this->includeAtTemplateBase('includes/header.php'); ?>
+<!--  default theme -->
+<?php 
+$this->includeLanguageFile('attributes.php'); // attribute listings translated by this dictionary
+ 
+?> 
+
+<script>
+	function setConsentText(consentStatus, show_spid) {
+		document.getElementById("consentText" + show_spid).innerHTML = consentStatus;
+	}	
+</script>
+
+<script src="includes/consentSimpleAjax.js"></script> 
+
+<style>
+.caSPName {
+	font-weight: bold;
+}
+
+td.caSPName {
+	vertical-align: top;
+}
+
+.caAllowed {
+	
+}
+
+td.caAllowed {
+	vertical-align: top;
+}
+
+td.caAttributes {
+	
+}
+
+tr.row0 td {
+	background-color: #888888;
+	color: black;
+}
+
+tr.row1 td {
+	background-color: #aaaaaa;
+	color: black;
+}
+
+a.orange {
+	color: #ffd633;
+}
+
+span.showhide {
+	
+}
+</style>
+	
+	<div id="content">
+
+		<!-- <h2><?php if (isset($this->data['header'])) { echo $this->t($this->data['header']); } else { echo "Some error occured"; } ?></h2> -->
+	    <h2><?php echo $this->t('consentadmin_header') ?></h2>	
+		<p> 
+		<?php echo $this->t('consentadmin_description1') ?> </p>
+
+		
+			<table>
+			<tr>
+				<th width="80%"><?php echo $this->t('service_provider_header') ?></th>
+				<th width="140"><?php echo $this->t('status_header') ?></th>
+			</tr>
+			<?php
+			$spList = $this->data['spList'];
+			$show_spid = 0;
+			//$show_hide_attributes= $this->t('show_hide_attributes');
+			$show_text = $this->t('show');
+			$hide_text = $this->t('hide');
+			$attributes_text = $this->t('attributes_text');
+			foreach ($spList AS $spName => $spValues) {
+				$this->includeInlineTranslation('spname', $spValues['name']);
+				$this->includeInlineTranslation('spdescription', $spValues['description']);
+				$htmlSpName = htmlspecialchars($this->t('spname', array(), false, true) );
+				$spDescription = htmlspecialchars($this->t('spdescription',array(), false, true));
+				$checkedAttr = $spValues['consentStatus'] == 'ok' ? "checked='yes'" : "";
+				$consentValue = $spValues['consentValue'];
+				$consentText = $spValues['consentStatus'] == 'changed' ? "attributes has changed" : "";
+				$row_class = ($show_spid % 2) ? "row0" : "row1";
+				echo <<<TRSTART
+<tr class="$row_class">
+<td>
+	<table>
+	  <tr class="$row_class"><td><span class='caSPName'><span title='$spDescription'>$htmlSpName</span>&emsp;<span style="font-size: 80%;"onclick="javascript:toggleShowAttributes('$show_spid');"><span id=showing_$show_spid >$show_text</span><span id=hiding_$show_spid style='display:none;'>$hide_text</span> $attributes_text</span></span></td>
+	  <!-- <tr><td><a class="orange" href="javascript:toggleShowAttributes('$show_spid');">$show_hide_attributes</a></td></tr> -->
+	  <tr><td colspan="2" class="caAttributes"><div id="attributes_$show_spid" style="display: none;">
+TRSTART;
+				$attributes = $spValues['attributes_by_sp'];
+				echo "\n<ul>\n";
+				foreach ($attributes AS $name => $value) {
+
+				if (isset($this->data['attribute_' . htmlspecialchars(strtolower($name)) ])) {
+				  $name = $this->data['attribute_' . htmlspecialchars(strtolower($name))];
+				}
+				$name = $this->t('attribute_'.strtolower($name)); // translate
+				if (sizeof($value) > 1) {
+						echo "<li>" . htmlspecialchars($name) . ":\n<ul>\n";
+						foreach ($value AS $v) {
+							echo '<li>' . htmlspecialchars($v) . "</li>\n";
+						}
+						echo "</ul>\n</li>\n";
+					} else {
+						echo "<li>" . htmlspecialchars($name) . ": " . htmlspecialchars($value[0]) . "</li>\n";
+					}
+				}
+				echo "</ul>";
+				echo <<<TRSTART
+	  </div></td></tr>
+  </table> 
+</td>
+	
+<td class='caAllowed'><input onClick="javascript:checkConsent(this.value, $show_spid, this.checked)" value='$consentValue' type='checkbox' $checkedAttr><span id="consentText$show_spid">$consentText</span></td>
+TRSTART;
+			echo "</td></tr>\n";
+			$show_spid++;
+			}
+			?>
+			</table>
+		
+			<p> 
+		<?php echo $this->t('consentadmin_description2') ?> </p>
+		
+		<h2>Logout</h2>
+
+			<p><?php echo $this->data['logout']; ?></p>
+		
+<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
diff --git a/modules/consentAdmin/templates/consentadminajax.php b/modules/consentAdmin/templates/consentadminajax.php
new file mode 100644
index 000000000..6897ad172
--- /dev/null
+++ b/modules/consentAdmin/templates/consentadminajax.php
@@ -0,0 +1,3 @@
+<?php 
+print $this->t($this->data['res']);
+?>
diff --git a/modules/consentAdmin/www/consentAdmin.php b/modules/consentAdmin/www/consentAdmin.php
new file mode 100644
index 000000000..73f3d0a0f
--- /dev/null
+++ b/modules/consentAdmin/www/consentAdmin.php
@@ -0,0 +1,261 @@
+<?php
+/*
+ * consentAdmin - Consent administration module
+ *
+ * This module enables the user to add and remove consents given for a given
+ * Service Provider.
+ *
+ * The module relies on methods and functions from the Consent module and can
+ * not be user without it.
+ *
+ * Author: Mads Freen - WAYF, Jacob Christiansen - WAYF
+ */
+
+/*
+ * Runs the processingchain and ignores all filter which have user 
+ * interaction.
+ */
+function driveProcessingChain($idp_metadata, $source, $sp_metadata, $sp_entityid, $attributes, $userid) {
+
+	/* 
+	 * Create a new processing chain 
+	 */
+	$pc = new SimpleSAML_Auth_ProcessingChain($idp_metadata, $sp_metadata, 'idp');
+
+	/* 
+	 * Construct the state.
+	 * REMEMBER: Do not set Return URL if you are calling processStatePassive
+	 */
+	$authProcState = array(
+		'Attributes' => $attributes,
+		'Destination' => $sp_metadata,
+		'Source' => $idp_metadata,
+		'isPassive' => TRUE,
+	);
+
+	/* 
+	 * Call processStatePAssive.
+	 * We are not interested in any user interaction, only modifications to the attributes
+	 */
+	$pc->processStatePassive($authProcState);
+
+	$attributes = $authProcState['Attributes'];
+
+	/*
+	 * Generate identifiers and hashes
+	 */
+	$destination = $sp_metadata['metadata-set'] . '|' . $sp_entityid;
+
+	$targeted_id    = sspmod_consent_Auth_Process_Consent::getTargetedID($userid, $source, $destination);
+	$attribute_hash = sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes, false);
+
+	SimpleSAML_Logger::info('consentAdmin: user: ' . $hashed_user_id);
+	SimpleSAML_Logger::info('consentAdmin: target: ' . $targeted_id);
+	SimpleSAML_Logger::info('consentAdmin: attribute: ' . $attribute_hash);
+
+	/* Return values */
+	return array($targeted_id, $attribute_hash, $attributes);
+}
+
+// Get config object
+$config = SimpleSAML_Configuration::getInstance();
+
+// Get session object
+$session = SimpleSAML_Session::getInstance();
+
+/* Check if valid local session exists */
+if (!isset($session) || !$session->isValid('saml2') ) {
+	// Set idpentity to force a specific IdP
+	SimpleSAML_Utilities::redirect('/saml2/sp/initSSO.php',
+		array('RelayState'  => SimpleSAML_Utilities::selfURL())
+	);
+}
+
+// Get user ID
+$userid_attributename = $config->getValue('consent_userid', 'eduPersonPrincipalName');
+$userids = ($session->getAttribute($userid_attributename));
+		
+if (empty($userids)) {
+	throw new Exception('Could not generate useridentifier for storing consent. Attribute [' .
+		$userid_attributename . '] was not available.');
+}
+
+$userid = $userids[0];
+
+// Get metadata storage handler
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+// Get all SP metadata
+$all_sp_metadata = $metadata->getList('saml20-sp-remote');
+
+// Get released attributes
+$attributes = $session->getAttributes();
+
+// Parse action, if any
+$action = null;
+$sp_entityid = null;
+if (!empty($_GET['cv'])) {
+	$sp_entityid=$_GET['cv'];
+}
+if (!empty($_GET['action'])) {
+	$action=$_GET["action"];
+}
+
+SimpleSAML_Logger::critical('consentAdmin: sp: ' .$sp_entityid.' action: '.$action);
+
+/*
+ * Get IdP id and metadata
+ */
+if($session->getIdP() != null) {
+	/*
+	 * From a remote idp (as bridge)
+ 	 */
+	$idp_entityid = $session->getIdP();
+	$idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-remote');
+} else {
+	/*
+	 * from the local idp
+	 */
+	$idp_entityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+	$idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-hosted');
+}
+ 
+SimpleSAML_Logger::info('consentAdmin: '.$idp_entityid);
+
+// Calc correct source
+$source = $idp_metadata['metadata-set'] . '|' . $idp_entityid;
+
+// Parse consent config
+$consent_storage = sspmod_consent_Store::parseStoreConfig($config->getValue('consentadmin'));
+
+// Calc correct user ID hash
+$hashed_user_id = sspmod_consent_Auth_Process_Consent::getHashedUserID($userid, $source);
+
+// If a checkbox have been clicked
+if ($action != null && $sp_entityid != null) {
+	// Get SP metadata
+	$sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote');
+
+	// Run AuthProc filters
+	list($targeted_id, $attribute_hash, $attributes_new) = driveProcessingChain($idp_metadata, $source, $sp_metadata, $sp_entityid, $attributes, $userid);
+
+	// Add a consent (or update if attributes have changed and old consent for SP and IdP exists)
+	if($action == 'true') {
+		$isStored = $consent_storage->saveConsent($hashed_user_id, $targeted_id, $attribute_hash);
+		if($isStored) {
+			$res =  "added";
+		} else {
+			$res = "updated";
+		}
+	// Remove consent
+	} else if($action == 'false') {
+		// Got consent, so this is a request to remove it
+		$rowcount = $consent_storage->deleteConsent($hashed_user_id, $targeted_id, $attribute_hash);
+		if($rowcount > 0) {
+			$res = "removed";
+		}
+	// Unknown action (should not happen)
+	} else {
+		SimpleSAML_Logger::info('consentAdmin: unknown action');
+		$res = "unknown";
+	}
+	/*
+	 * Init template to enable translation of status messages 
+ 	 */
+	$et = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadminajax.php', 'consentAdmin:consentadmin');
+	$et->data['res'] = $res;
+	$et->show();
+	exit;
+}
+
+// Get all consents for user
+$user_consent_list = $consent_storage->getConsents($hashed_user_id);
+
+// Parse list of consents
+$user_consent = array();
+foreach ($user_consent_list as $c) {
+	$user_consent[$c[0]]=$c[1];
+}
+
+$sp_empty_name = array(
+	'en' => '(name not specified)',
+	'no' => '(namn ikke spesifisert)',
+	'nn' => '(name not specified)',
+	'da' => '(navn ikke angivet)',
+	'en' => '(name not specified)',
+	'fr' => '(name not specified)',
+	'de' => '(name nicht definiert)',
+	'nl' => '(name not specified)',
+	'lu' => '(name not specified)',
+	'sl' => '(name not specified)',
+); // TODO: Should be moved to language file
+$sp_empty_description = array(
+	'en' => '(no description)',
+	'no' => '(ingen beskrivelse)',
+	'nn' => '(no description)',
+	'da' => '(ingen beskrivelse)',
+	'es' => '(no description)',
+	'fr' => '(no description)',
+	'de' => '(no description)',
+	'nl' => '(no description)',
+	'lu' => '(no description)',
+	'sl' => '(no description)',
+); // TODO: Should be moved to language file
+
+$template_sp_content = array();
+
+// Process consents for all SP
+foreach ($all_sp_metadata as $sp_entityid => $sp_values) {
+	// Get metadata for SP
+	$sp_metadata = $metadata->getMetaData($sp_entityid, 'saml20-sp-remote');
+
+	// Run attribute filters
+	list($targeted_id, $attribute_hash, $attributes_new) = driveProcessingChain($idp_metadata, $source, $sp_metadata, $sp_entityid, $attributes, $userid);
+
+	// Check if consent exists
+	if (array_key_exists($targeted_id, $user_consent)) {
+		$sp_status = "changed";
+		SimpleSAML_Logger::info('consentAdmin: changed');
+		// Check if consent is valid. (Possible that attributes has changed)
+		if ($user_consent[$targeted_id] == $attribute_hash) {
+			SimpleSAML_Logger::info('consentAdmin: ok');
+			$sp_status = "ok";
+		}
+	// Consent does not exists
+	} else {
+		SimpleSAML_Logger::info('consentAdmin: none');
+		$sp_status = "none";
+	}
+
+	// Set name of SP
+	if(empty($sp_values['name']) || !is_array($sp_values['name'])) {
+		$sp_name = $sp_empty_name;
+	} else {
+		$sp_name = $sp_metadata['name'];
+	}
+
+	// Set description of SP
+	if(empty($sp_metadata['description']) || !is_array($sp_metadata['description'])) {
+		$sp_description = $sp_empty_description;
+	} else {
+		$sp_description = $sp_metadata['description'];
+	}
+
+	// Fill out array for the template
+	$sp_list[$sp_entityid] = array(
+		'spentityid' => $sp_entityid,
+		'name' =>  $sp_name,
+		'description' =>  $sp_description,
+		'consentStatus' => $sp_status,
+		'consentValue' => $sp_entityid,
+		'attributes_by_sp' => $attributes_new,
+	);
+}
+
+// Init template
+$et = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadmin.php', 'consentAdmin:consentadmin');
+$et->data['header'] = 'Consent Administration';
+$et->data['logout'] = '<p>[ <a href="/' . $config->getValue('baseurlpath') . 'saml2/sp/initSLO.php?RelayState=https://www.wayf.dk">Logout</a> ]'; // TODO: Fix RelayState. Should be set in config
+$et->data['spList'] = $sp_list;
+$et->show();
+?>
diff --git a/modules/consentAdmin/www/includes/consentSimpleAjax.js b/modules/consentAdmin/www/includes/consentSimpleAjax.js
new file mode 100755
index 000000000..1aaa5f331
--- /dev/null
+++ b/modules/consentAdmin/www/includes/consentSimpleAjax.js
@@ -0,0 +1,70 @@
+var xmlHttp;
+
+function checkConsent(consentValue, show_spid, checkAction)
+{ 
+	xmlHttp=GetXmlHttpObject()
+	if (xmlHttp==null) {
+ 		alert ("Browser does not support HTTP Request")
+ 		
+ 		return
+	}
+	
+	var url="consentAdmin.php"
+	url=url+"?cv="+consentValue
+	url=url+"&action="+checkAction
+	url=url+"&sid="+Math.random()
+	xmlHttp.onreadystatechange=function() { 
+	if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
+	{ 
+		setConsentText(xmlHttp.responseText, show_spid);
+	} 
+}
+
+	xmlHttp.open("GET",url,true)
+	xmlHttp.send(null)
+}
+
+// This function will be automaticly called when the Ajax call is done returning data
+function stateChanged() { 
+	if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
+	{ 
+		//Alert("Status of consent:"  + xmlHttp.responseText );
+	} 
+}
+
+// This function creates an XMLHttpRequest
+function GetXmlHttpObject() {
+	var xmlHttp=null;
+	try
+ 	{
+		// Firefox, Opera 8.0+, Safari
+		xmlHttp=new XMLHttpRequest();
+	}
+	catch (e)
+	{
+		//Internet Explorer
+		try
+		{
+			xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
+		}
+		catch (e)
+		{
+			xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
+		}
+	}
+	
+	return xmlHttp;
+}
+
+function toggleShowAttributes(show_spid) {
+	var disp = document.getElementById('attributes_' + show_spid);
+	//var showhide = document.getElementById('showhide_' + show_spid);
+	var showing = document.getElementById('showing_' + show_spid);
+	var hiding = document.getElementById('hiding_' + show_spid);
+	
+	disp.style.display = (disp.style.display == 'none' ? 'block' : 'none');
+	//showhide.innerHTML = (disp.style.display == 'none' ? 'Show' : 'Hide')
+	showing.style.display = (disp.style.display == 'none' ? 'inline' : 'none');
+	hiding.style.display = (disp.style.display == 'none' ? 'none' : 'inline');
+	//alert('hiding display'+hiding.display);
+}
diff --git a/templates/admin-metadatalist.php b/templates/admin-metadatalist.php
index a4e64c074..ad3f9738b 100644
--- a/templates/admin-metadatalist.php
+++ b/templates/admin-metadatalist.php
@@ -40,7 +40,8 @@ $this->includeAtTemplateBase('includes/header.php');
 				if (count($entity['leftovers']) > 0) $warning = TRUE;
 				if (count($entity['required.notfound']) > 0) $warning = TRUE;
 
-
+				$t->includeInlineTranslation('spname', $name);
+				$name = $t->t('spname', array(), false, true);
 
 				echo '<h4 style="padding-left: 2em; clear: both;" onclick="document.getElementById(\'metadatasection-' . $encodedEntityID . '\').style.display=\'block\';">' . htmlspecialchars($name) . '</h4>';
 				
@@ -52,7 +53,9 @@ $this->includeAtTemplateBase('includes/header.php');
 				echo '<div id="metadatasection-' . $encodedEntityID . '" style="display: none">';
 				
 				if (isset($entity['optional.found']['description'])) {
-					echo '<p>' . htmlspecialchars($entity['optional.found']['description']) . '</p>';
+					$t->includeInlineTranslation('spdescription', $entity['optional.found']['description']);
+					$description = $t->t('spdescription');
+					echo '<p>' . htmlspecialchars($description) . '</p>';
 				}
 				
 				echo '<div style="margin-left: 1em">';
-- 
GitLab