mirror of
https://github.com/netzbegruenung/user_saml.git
synced 2024-05-03 17:43:41 +02:00
ee8845252a
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
232 lines
6.7 KiB
PHP
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();
|
|
}
|
|
}
|