diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2022-02-02 20:10:10 +0300 |
---|---|---|
committer | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2022-02-15 15:23:24 +0300 |
commit | 6813b3814800b2e94ef71f173fe95bf884bd81f4 (patch) | |
tree | 11c4fddd9f1b81446e6ef12a45effacbc56cd539 | |
parent | 4b9241b6b2080c7a9b633306ebc5020a41bb25a7 (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 | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php index 0c1dcee0a35..335a310fd75 100644 --- a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php +++ b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php @@ -52,6 +52,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 +178,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(); + } } |