aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Repair/RepairDavShares.php
blob: 792fdd4033e01ea78c2c57aeb82fd22cdeaa8231 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<?php

declare(strict_types=1);

/**
 * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
namespace OC\Repair;

use OCP\DB\Exception;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use Psr\Log\LoggerInterface;
use function strlen;
use function substr;
use function urldecode;
use function urlencode;

class RepairDavShares implements IRepairStep {
	protected const GROUP_PRINCIPAL_PREFIX = 'principals/groups/';

	/** @var IConfig */
	private $config;
	/** @var IDBConnection */
	private $dbc;
	/** @var IGroupManager */
	private $groupManager;
	/** @var LoggerInterface */
	private $logger;
	/** @var bool */
	private $hintInvalidShares = false;

	public function __construct(
		IConfig $config,
		IDBConnection $dbc,
		IGroupManager $groupManager,
		LoggerInterface $logger
	) {
		$this->config = $config;
		$this->dbc = $dbc;
		$this->groupManager = $groupManager;
		$this->logger = $logger;
	}

	/**
	 * @inheritDoc
	 */
	public function getName() {
		return 'Repair DAV shares';
	}

	protected function repairUnencodedGroupShares() {
		$qb = $this->dbc->getQueryBuilder();
		$qb->select(['id', 'principaluri'])
			->from('dav_shares')
			->where($qb->expr()->like('principaluri', $qb->createNamedParameter(self::GROUP_PRINCIPAL_PREFIX . '%')));

		$updateQuery = $this->dbc->getQueryBuilder();
		$updateQuery->update('dav_shares')
			->set('principaluri', $updateQuery->createParameter('updatedPrincipalUri'))
			->where($updateQuery->expr()->eq('id', $updateQuery->createParameter('shareId')));

		$statement = $qb->execute();
		while ($share = $statement->fetch()) {
			$gid = substr($share['principaluri'], strlen(self::GROUP_PRINCIPAL_PREFIX));
			$decodedGid = urldecode($gid);
			$encodedGid = urlencode($gid);
			if ($gid === $encodedGid
				|| !$this->groupManager->groupExists($gid)
				|| ($gid !== $decodedGid && $this->groupManager->groupExists($decodedGid))
			) {
				$this->hintInvalidShares = $this->hintInvalidShares || $gid !== $encodedGid;
				continue;
			}

			// Repair when
			// + the group name needs encoding
			// + AND it is not encoded yet
			// + AND there are no ambivalent groups

			try {
				$fixedPrincipal = self::GROUP_PRINCIPAL_PREFIX . $encodedGid;
				$logParameters = [
					'app' => 'core',
					'id' => $share['id'],
					'old' => $share['principaluri'],
					'new' => $fixedPrincipal,
				];
				$updateQuery
					->setParameter('updatedPrincipalUri', $fixedPrincipal)
					->setParameter('shareId', $share['id'])
					->execute();
				$this->logger->info('Repaired principal for dav share {id} from {old} to {new}', $logParameters);
			} catch (Exception $e) {
				$logParameters['message'] = $e->getMessage();
				$logParameters['exception'] = $e;
				$this->logger->info('Could not repair principal for dav share {id} from {old} to {new}: {message}', $logParameters);
			}
		}
		return true;
	}

	/**
	 * @inheritDoc
	 */
	public function run(IOutput $output) {
		$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
		if (version_compare($versionFromBeforeUpdate, '20.0.8', '<')
			&& $this->repairUnencodedGroupShares()
		) {
			$output->info('Repaired DAV group shares');
			if ($this->hintInvalidShares) {
				$output->info('Invalid shares might be left in the database, running "occ dav:remove-invalid-shares" can remove them.');
			}
		}
	}
}