summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlav Morken <olav.morken@uninett.no>2011-03-15 14:29:32 +0000
committerOlav Morken <olav.morken@uninett.no>2011-03-15 14:29:32 +0000
commite6359625e54a681be65d29da07cb81505bd3f16f (patch)
treeb512a4a24f0d0f3acba38fdecf22ae0da01631cb
parenta8c889d29c931d32f041dbc8c107f785058b100a (diff)
downloadsimplesamlphp-e6359625e54a681be65d29da07cb81505bd3f16f.zip
simplesamlphp-e6359625e54a681be65d29da07cb81505bd3f16f.tar.gz
simplesamlphp-e6359625e54a681be65d29da07cb81505bd3f16f.tar.bz2
saml:SQLPersistentNameID: New filter for storing persistent NameIDs in SQL datastore.
git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2781 44740490-163a-0410-bde0-09ae8108e29a
-rw-r--r--modules/saml/docs/nameid.txt29
-rw-r--r--modules/saml/lib/Auth/Process/SQLPersistentNameID.php92
-rw-r--r--modules/saml/lib/IdP/SQLNameID.php172
3 files changed, 293 insertions, 0 deletions
diff --git a/modules/saml/docs/nameid.txt b/modules/saml/docs/nameid.txt
index 1fa3c63..3feb648 100644
--- a/modules/saml/docs/nameid.txt
+++ b/modules/saml/docs/nameid.txt
@@ -60,6 +60,23 @@ Generates a transient NameID with the format `urn:oasis:names:tc:SAML:2.0:nameid
No extra options are available for this filter.
+`saml:SQLPersistentNameID`
+--------------------------
+
+Generates and stores persistent NameIDs in a SQL datastore.
+
+This filter generates and stores a persistent NameID in a SQL datastore.
+To use this filter, simpleSAMLphp must be configured to use a SQL datastore.
+See the `store.type` configuration option in `config.php`.
+
+This filter will only create new NameIDs when the SP specifies `AllowCreate="true"` in the authentication request.
+
+### Options
+
+`attribute`
+: The name of the attribute we should use as the unique user ID.
+
+
Example
-------
@@ -79,3 +96,15 @@ This example makes three NameIDs available:
'Format' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
),
),
+
+Storing persistent NameIDs in a SQL database:
+
+ 'authproc' => array(
+ 1 => array(
+ 'class' => 'saml:TransientNameID',
+ ),
+ 2 => array(
+ 'class' => 'saml:SQLPersistentNameID',
+ 'attribute' => 'eduPersonPrincipalName',
+ ),
+ ),
diff --git a/modules/saml/lib/Auth/Process/SQLPersistentNameID.php b/modules/saml/lib/Auth/Process/SQLPersistentNameID.php
new file mode 100644
index 0000000..ac75341
--- /dev/null
+++ b/modules/saml/lib/Auth/Process/SQLPersistentNameID.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Authproc filter to generate a persistent NameID.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml_Auth_Process_SQLPersistentNameID extends sspmod_saml_BaseNameIDGenerator {
+
+ /**
+ * Which attribute contains the unique identifier of the user.
+ *
+ * @var string
+ */
+ private $attribute;
+
+
+ /**
+ * Initialize this filter, parse 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)');
+
+ $this->format = SAML2_Const::NAMEID_PERSISTENT;
+
+ if (!isset($config['attribute'])) {
+ throw new SimpleSAML_Error_Exception('PersistentNameID: Missing required option \'attribute\'.');
+ }
+ $this->attribute = $config['attribute'];
+ }
+
+
+ /**
+ * Get the NameID value.
+ *
+ * @return string|NULL The NameID value.
+ */
+ protected function getValue(array &$state) {
+
+ if (!isset($state['saml:NameIDFormat']) || $state['saml:NameIDFormat'] !== $this->format) {
+ SimpleSAML_Logger::debug('SQLPersistentNameID: Request did not specify persistent NameID format - not generating persistent NameID.');
+ return NULL;
+ }
+
+ if (!isset($state['Destination']['entityid'])) {
+ SimpleSAML_Logger::warning('SQLPersistentNameID: No SP entity ID - not generating persistent NameID.');
+ return NULL;
+ }
+ $spEntityId = $state['Destination']['entityid'];
+
+ if (!isset($state['Source']['entityid'])) {
+ SimpleSAML_Logger::warning('SQLPersistentNameID: No IdP entity ID - not generating persistent NameID.');
+ return NULL;
+ }
+ $idpEntityId = $state['Source']['entityid'];
+
+ if (!isset($state['Attributes'][$this->attribute]) || count($state['Attributes'][$this->attribute]) === 0) {
+ SimpleSAML_Logger::warning('SQLPersistentNameID: Missing attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating persistent NameID.');
+ return NULL;
+ }
+ if (count($state['Attributes'][$this->attribute]) > 1) {
+ SimpleSAML_Logger::warning('SQLPersistentNameID: More than one value in attribute ' . var_export($this->attribute, TRUE) . ' on user - not generating persistent NameID.');
+ return NULL;
+ }
+ $uid = array_values($state['Attributes'][$this->attribute]); /* Just in case the first index is no longer 0. */
+ $uid = $uid[0];
+
+
+ $value = sspmod_saml_IdP_SQLNameID::get($idpEntityId, $spEntityId, $uid);
+ if ($value !== NULL) {
+ SimpleSAML_Logger::debug('SQLPersistentNameID: Found persistent NameID ' . var_export($value, TRUE) . ' for user ' . var_export($uid, TRUE) . '.');
+ return $value;
+ }
+
+ if (!isset($state['saml:AllowCreate']) || !$state['saml:AllowCreate']) {
+ SimpleSAML_Logger::warning('SQLPersistentNameID: Did not find persistent NameID for user, and not allowed to create new NameID.');
+ throw new sspmod_saml_Error(SAML2_Const::STATUS_RESPONDER, 'urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy');
+ }
+
+ $value = SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(20));
+ SimpleSAML_Logger::debug('SQLPersistentNameID: Created persistent NameID ' . var_export($value, TRUE) . ' for user ' . var_export($uid, TRUE) . '.');
+ sspmod_saml_IdP_SQLNameID::add($idpEntityId, $spEntityId, $uid, $value);
+
+ return $value;
+ }
+
+}
diff --git a/modules/saml/lib/IdP/SQLNameID.php b/modules/saml/lib/IdP/SQLNameID.php
new file mode 100644
index 0000000..9560765
--- /dev/null
+++ b/modules/saml/lib/IdP/SQLNameID.php
@@ -0,0 +1,172 @@
+<?php
+
+/**
+ * Helper class for working with persistent NameIDs stored in SQL datastore.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml_IdP_SQLNameID {
+
+ /**
+ * Create NameID table in SQL, if it is missing.
+ *
+ * @param SimpleSAML_Store_SQL $store The datastore.
+ */
+ private static function createTable(SimpleSAML_Store_SQL $store) {
+
+ if ($store->getTableVersion('saml_PersistentNameID') === 1) {
+ return;
+ }
+
+ $query = 'CREATE TABLE ' . $store->prefix . '_saml_PersistentNameID (
+ _idp VARCHAR(256) NOT NULL,
+ _sp VARCHAR(256) NOT NULL,
+ _user VARCHAR(256) NOT NULL,
+ _value VARCHAR(40) NOT NULL,
+ UNIQUE (_idp, _sp, _user)
+ )';
+ $store->pdo->exec($query);
+
+ $query = 'CREATE INDEX ' . $store->prefix . '_saml_PersistentNameID_idp_sp ON ' . $store->prefix . '_saml_PersistentNameID (_idp, _sp)';
+ $store->pdo->exec($query);
+
+ $store->setTableVersion('saml_PersistentNameID', 1);
+ }
+
+
+ /**
+ * Retrieve the SQL datastore.
+ *
+ * Will also ensure that the NameID table is present.
+ *
+ * @return SimpleSAML_Store_SQL SQL datastore.
+ */
+ private static function getStore() {
+
+ $store = SimpleSAML_Store::getInstance();
+ if (!($store instanceof SimpleSAML_Store_SQL)) {
+ throw new SimpleSAML_Error_Exception('SQL NameID store requires simpleSAMLphp to be configured with a SQL datastore.');
+ }
+
+ self::createTable($store);
+
+ return $store;
+ }
+
+
+ /**
+ * Add a NameID into the database.
+ *
+ * @param SimpleSAML_Store_SQL $store The data store.
+ * @param string $idpEntityId The IdP entityID.
+ * @param string $spEntityId The SP entityID.
+ * @param string $user The user's unique identificator (e.g. username).
+ * @param string $value The NameID value.
+ */
+ public static function add($idpEntityId, $spEntityId, $user, $value) {
+ assert('is_string($idpEntityId)');
+ assert('is_string($spEntityId)');
+ assert('is_string($user)');
+ assert('is_string($value)');
+
+ $store = self::getStore();
+
+ $params = array(
+ '_idp' => $idpEntityId,
+ '_sp' => $spEntityId,
+ '_user' => $user,
+ '_value' => $value,
+ );
+
+ $query = 'INSERT INTO ' . $store->prefix . '_saml_PersistentNameID (_idp, _sp, _user, _value) VALUES(:_idp, :_sp, :_user, :_value)';
+ $query = $store->pdo->prepare($query);
+ $query->execute($params);
+ }
+
+
+ /**
+ * Retrieve a NameID into from database.
+ *
+ * @param string $idpEntityId The IdP entityID.
+ * @param string $spEntityId The SP entityID.
+ * @param string $user The user's unique identificator (e.g. username).
+ * @return string|NULL $value The NameID value, or NULL of no NameID value was found.
+ */
+ public static function get($idpEntityId, $spEntityId, $user) {
+ assert('is_string($idpEntityId)');
+ assert('is_string($spEntityId)');
+ assert('is_string($user)');
+
+ $store = self::getStore();
+
+ $params = array(
+ '_idp' => $idpEntityId,
+ '_sp' => $spEntityId,
+ '_user' => $user,
+ );
+
+ $query = 'SELECT _value FROM ' . $store->prefix . '_saml_PersistentNameID WHERE _idp = :_idp AND _sp = :_sp AND _user = :_user';
+ $query = $store->pdo->prepare($query);
+ $query->execute($params);
+
+ $row = $query->fetch(PDO::FETCH_ASSOC);
+ if ($row === FALSE) {
+ /* No NameID found. */
+ return NULL;
+ }
+
+ return $row['_value'];
+ }
+
+
+ /**
+ * Delete a NameID from the database.
+ *
+ * @param string $idpEntityId The IdP entityID.
+ * @param string $spEntityId The SP entityID.
+ * @param string $user The user's unique identificator (e.g. username).
+ */
+ public static function delete($idpEntityId, $spEntityId, $user) {
+ assert('is_string($idpEntityId)');
+ assert('is_string($spEntityId)');
+ assert('is_string($user)');
+
+ $store = self::getStore();
+
+ $params = array(
+ '_idp' => $idpEntityId,
+ '_sp' => $spEntityId,
+ '_user' => $user,
+ );
+
+ $query = 'DELETE FROM ' . $store->prefix . '_saml_PersistentNameID WHERE _idp = :_idp AND _sp = :_sp AND _user = :_user';
+ $query = $store->pdo->prepare($query);
+ $query->execute($params);
+ }
+
+
+ /**
+ * Retrieve all federated identities for an IdP-SP pair.
+ *
+ * @param string $idpEntityId The IdP entityID.
+ * @param string $spEntityId The SP entityID.
+ * @return array Array of userid => NameID.
+ */
+ public static function getIdentities($idpEntityId, $spEntityId) {
+ assert('is_string($idpEntityId)');
+ assert('is_string($spEntityId)');
+
+ $query = 'SELECT _user, _value FROM ' . $store->prefix . '_saml_PersistentNameID WHERE _idp = :_idp AND _sp = :_sp';
+ $query = $store->pdo->prepare($query);
+ $query->execute($params);
+
+ $res = array();
+ while ( ($row = $query->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
+ $res[$row['_user']] = $row['_value'];
+ }
+
+ return $res;
+ }
+
+}