user_saml/lib/Migration/Version5000Date20211025124248.php
Arthur Schiwon ee8845252a also migrate sp-x509cert, sp-name-id-format, sp-privateKey
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-04-07 20:29:04 +00:00

232 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\User_SAML\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version5000Date20211025124248 extends SimpleMigrationStep {
private const IDP_CONFIG_KEYS = [
'general-idp0_display_name',
'general-uid_mapping',
'idp-entityId',
'idp-singleLogoutService.responseUrl',
'idp-singleLogoutService.url',
'idp-singleSignOnService.url',
'idp-x509cert',
'security-authnRequestsSigned',
'security-general',
'security-logoutRequestSigned',
'security-logoutResponseSigned',
'security-lowercaseUrlencoding',
'security-nameIdEncrypted',
'security-offer',
'security-required',
'security-signatureAlgorithm',
'security-signMetadata',
'security-sloWebServerDecode',
'security-wantAssertionsEncrypted',
'security-wantAssertionsSigned',
'security-wantMessagesSigned',
'security-wantNameId',
'security-wantNameIdEncrypted',
'security-wantXMLValidation',
'saml-attribute-mapping-displayName_mapping',
'saml-attribute-mapping-email_mapping',
'saml-attribute-mapping-group_mapping',
'saml-attribute-mapping-home_mapping',
'saml-attribute-mapping-quota_mapping',
'sp-x509cert',
'sp-name-id-format',
'sp-privateKey',
];
/** @var IDBConnection */
private $dbc;
/** @var ?IQueryBuilder */
private $insertQuery;
/** @var ?IQueryBuilder */
private $deleteQuery;
/** @var ?IQueryBuilder */
private $readQuery;
public function __construct(IDBConnection $dbc) {
$this->dbc = $dbc;
}
/**
* @param IOutput $output
* @param Closure():ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('user_saml_configurations')) {
$table = $schema->createTable('user_saml_configurations');
$table->addColumn('id', Types::INTEGER, [
'notnull' => true,
]);
$table->addColumn('name', Types::STRING, [
'length' => 256,
'notnull' => false,
'default' => '',
]);
$table->addColumn('configuration', Types::TEXT, [
'notnull' => true,
]);
$table->setPrimaryKey(['id'], 'idx_user_saml_config');
}
return $schema;
}
/**
* @param IOutput $output
* @param Closure():IschemaWrapper $schemaClosure
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
$prefixes = $this->fetchPrefixes();
foreach ($prefixes as $prefix) {
$keyStart = $prefix === 1 ? '' : $prefix . '-';
$configKeys = array_reduce(
self::IDP_CONFIG_KEYS,
function (array $carry, string $rawConfigKey) use ($keyStart): array {
$carry[] = $keyStart . $rawConfigKey;
return $carry;
},
[]
);
$configData = $keysToDelete = [];
$gRows = $this->readConfiguration($configKeys);
while ($row = $gRows->current()) {
$configData[$this->normalizeKey($row['configkey'])] = $row['configvalue'];
$keysToDelete[] = $row['configkey'];
$gRows->next();
}
if (empty($configData)) {
continue; // No config found
}
if ($this->insertConfiguration($prefix, $configData) && !empty($keysToDelete)) {
$this->deleteOldConfiguration($keysToDelete);
}
}
$this->deletePrefixes();
}
/**
* @psalm-param list<string> $keys the list of keys to delete
*/
protected function deleteOldConfiguration(array $keys): bool {
if (!$this->deleteQuery) {
$this->deleteQuery = $this->dbc->getQueryBuilder();
$this->deleteQuery->delete('appconfig')
->where($this->deleteQuery->expr()->eq('appid', $this->deleteQuery->createNamedParameter('user_saml')))
->andWhere($this->deleteQuery->expr()->in('configkey', $this->deleteQuery->createParameter('cfgKeys')));
}
$deletedRows = $this->deleteQuery
->setParameter('cfgKeys', $keys, IQueryBuilder::PARAM_STR_ARRAY)
->execute();
return $deletedRows > 0;
}
/**
* @param array<string, string> $configData The key-value map of config to save
*/
protected function insertConfiguration(int $id, array $configData): bool {
if (!$this->insertQuery) {
$this->insertQuery = $this->dbc->getQueryBuilder();
$this->insertQuery->insert('user_saml_configurations')
->values([
'id' => $this->insertQuery->createParameter('configId'),
'name' => $this->insertQuery->createParameter('displayName'),
'configuration' => $this->insertQuery->createParameter('configuration'),
]);
}
$insertedRows = $this->insertQuery
->setParameter('configId', $id)
->setParameter('displayName', $configData['general-idp0_display_name'] ?? '')
->setParameter('configuration', \json_encode($configData, JSON_THROW_ON_ERROR))
->execute();
return $insertedRows > 0;
}
/** @psalm-param list<string> $configKeys */
protected function readConfiguration(array $configKeys): \Generator {
if (!$this->readQuery) {
$this->readQuery = $this->dbc->getQueryBuilder();
$this->readQuery->select('configkey', 'configvalue')
->from('appconfig')
->where($this->readQuery->expr()->eq('appid', $this->readQuery->createNamedParameter('user_saml')))
->andWhere($this->readQuery->expr()->in('configkey', $this->readQuery->createParameter('cfgKeys')));
}
$r = $this->readQuery->setParameter('cfgKeys', $configKeys, IQueryBuilder::PARAM_STR_ARRAY)
->execute();
while ($row = $r->fetch()) {
yield $row;
}
$r->closeCursor();
}
protected function normalizeKey(string $prefixedKey): string {
$isPrefixed = \preg_match('/^[0-9]*-/', $prefixedKey, $matches);
if ($isPrefixed === 0) {
return $prefixedKey;
} elseif ($isPrefixed === 1) {
return \substr($prefixedKey, strlen($matches[0]));
}
throw new \RuntimeException('Invalid regex pattern');
}
/** @psalm-return list<int> */
protected function fetchPrefixes(): array {
$q = $this->dbc->getQueryBuilder();
$q->select('configvalue')
->from('appconfig')
->where($q->expr()->eq('appid', $q->createNamedParameter('user_saml')))
->andWhere($q->expr()->eq('configkey', $q->createNamedParameter('providerIds')));
$r = $q->execute();
$prefixes = $r->fetchOne();
if ($prefixes === false) {
return [1]; // 1 is the default value for providerIds
}
return array_map('intval', explode(',', $prefixes));
}
protected function deletePrefixes(): void {
$q = $this->dbc->getQueryBuilder();
$q->delete('appconfig')
->where($q->expr()->eq('appid', $q->createNamedParameter('user_saml')))
->andWhere($q->expr()->eq('configkey', $q->createNamedParameter('providerIds')))
->execute();
}
}