Skip to content
Snippets Groups Projects
Commit 4c96d47c authored by Andreas Åkre Solberg's avatar Andreas Åkre Solberg
Browse files

Adding support for user consent, and consent storage with PDO. Consent storage...

Adding support for user consent, and consent storage with PDO. Consent storage is turned off by default.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@374 44740490-163a-0410-bde0-09ae8108e29a
parent f4798cc2
Branches main
No related tags found
No related merge requests found
...@@ -138,6 +138,16 @@ $config = array ( ...@@ -138,6 +138,16 @@ $config = array (
*/ */
'idpdisco.layout' => 'links', 'idpdisco.layout' => 'links',
/*
* Configuration of Consent storage used for attribute consent.
* connect, user and passwd is used with PDO (in example Mysql)
*/
'consent_usestorage' => FALSE,
'consent_userid' => 'eduPersonPrincipalName',
'consent_salt' => 'sdkfjhsidu87werwe8r79w8e7r',
'consent_pdo_connect' => 'mysql:host=sql.example.org;dbname=simplesamlconsent',
'consent_pdo_user' => 'simplesamluser',
'consent_pdo_passwd' => 'xxxx',
/* /*
* This option configures the metadata sources. The metadata sources is given as an array with * This option configures the metadata sources. The metadata sources is given as an array with
......
<?php
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Configuration.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/SessionHandler.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Logger.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Consent/ConsentStorage.php');
/**
* The Consent class is used for Attribute Release consent.
*
* @author Mads, Lasse, David, Peter and Andreas.
* @package simpleSAMLphp
* @version $Id$
*/
class SimpleSAML_Consent_Consent {
private $config;
private $session;
private $spentityid;
private $idpentityid;
private $salt;
private $attributes;
private $filteredattributes;
private $storageerror;
/**
* Constructor
*/
public function __construct($config, $session, $spentityid, $idpentityid, $attributes, $filteredattributes) {
$this->config = $config;
$this->salt = $this->config->getValue('consent_salt', 'eae46a3d5cb6e8546dded65be9855e5c');
$this->attributes = $attributes;
$this->filteredattributes = $filteredattributes;
$this->session = $session;
$this->spentityid = $spentityid;
$this->idpentityid = $idpentityid;
$this->storageerror = false;
}
/**
* An identifier for the federation (IdP). Will use SAML 2.0 IdP remote if running in bridge
* mode. If running as a standalone IdP, use the hosted IdP entity ID.
*
* @return Identifier of the IdP
*/
private function getIdPID() {
if ($this->session->getAuthority() === 'saml2') {
return $this->session->getIdP();
}
// from the local idp
return $this->idpentityid;
}
/**
* Generate a globally unique identifier of the user. Will also be anonymous (hashed).
*
* @return hash( eduPersonPrincipalName + salt + IdP-identifier )
*/
private function getHashedUserID() {
$userid_attributename = $this->config->getValue('consent_userid', 'eduPersonPrincipalName');
if (empty($this->attributes[$userid_attributename])) {
throw new Exception('Could not generate useridentifier for storing consent. Attribute [' .
$userid_attributename . '] was not available.');
}
$userid = $this->attributes[$userid_attributename];
return hash('sha1', $userid . $this->salt . $this->getIdPID() );
}
/**
* Get a targeted ID. An identifier that is unique per SP entity ID.
*/
private function getTargetedID($hashed_userid) {
return hash('sha1', $hashed_userid . $salt . $this->spentityid);
}
/**
* Get a hash value that changes when attributes are added or attribute values changed.
*/
private function getAttributeHash() {
return hash('sha1', serialize($this->filteredattributes));
}
public function useStorage() {
if ($this->storageerror) return false;
return $this->config->getValue('consent_usestorage', false);
}
public function consent() {
/**
* The user has manually accepted consent and chosen not to store the consent
* for later.
*/
if (isset($_GET['consent']) && !isset($_GET['saveconsent'])) {
return true;
}
if (!$this->useStorage() ) {
return false;
}
/*
* Generate identifiers and hashes
*/
$hashed_user_id = $this->getHashedUserID();
$targeted_id = $this->getTargetedID($hashed_user_id);
$attribute_hash = $this->getAttributeHash();
try {
// Create a consent storage.
$consent_storage = new SimpleSAML_Consent_Storage($this->config);
} catch (Exception $e ) {
SimpleSAML_Logger::error('Library - Consent: Error connceting to storage: ' . $e->getMessage() );
$this->storageerror = true;
return false;
}
/**
* User has given cosent and asked for storing it for later.
*/
if (isset($_GET['consent']) && isset($_GET['saveconsent'])) {
try {
$consent_storage->store($hashed_user_id, $targeted_id, $attribute_hash);
} catch (Exception $e) {
SimpleSAML_Logger::error('Library - Consent: Error connceting to storage: ' . $e->getMessage() );
}
return true;
}
/**
* Check if consent exists in storage, and if it does update the usage time stamp
* and return true.
*/
try {
if ($consent_storage->lookup($hashed_user_id, $targeted_id, $attribute_hash)) {
SimpleSAML_Logger::notice('Library - Consent consent(): Found stored consent.');
return true;
}
} catch (Exception $e) {
SimpleSAML_Logger::error('Library - Consent: Error connceting to storage: ' . $e->getMessage() );
}
return false;
}
}
?>
\ No newline at end of file
<?php
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Configuration.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/SessionHandler.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Logger.php');
/**
* The Consent Storage class is used for storing Attribute Release consents.
*
* CREATE TABLE consent (
* federation_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 (federation_id, service_id)
* );
*
* @author Mads, Lasse, David, Peter and Andreas.
* @package simpleSAMLphp
* @version $Id$
*/
class SimpleSAML_Consent_Storage {
private $config;
private $dbh;
/**
* Constructor
*/
public function __construct($config) {
$this->config = $config;
$pdo_connect = $config->getValue('consent_pdo_connect');
$pdo_user = $config->getValue('consent_pdo_user');
$pdo_passwd = $config->getValue('consent_pdo_passwd');
$this->dbh = new PDO($pdo_connect, $pdo_user, $pdo_passwd,
array( PDO::ATTR_PERSISTENT => false));
//$this->dbh->setAttribute('PDO::ATTR_TIMEOUT', 5);
}
/**
* Lookup consent database for an entry, and update the timestamp.
*
* @return Will return true if consent is stored, and false if consent is not stored.
*/
public function lookup($user_id, $targeted_id, $attribute_hash) {
$stmt = $this->dbh->prepare("UPDATE consent SET usage_date = NOW() WHERE federation_id = ? AND service_id = ? AND attribute = ?");
$stmt->execute(array($user_id, $targeted_id, $attribute_hash));
$rows = $stmt->rowCount();
SimpleSAML_Logger::debug('Library - ConsentStorage get(): user_id : ' . $user_id);
SimpleSAML_Logger::debug('Library - ConsentStorage get(): targeted_id : ' . $targeted_id);
SimpleSAML_Logger::debug('Library - ConsentStorage get(): attribute_hash : ' . $attribute_hash);
SimpleSAML_Logger::debug('Library - ConsentStorage get(): Number of rows : [' . $rows . ']');
return ($rows === 1);
}
/**
* Store user consent in database
*/
public function store($user_id, $targeted_id, $attribute_hash) {
SimpleSAML_Logger::debug('Library - ConsentStorage store(): user_id : ' . $user_id);
SimpleSAML_Logger::debug('Library - ConsentStorage store(): targeted_id : ' . $targeted_id);
SimpleSAML_Logger::debug('Library - ConsentStorage store(): attribute_hash : ' . $attribute_hash);
/**
* insert new entry into consent storage.
*/
$stmt = $this->dbh->prepare("REPLACE INTO consent VALUES (?,?,?,NOW(),NOW())");
$stmt->execute(array($user_id, $targeted_id, $attribute_hash));
}
}
?>
\ No newline at end of file
...@@ -3,17 +3,38 @@ ...@@ -3,17 +3,38 @@
<div id="content"> <div id="content">
<p>You are about to login to the service <strong><?php echo htmlspecialchars($data['spentityid']); ?></strong>. In the login proccess, the identity provider will send attributes containing information about your identity to this service. Do you accept this?</p> <p>You are about to login to the service <strong><?php echo htmlspecialchars($data['sp_name']); ?></strong>. In the login proccess, the identity provider will send attributes containing information about your identity to this service. Do you accept this?</p>
<p><a href="<?php echo htmlspecialchars($data['consenturl']); ?>"><strong>Yes</strong>, I accept that attributes are sent to this service</a></p>
<p style="font-size: x-small">[ <a href="">Show attributes that are sent</a> ]</p>
<form action="<?php echo htmlspecialchars($data['consenturl']); ?>">
<input type="submit" value="Yes">
<input type="hidden" name="consent" value="1">
<input type="hidden" name="RequestID" value="<?php echo $this->data['requestid']; ?>">
<?php if($this->data['usestorage']) { ?>
<input type="checkbox" name="saveconsent" id="saveconsent" value="1"> remember consent
<?php } ?>
</form>
<form action="<?php echo htmlspecialchars($this->data['noconsent']); ?>" method="GET">
<input type="submit" value="No">
</form>
<table style="font-size: x-small"> <table style="font-size: x-small">
<?php <?php
$attributes = $data['attributes']; $attributes = $data['attributes'];
foreach ($attributes AS $name => $value) { foreach ($attributes AS $name => $value) {
if (isset($this->data['attribute_' . htmlspecialchars(strtolower($name)) ])) {
$name = $this->data['attribute_' . htmlspecialchars(strtolower($name))];
}
if (sizeof($value) > 1) { if (sizeof($value) > 1) {
echo '<tr><td>' . htmlspecialchars($name) . '</td><td><ul>'; echo '<tr><td>' . htmlspecialchars($name) . '</td><td><ul>';
foreach ($value AS $v) { foreach ($value AS $v) {
......
<?php
$this->data['header'] = 'No consent was given';
$this->data['icon'] = 'bomb_l.png';
$this->includeAtTemplateBase('includes/header.php');
?>
<div id="content">
<h2><?php echo $this->data['title']; ?></h2>
You did not accept to give consent.
<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
\ No newline at end of file
<?php
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '_include.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
/* Load simpleSAMLphp, configuration */
$config = SimpleSAML_Configuration::getInstance();
$session = SimpleSAML_Session::getInstance(true);
$t = new SimpleSAML_XHTML_Template($config, 'noconsent.php');
$t->show();
?>
\ No newline at end of file
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '../../../www/_include.php'); require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '../../../www/_include.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php'); require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Consent/Consent.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php'); require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Logger.php'); require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Logger.php');
require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php'); require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
...@@ -52,7 +53,6 @@ if (!$config->getValue('enable.saml20-idp', false)) ...@@ -52,7 +53,6 @@ if (!$config->getValue('enable.saml20-idp', false))
*/ */
if (isset($_GET['SAMLRequest'])) { if (isset($_GET['SAMLRequest'])) {
try { try {
$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
$authnrequest = $binding->decodeRequest($_GET); $authnrequest = $binding->decodeRequest($_GET);
...@@ -107,7 +107,6 @@ if (isset($_GET['SAMLRequest'])) { ...@@ -107,7 +107,6 @@ if (isset($_GET['SAMLRequest'])) {
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'CACHEAUTHNREQUEST', $exception); SimpleSAML_Utilities::fatalError($session->getTrackID(), 'CACHEAUTHNREQUEST', $exception);
} }
} else { } else {
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS'); SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS');
} }
...@@ -148,31 +147,7 @@ if (!isset($session) || !$session->isValid($authority) ) { ...@@ -148,31 +147,7 @@ if (!isset($session) || !$session->isValid($authority) ) {
$spentityid = $requestcache['Issuer']; $spentityid = $requestcache['Issuer'];
$spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote'); $spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote');
/* $sp_name = (isset($spmetadata['name']) ? $spmetadata['name'] : $spentityid);
* Dealing with attribute release consent.
*/
if (array_key_exists('requireconsent', $idpmetadata)
&& $idpmetadata['requireconsent']) {
if (!isset($_GET['consent'])) {
SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Requires consent from user for attribute release');
$t = new SimpleSAML_XHTML_Template($config, 'consent.php');
$t->data['header'] = 'Consent';
$t->data['spentityid'] = $spentityid;
$t->data['attributes'] = $session->getAttributes();
$t->data['consenturl'] = SimpleSAML_Utilities::addURLparameter(SimpleSAML_Utilities::selfURL(), 'consent=1');
$t->show();
exit(0);
} else {
SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Got consent from user');
}
}
// Adding this service provider to the list of sessions. // Adding this service provider to the list of sessions.
// Right now the list is used for SAML 2.0 only. // Right now the list is used for SAML 2.0 only.
...@@ -186,7 +161,8 @@ if (!isset($session) || !$session->isValid($authority) ) { ...@@ -186,7 +161,8 @@ if (!isset($session) || !$session->isValid($authority) ) {
/* /*
* Attribute handling * Attribute handling
*/ */
$afilter = new SimpleSAML_XML_AttributeFilter($config, $session->getAttributes()); $attributes = $session->getAttributes();
$afilter = new SimpleSAML_XML_AttributeFilter($config, $attributes);
$afilter->process($idpmetadata, $spmetadata); $afilter->process($idpmetadata, $spmetadata);
/** /**
...@@ -210,6 +186,45 @@ if (!isset($session) || !$session->isValid($authority) ) { ...@@ -210,6 +186,45 @@ if (!isset($session) || !$session->isValid($authority) ) {
$filteredattributes = $afilter->getAttributes(); $filteredattributes = $afilter->getAttributes();
/*
* Dealing with attribute release consent.
*/
$requireconsent = false;
if (isset($idpmetadata['requireconsent'])) {
if (is_bool($idpmetadata['requireconsent'])) {
$requireconsent = $idpmetadata['requireconsent'];
} else {
throw new Exception('SAML 2.0 IdP hosted metadata parameter [requireconsent] is in illegal format, must be a PHP boolean type.');
}
}
if ($requireconsent) {
$consent = new SimpleSAML_Consent_Consent($config, $session, $spentityid, $idpentityid, $attributes, $filteredattributes);
if (!$consent->consent()) {
$t = new SimpleSAML_XHTML_Template($config, 'consent.php', 'attributes.php');
$t->data['header'] = 'Consent';
$t->data['sp_name'] = $sp_name;
$t->data['attributes'] = $filteredattributes;
$t->data['consenturl'] = SimpleSAML_Utilities::selfURLNoQuery();
$t->data['requestid'] = $requestid;
$t->data['usestorage'] = $consent->useStorage();
$t->data['noconsent'] = '/' . $config->getBaseURL() . 'noconsent.php';
$t->show();
exit;
}
}
// END ATTRIBUTE CONSENT CODE
// Generate an SAML 2.0 AuthNResponse message // Generate an SAML 2.0 AuthNResponse message
$ar = new SimpleSAML_XML_SAML20_AuthnResponse($config, $metadata); $ar = new SimpleSAML_XML_SAML20_AuthnResponse($config, $metadata);
$authnResponseXML = $ar->generate($idpentityid, $spentityid, $requestid, null, $filteredattributes); $authnResponseXML = $ar->generate($idpentityid, $spentityid, $requestid, null, $filteredattributes);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment