diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2022-02-02 20:10:10 +0300 |
---|---|---|
committer | Côme Chilliet <come.chilliet@nextcloud.com> | 2022-02-10 19:44:46 +0300 |
commit | f975fe4ff395c61020e898867b824ab8354ff942 (patch) | |
tree | 11da631c80b971415da84f48ec4f7c51a40f9635 | |
parent | 1e2baa5a6bbcb23bfe25b49d03159ad4293d5280 (diff) |
invalidated duplicated UUIDs prior to migration change
- in a proper setup there are no duplicated UUIDs
- not all setups are proper
- log warning to admin
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
-rw-r--r-- | apps/user_ldap/lib/Migration/Version1130Date20211102154716.php | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php index 8695f90ca65..94f0f0aa82e 100644 --- a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php +++ b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace OCA\User_LDAP\Migration; use Closure; +use Generator; use OCP\DB\Exception; use OCP\DB\ISchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -52,6 +53,12 @@ class Version1130Date20211102154716 extends SimpleMigrationStep { return 'Adjust LDAP user and group ldap_dn column lengths and add ldap_dn_hash columns'; } + public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { + foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { + $this->processDuplicateUUIDs($tableName); + } + } + /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` @@ -172,4 +179,87 @@ class Version1130Date20211102154716 extends SimpleMigrationStep { ->where($qb->expr()->eq('owncloud_name', $qb->createParameter('name'))); return $qb; } + + /** + * @throws Exception + */ + protected function processDuplicateUUIDs(string $table): void { + $uuids = $this->getDuplicatedUuids($table); + $idsWithUuidToInvalidate = []; + foreach ($uuids as $uuid) { + array_push($idsWithUuidToInvalidate, ...$this->getNextcloudIdsByUuid($table, $uuid)); + } + $this->invalidateUuids($table, $idsWithUuidToInvalidate); + } + + /** + * @throws Exception + */ + protected function invalidateUuids(string $table, array $idList): void { + $update = $this->dbc->getQueryBuilder(); + $update->update($table) + ->set('directory_uuid', $update->createParameter('invalidatedUuid')) + ->where($update->expr()->eq('owncloud_name', $update->createParameter('nextcloudId'))); + + while ($nextcloudId = array_shift($idList)) { + $update->setParameter('nextcloudId', $nextcloudId); + $update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6))); + try { + $update->executeStatement(); + $this->logger->warning( + 'LDAP user or group with ID {nid} has a duplicated UUID value which therefore was invalidated. You may double-check your LDAP configuration and trigger an update of the UUID.', + [ + 'app' => 'user_ldap', + 'nid' => $nextcloudId, + ] + ); + } catch (Exception $e) { + // Catch possible, but unlikely duplications if new invalidated errors. + // There is the theoretical chance of an infinity loop is, when + // the constraint violation has a different background. I cannot + // think of one at the moment. + if ($e->getReason() !== Exception::REASON_CONSTRAINT_VIOLATION) { + throw $e; + } + $idList[] = $nextcloudId; + } + } + } + + /** + * @throws \OCP\DB\Exception + * @return array<string> + */ + protected function getNextcloudIdsByUuid(string $table, string $uuid): array { + $select = $this->dbc->getQueryBuilder(); + $select->select('owncloud_name') + ->from($table) + ->where($select->expr()->eq('directory_uuid', $select->createNamedParameter($uuid))); + + $result = $select->executeQuery(); + $idList = []; + while ($id = $result->fetchOne()) { + $idList[] = $id; + } + $result->closeCursor(); + return $idList; + } + + /** + * @return Generator<string> + * @throws \OCP\DB\Exception + */ + protected function getDuplicatedUuids(string $table): Generator{ + $select = $this->dbc->getQueryBuilder(); + $select->select('directory_uuid') + ->from($table) + ->groupBy('directory_uuid') + ->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1))); + + $result = $select->executeQuery(); + while ($uuid = $result->fetchOne()) { + yield $uuid; + } + $result->closeCursor(); + } } |