Newer
Older
namespace SimpleSAML\Metadata;
use SimpleSAML\Database;
use SimpleSAML\Error;
/**
* 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
class MetaDataStorageHandlerPdo extends MetaDataStorageSource
/**
* Prefix to apply to the metadata table
*/
/**
* This is an associative array which stores the different metadata sets we have loaded.
*/
/**
* All the metadata sets supported by this MetaDataStorageHandler
'adfs-idp-hosted',
'adfs-sp-remote',
'saml20-idp-hosted',
'saml20-idp-remote',
'saml20-sp-remote',
/**
* 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 associative array with the configuration for this handler.
public function __construct(/** @scrutinizer ignore-unused */ array $config)
$this->db = Database::getInstance();
}
/**
* 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|null $metadata Associative array with the metadata, or NULL if we are unable to load
* metadata from the given file.
Bryce Lowe
committed
* @throws \Exception If a database error occurs.
* @throws \SimpleSAML\Error\Exception If the metadata can be retrieved from the database, but cannot be decoded.
private function load(string $set): ?array
if (!in_array($set, $this->supportedSets, true)) {
return null;
}
$stmt = $this->db->read("SELECT entity_id, entity_data FROM $tableName");
if ($stmt->execute()) {
$data = json_decode($d['entity_data'], true);
if ($data === null) {
throw new Error\Exception("Cannot decode metadata for entity '${d['entity_id']}'");
}
if (!array_key_exists('entityid', $data)) {
$data['entityid'] = $d['entity_id'];
}
$metadata[$d['entity_id']] = $data;
throw new \Exception(
'PDO metadata handler: Database error: ' . var_export($this->db->getLastError(), 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(string $set): array
{
if (array_key_exists($set, $this->cachedMetadata)) {
return $this->cachedMetadata[$set];
}
$metadataSet = $this->load($set);
if ($metadataSet === null) {
}
foreach ($metadataSet as $entityId => &$entry) {
$entry['entityid'] = $entityId;
}
$this->cachedMetadata[$set] = $metadataSet;
return $metadataSet;
}
* Retrieve a metadata entry.
* @param string $entityId The entityId we are looking up.
* @param string $set The set we are looking for metadata in.
*
* @return array|null An associative array with metadata for the given entity, or NULL if we are unable to
* locate the entity.
*/
public function getMetaData(string $entityId, string $set): ?array
Bryce Lowe
committed
// validate the metadata set is valid
if (!in_array($set, $this->supportedSets, true)) {
return null;
}
Bryce Lowe
committed
// support caching
if (isset($this->cachedMetadata[$entityId][$set])) {
return $this->cachedMetadata[$entityId][$set];
}
Bryce Lowe
committed
$tableName = $this->getTableName($set);
$stmt = $this->db->read(
"SELECT entity_id, entity_data FROM {$tableName} WHERE entity_id = :entityId",
['entityId' => $entityId]
);
Bryce Lowe
committed
// throw pdo exception upon execution failure
if (!$stmt->execute()) {
throw new \Exception(
'PDO metadata handler: Database error: ' . var_export($this->db->getLastError(), true)
);
Bryce Lowe
committed
// load the metadata into an array
$metadataSet = [];
while ($d = $stmt->fetch()) {
$data = json_decode($d['entity_data'], true);
if (json_last_error() != JSON_ERROR_NONE) {
throw new \SimpleSAML\Error\Exception(
"Cannot decode metadata for entity '${d['entity_id']}'"
);
Bryce Lowe
committed
}
Bryce Lowe
committed
// update the entity id to either the key (if not dynamic or generate the dynamic hosted url)
$data['entityid'] = $entityId;
$metadataSet[$d['entity_id']] = $data;
Bryce Lowe
committed
}
$indexLookup = $this->lookupIndexFromEntityId($entityId, $metadataSet);
if (isset($indexLookup) && array_key_exists($indexLookup, $metadataSet)) {
$this->cachedMetadata[$indexLookup][$set] = $metadataSet[$indexLookup];
return $this->cachedMetadata[$indexLookup][$set];
Bryce Lowe
committed
return null;
}
/**
* 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 successfully added
*/
public function addEntry(string $index, string $set, array $entityData): bool
if (!in_array($set, $this->supportedSets, true)) {
return false;
}
$tableName = $this->getTableName($set);
$metadata = $this->db->read(
"SELECT entity_id, entity_data FROM $tableName WHERE entity_id = :entity_id",
);
$retrivedEntityIDs = $metadata->fetch();
'entity_id' => $index,
'entity_data' => json_encode($entityData),
if ($retrivedEntityIDs !== false && count($retrivedEntityIDs) > 0) {
$rows = $this->db->write(
"UPDATE $tableName SET entity_data = :entity_data WHERE entity_id = :entity_id",
$params
);
} else {
$rows = $this->db->write(
"INSERT INTO $tableName (entity_id, entity_data) VALUES (:entity_id, :entity_data)",
$params
);
}
return $rows === 1;
}
/**
* 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(string $table): string
return $this->db->applyPrefix(str_replace("-", "_", $this->tablePrefix . $table));
}
/**
* Initialize the configured database
*
* @return int|false The number of SQL statements successfully executed, false if some error occurred.
$stmt = 0;
$fine = true;
$driver = $this->db->getDriver();
$text = 'TEXT';
if ($driver === 'mysql') {
$text = 'MEDIUMTEXT';
}
foreach ($this->supportedSets as $set) {
$tableName = $this->getTableName($set);
$rows = $this->db->write(sprintf(
"CREATE TABLE IF NOT EXISTS $tableName (entity_id VARCHAR(255) PRIMARY KEY NOT NULL, "
. "entity_data %s NOT NULL)",
$text
));
$fine = false;
} else {
$stmt += $rows;
}
}
if (!$fine) {
return false;
return $stmt;