<?php /** * Class for handling metadata files stored in a database. * * This class has been based off a previous version written by * mooknarf@gmail.com and patched to work with the latest version * of simpleSAMLphp * * @author Tyler Antonio, University of Alberta <tantonio@ualberta.ca> * @package simpleSAMLphp */ class SimpleSAML_Metadata_MetaDataStorageHandlerPdo extends SimpleSAML_Metadata_MetaDataStorageSource{ /** * PDO Database connection string */ private $dsn; /** * The PDO object */ private $db; /** * Prefix to apply to the metadata table */ private $tablePrefix; /** * This is an associative array which stores the different metadata sets we have loaded. */ private $cachedMetadata = array(); /** * All the metadata sets supported by this MetaDataStorageHandler */ public $supportedSets = array( 'adfs-idp-hosted', 'adfs-sp-remote', 'saml20-idp-hosted', 'saml20-idp-remote', 'saml20-sp-remote', 'shib13-idp-hosted', 'shib13-idp-remote', 'shib13-sp-hosted', 'shib13-sp-remote', 'wsfed-idp-remote', 'wsfed-sp-hosted' ); /** * This constructor initializes the PDO metadata storage handler with the specified * configuration. The configuration is an associative array with the following * possible elements (set in config.php): * - 'usePersistentConnection': TRUE/FALSE if database connection should be * persistent. * * - 'dsn': The database connection string. * * - 'username': Database user name * * - 'password': Password for the database user. * * @param array $config An associtive array with the configuration for this handler. */ public function __construct($config) { assert('is_array($config)'); $globalConfig = SimpleSAML_Configuration::getInstance(); $this->db = SimpleSAML\Database::getInstance(); $cfgHelp = SimpleSAML_Configuration::loadFromArray($config, 'pdo metadata source'); } /** * This function loads the given set of metadata from a file to a configured database. * This function returns NULL if it is unable to locate the given set in the metadata directory. * * @param string $set The set of metadata we are loading. * @return array $metadata Associative array with the metadata, or NULL if we are unable to load metadata from the given file. */ private function load($set) { assert('is_string($set)'); $tableName = $this->getTableName($set); if (!in_array($set, $this->supportedSets)) { return NULL; } $stmt = $this->db->read("SELECT entity_id, entity_data FROM $tableName"); if($stmt->execute()) { $metadata = array(); while($d = $stmt->fetch()) { $metadata[$d['entity_id']] = json_decode($d['entity_data'], TRUE); } return $metadata; } else { throw new Exception('PDO metadata handler: Database error: ' . var_export($this->pdo->errorInfo(), TRUE)); } } /** * Retrieve a list of all available metadata for a given set. * * @param string $set The set we are looking for metadata in. * @return array $metadata An associative array with all the metadata for the given set. */ public function getMetadataSet($set) { assert('is_string($set)'); if(array_key_exists($set, $this->cachedMetadata)) { return $this->cachedMetadata[$set]; } $metadataSet = $this->load($set); if($metadataSet === NULL) { $metadataSet = array(); } foreach ($metadataSet AS $entityId => &$entry) { if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) { $entry['entityid'] = $this->generateDynamicHostedEntityID($set); } else { $entry['entityid'] = $entityId; } } $this->cachedMetadata[$set] = $metadataSet; return $metadataSet; } private function generateDynamicHostedEntityID($set) { assert('is_string($set)'); /* Get the configuration. */ $baseurl = SimpleSAML_Utilities::getBaseURL(); if ($set === 'saml20-idp-hosted') { return $baseurl . 'saml2/idp/metadata.php'; } elseif($set === 'saml20-sp-hosted') { return $baseurl . 'saml2/sp/metadata.php'; } elseif($set === 'shib13-idp-hosted') { return $baseurl . 'shib13/idp/metadata.php'; } elseif($set === 'shib13-sp-hosted') { return $baseurl . 'shib13/sp/metadata.php'; } elseif($set === 'wsfed-sp-hosted') { return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost(); } elseif($set === 'adfs-idp-hosted') { return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost() . ':idp'; } else { throw new Exception('Can not generate dynamic EntityID for metadata of this type: [' . $set . ']'); } } /** * Add metadata to the configured database * * @param string $index Entity ID * @param string $set The set to add the metadata to * @param array $entityData Metadata * @return bool True/False if entry was sucessfully added */ public function addEntry($index, $set, $entityData) { assert('is_string($index)'); assert('is_string($set)'); assert('is_array($entityData)'); if (!in_array($set, $this->supportedSets)) { return FALSE; } $tableName = $this->getTableName($set); $metadata = $this->db->read("SELECT entity_id, entity_data FROM $tableName WHERE entity_id = :entity_id", array( 'entity_id' => $index, )); $retrivedEntityIDs = $metadata->fetch(); $params = array( 'entity_id' => $index, 'entity_data' => json_encode($entityData), ); if($retrivedEntityIDs !== FALSE && count($retrivedEntityIDs) > 0){ $stmt = $this->db->write("UPDATE $tableName SET entity_data = :entity_data WHERE entity_id = :entity_id", $params); } else{ $stmt = $this->db->write("INSERT INTO $tableName (entity_id, entity_data) VALUES (:entity_id, :entity_data)", $params); } return 1 === $stmt->rowCount(); } /** * Replace the -'s to an _ in table names for Metadata sets * since SQL does not allow a - in a table name. * * @param string $table Table * @return string Replaced table name */ private function getTableName($table) { assert('is_string($table)'); return $this->db->applyPrefix(str_replace("-", "_", $this->tablePrefix . $table)); } /** * Initialize the configured database */ public function initDatabase() { foreach ($this->supportedSets as $set) { $tableName = $this->getTableName($set); $this->db->write("CREATE TABLE IF NOT EXISTS $tableName (entity_id VARCHAR(255) PRIMARY KEY NOT NULL, entity_data TEXT NOT NULL)"); } } }