@@ -16,73 +16,73 @@ |
||
| 16 | 16 | |
| 17 | 17 | class Version1002Date20170607113030 extends SimpleMigrationStep { |
| 18 | 18 | |
| 19 | - /** |
|
| 20 | - * @param IDBConnection $connection |
|
| 21 | - */ |
|
| 22 | - public function __construct( |
|
| 23 | - protected IDBConnection $connection, |
|
| 24 | - ) { |
|
| 25 | - } |
|
| 19 | + /** |
|
| 20 | + * @param IDBConnection $connection |
|
| 21 | + */ |
|
| 22 | + public function __construct( |
|
| 23 | + protected IDBConnection $connection, |
|
| 24 | + ) { |
|
| 25 | + } |
|
| 26 | 26 | |
| 27 | - /** |
|
| 28 | - * @param IOutput $output |
|
| 29 | - * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 30 | - * @param array $options |
|
| 31 | - * @since 13.0.0 |
|
| 32 | - */ |
|
| 33 | - public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 34 | - /** @var ISchemaWrapper $schema */ |
|
| 35 | - $schema = $schemaClosure(); |
|
| 27 | + /** |
|
| 28 | + * @param IOutput $output |
|
| 29 | + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 30 | + * @param array $options |
|
| 31 | + * @since 13.0.0 |
|
| 32 | + */ |
|
| 33 | + public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 34 | + /** @var ISchemaWrapper $schema */ |
|
| 35 | + $schema = $schemaClosure(); |
|
| 36 | 36 | |
| 37 | - if (!$schema->hasTable('twofactor_backup_codes')) { |
|
| 38 | - // Legacy table does not exist |
|
| 39 | - return; |
|
| 40 | - } |
|
| 37 | + if (!$schema->hasTable('twofactor_backup_codes')) { |
|
| 38 | + // Legacy table does not exist |
|
| 39 | + return; |
|
| 40 | + } |
|
| 41 | 41 | |
| 42 | - $insert = $this->connection->getQueryBuilder(); |
|
| 43 | - $insert->insert('twofactor_backupcodes') |
|
| 44 | - ->values([ |
|
| 45 | - // Inserting with id might fail: 'id' => $insert->createParameter('id'), |
|
| 46 | - 'user_id' => $insert->createParameter('user_id'), |
|
| 47 | - 'code' => $insert->createParameter('code'), |
|
| 48 | - 'used' => $insert->createParameter('used'), |
|
| 49 | - ]); |
|
| 42 | + $insert = $this->connection->getQueryBuilder(); |
|
| 43 | + $insert->insert('twofactor_backupcodes') |
|
| 44 | + ->values([ |
|
| 45 | + // Inserting with id might fail: 'id' => $insert->createParameter('id'), |
|
| 46 | + 'user_id' => $insert->createParameter('user_id'), |
|
| 47 | + 'code' => $insert->createParameter('code'), |
|
| 48 | + 'used' => $insert->createParameter('used'), |
|
| 49 | + ]); |
|
| 50 | 50 | |
| 51 | - $query = $this->connection->getQueryBuilder(); |
|
| 52 | - $query->select('*') |
|
| 53 | - ->from('twofactor_backup_codes') |
|
| 54 | - ->orderBy('id', 'ASC'); |
|
| 55 | - $result = $query->executeQuery(); |
|
| 51 | + $query = $this->connection->getQueryBuilder(); |
|
| 52 | + $query->select('*') |
|
| 53 | + ->from('twofactor_backup_codes') |
|
| 54 | + ->orderBy('id', 'ASC'); |
|
| 55 | + $result = $query->executeQuery(); |
|
| 56 | 56 | |
| 57 | - $output->startProgress(); |
|
| 58 | - while ($row = $result->fetchAssociative()) { |
|
| 59 | - $output->advance(); |
|
| 57 | + $output->startProgress(); |
|
| 58 | + while ($row = $result->fetchAssociative()) { |
|
| 59 | + $output->advance(); |
|
| 60 | 60 | |
| 61 | - $insert |
|
| 62 | - // Inserting with id might fail: ->setParameter('id', $row['id'], IQueryBuilder::PARAM_INT) |
|
| 63 | - ->setParameter('user_id', $row['user_id'], IQueryBuilder::PARAM_STR) |
|
| 64 | - ->setParameter('code', $row['code'], IQueryBuilder::PARAM_STR) |
|
| 65 | - ->setParameter('used', $row['used'], IQueryBuilder::PARAM_INT) |
|
| 66 | - ->executeStatement(); |
|
| 67 | - } |
|
| 68 | - $output->finishProgress(); |
|
| 69 | - } |
|
| 61 | + $insert |
|
| 62 | + // Inserting with id might fail: ->setParameter('id', $row['id'], IQueryBuilder::PARAM_INT) |
|
| 63 | + ->setParameter('user_id', $row['user_id'], IQueryBuilder::PARAM_STR) |
|
| 64 | + ->setParameter('code', $row['code'], IQueryBuilder::PARAM_STR) |
|
| 65 | + ->setParameter('used', $row['used'], IQueryBuilder::PARAM_INT) |
|
| 66 | + ->executeStatement(); |
|
| 67 | + } |
|
| 68 | + $output->finishProgress(); |
|
| 69 | + } |
|
| 70 | 70 | |
| 71 | - /** |
|
| 72 | - * @param IOutput $output |
|
| 73 | - * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 74 | - * @param array $options |
|
| 75 | - * @return null|ISchemaWrapper |
|
| 76 | - * @since 13.0.0 |
|
| 77 | - */ |
|
| 78 | - public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 79 | - /** @var ISchemaWrapper $schema */ |
|
| 80 | - $schema = $schemaClosure(); |
|
| 71 | + /** |
|
| 72 | + * @param IOutput $output |
|
| 73 | + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 74 | + * @param array $options |
|
| 75 | + * @return null|ISchemaWrapper |
|
| 76 | + * @since 13.0.0 |
|
| 77 | + */ |
|
| 78 | + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 79 | + /** @var ISchemaWrapper $schema */ |
|
| 80 | + $schema = $schemaClosure(); |
|
| 81 | 81 | |
| 82 | - if ($schema->hasTable('twofactor_backup_codes')) { |
|
| 83 | - $schema->dropTable('twofactor_backup_codes'); |
|
| 84 | - return $schema; |
|
| 85 | - } |
|
| 86 | - return null; |
|
| 87 | - } |
|
| 82 | + if ($schema->hasTable('twofactor_backup_codes')) { |
|
| 83 | + $schema->dropTable('twofactor_backup_codes'); |
|
| 84 | + return $schema; |
|
| 85 | + } |
|
| 86 | + return null; |
|
| 87 | + } |
|
| 88 | 88 | } |
@@ -23,507 +23,507 @@ |
||
| 23 | 23 | * @package OCA\User_LDAP\Mapping |
| 24 | 24 | */ |
| 25 | 25 | abstract class AbstractMapping { |
| 26 | - /** |
|
| 27 | - * returns the DB table name which holds the mappings |
|
| 28 | - * |
|
| 29 | - * @return string |
|
| 30 | - */ |
|
| 31 | - abstract protected function getTableName(bool $includePrefix = true); |
|
| 32 | - |
|
| 33 | - /** |
|
| 34 | - * A month worth of cache time for as good as never changing mapping data. |
|
| 35 | - * Implemented when it was found that name-to-DN lookups are quite frequent. |
|
| 36 | - */ |
|
| 37 | - protected const LOCAL_CACHE_TTL = 2592000; |
|
| 38 | - |
|
| 39 | - /** |
|
| 40 | - * A week worth of cache time for rarely changing user count data. |
|
| 41 | - */ |
|
| 42 | - protected const LOCAL_USER_COUNT_TTL = 604800; |
|
| 43 | - |
|
| 44 | - /** |
|
| 45 | - * By default, the local cache is only used up to a certain amount of objects. |
|
| 46 | - * This constant holds this number. The amount of entries would amount up to |
|
| 47 | - * 1 MiB (worst case) per mappings table. |
|
| 48 | - * Setting `use_local_mapping_cache` for `user_ldap` to `yes` or `no` |
|
| 49 | - * deliberately enables or disables this mechanism. |
|
| 50 | - */ |
|
| 51 | - protected const LOCAL_CACHE_OBJECT_THRESHOLD = 2000; |
|
| 52 | - |
|
| 53 | - protected ?ICache $localNameToDnCache = null; |
|
| 54 | - |
|
| 55 | - /** @var array caches Names (value) by DN (key) */ |
|
| 56 | - protected array $cache = []; |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * @param IDBConnection $dbc |
|
| 60 | - */ |
|
| 61 | - public function __construct( |
|
| 62 | - protected IDBConnection $dbc, |
|
| 63 | - protected ICacheFactory $cacheFactory, |
|
| 64 | - protected IAppConfig $config, |
|
| 65 | - protected bool $isCLI, |
|
| 66 | - ) { |
|
| 67 | - $this->initLocalCache(); |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - protected function initLocalCache(): void { |
|
| 71 | - if ($this->isCLI || !$this->cacheFactory->isLocalCacheAvailable()) { |
|
| 72 | - return; |
|
| 73 | - } |
|
| 74 | - |
|
| 75 | - $useLocalCache = $this->config->getValueString('user_ldap', 'use_local_mapping_cache', 'auto', false); |
|
| 76 | - if ($useLocalCache !== 'yes' && $useLocalCache !== 'auto') { |
|
| 77 | - return; |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - $section = \str_contains($this->getTableName(), 'user') ? 'u/' : 'g/'; |
|
| 81 | - $this->localNameToDnCache = $this->cacheFactory->createLocal('ldap/map/' . $section); |
|
| 82 | - |
|
| 83 | - // We use the cache as well to store whether it shall be used. If the |
|
| 84 | - // answer was no, we unset it again. |
|
| 85 | - if ($useLocalCache === 'auto' && !$this->useCacheByUserCount()) { |
|
| 86 | - $this->localNameToDnCache = null; |
|
| 87 | - } |
|
| 88 | - } |
|
| 89 | - |
|
| 90 | - protected function useCacheByUserCount(): bool { |
|
| 91 | - $use = $this->localNameToDnCache->get('use'); |
|
| 92 | - if ($use !== null) { |
|
| 93 | - return $use; |
|
| 94 | - } |
|
| 95 | - |
|
| 96 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 97 | - $q = $qb->selectAlias($qb->createFunction('COUNT(owncloud_name)'), 'count') |
|
| 98 | - ->from($this->getTableName()); |
|
| 99 | - $q->setMaxResults(self::LOCAL_CACHE_OBJECT_THRESHOLD + 1); |
|
| 100 | - $result = $q->executeQuery(); |
|
| 101 | - $row = $result->fetchAssociative(); |
|
| 102 | - $result->closeCursor(); |
|
| 103 | - |
|
| 104 | - $use = (int)$row['count'] <= self::LOCAL_CACHE_OBJECT_THRESHOLD; |
|
| 105 | - $this->localNameToDnCache->set('use', $use, self::LOCAL_USER_COUNT_TTL); |
|
| 106 | - return $use; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - /** |
|
| 110 | - * checks whether a provided string represents an existing table col |
|
| 111 | - * |
|
| 112 | - * @param string $col |
|
| 113 | - * @return bool |
|
| 114 | - */ |
|
| 115 | - public function isColNameValid($col) { |
|
| 116 | - switch ($col) { |
|
| 117 | - case 'ldap_dn': |
|
| 118 | - case 'ldap_dn_hash': |
|
| 119 | - case 'owncloud_name': |
|
| 120 | - case 'directory_uuid': |
|
| 121 | - return true; |
|
| 122 | - default: |
|
| 123 | - return false; |
|
| 124 | - } |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * Gets the value of one column based on a provided value of another column |
|
| 129 | - * |
|
| 130 | - * @param string $fetchCol |
|
| 131 | - * @param string $compareCol |
|
| 132 | - * @param string $search |
|
| 133 | - * @return string|false |
|
| 134 | - * @throws \Exception |
|
| 135 | - */ |
|
| 136 | - protected function getXbyY($fetchCol, $compareCol, $search) { |
|
| 137 | - if (!$this->isColNameValid($fetchCol)) { |
|
| 138 | - //this is used internally only, but we don't want to risk |
|
| 139 | - //having SQL injection at all. |
|
| 140 | - throw new \Exception('Invalid Column Name'); |
|
| 141 | - } |
|
| 142 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 143 | - $qb->select($fetchCol) |
|
| 144 | - ->from($this->getTableName()) |
|
| 145 | - ->where($qb->expr()->eq($compareCol, $qb->createNamedParameter($search))); |
|
| 146 | - |
|
| 147 | - try { |
|
| 148 | - $res = $qb->executeQuery(); |
|
| 149 | - $data = $res->fetchOne(); |
|
| 150 | - $res->closeCursor(); |
|
| 151 | - return $data; |
|
| 152 | - } catch (Exception $e) { |
|
| 153 | - return false; |
|
| 154 | - } |
|
| 155 | - } |
|
| 156 | - |
|
| 157 | - /** |
|
| 158 | - * Performs a DELETE or UPDATE query to the database. |
|
| 159 | - * |
|
| 160 | - * @param IPreparedStatement $statement |
|
| 161 | - * @param array $parameters |
|
| 162 | - * @return bool true if at least one row was modified, false otherwise |
|
| 163 | - */ |
|
| 164 | - protected function modify(IPreparedStatement $statement, $parameters) { |
|
| 165 | - try { |
|
| 166 | - $result = $statement->execute($parameters); |
|
| 167 | - $updated = $result->rowCount() > 0; |
|
| 168 | - $result->closeCursor(); |
|
| 169 | - return $updated; |
|
| 170 | - } catch (Exception $e) { |
|
| 171 | - return false; |
|
| 172 | - } |
|
| 173 | - } |
|
| 174 | - |
|
| 175 | - /** |
|
| 176 | - * Gets the LDAP DN based on the provided name. |
|
| 177 | - */ |
|
| 178 | - public function getDNByName(string $name): string|false { |
|
| 179 | - $dn = array_search($name, $this->cache, true); |
|
| 180 | - if ($dn === false) { |
|
| 181 | - $dn = $this->localNameToDnCache?->get($name); |
|
| 182 | - if ($dn === null) { |
|
| 183 | - $dn = $this->getXbyY('ldap_dn', 'owncloud_name', $name); |
|
| 184 | - if ($dn !== false) { |
|
| 185 | - $this->cache[$dn] = $name; |
|
| 186 | - } |
|
| 187 | - $this->localNameToDnCache?->set($name, $dn, self::LOCAL_CACHE_TTL); |
|
| 188 | - } |
|
| 189 | - } |
|
| 190 | - return $dn ?? false; |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * Updates the DN based on the given UUID |
|
| 195 | - * |
|
| 196 | - * @param string $fdn |
|
| 197 | - * @param string $uuid |
|
| 198 | - * @return bool |
|
| 199 | - */ |
|
| 200 | - public function setDNbyUUID($fdn, $uuid) { |
|
| 201 | - $oldDn = $this->getDnByUUID($uuid); |
|
| 202 | - $statement = $this->dbc->prepare(' |
|
| 26 | + /** |
|
| 27 | + * returns the DB table name which holds the mappings |
|
| 28 | + * |
|
| 29 | + * @return string |
|
| 30 | + */ |
|
| 31 | + abstract protected function getTableName(bool $includePrefix = true); |
|
| 32 | + |
|
| 33 | + /** |
|
| 34 | + * A month worth of cache time for as good as never changing mapping data. |
|
| 35 | + * Implemented when it was found that name-to-DN lookups are quite frequent. |
|
| 36 | + */ |
|
| 37 | + protected const LOCAL_CACHE_TTL = 2592000; |
|
| 38 | + |
|
| 39 | + /** |
|
| 40 | + * A week worth of cache time for rarely changing user count data. |
|
| 41 | + */ |
|
| 42 | + protected const LOCAL_USER_COUNT_TTL = 604800; |
|
| 43 | + |
|
| 44 | + /** |
|
| 45 | + * By default, the local cache is only used up to a certain amount of objects. |
|
| 46 | + * This constant holds this number. The amount of entries would amount up to |
|
| 47 | + * 1 MiB (worst case) per mappings table. |
|
| 48 | + * Setting `use_local_mapping_cache` for `user_ldap` to `yes` or `no` |
|
| 49 | + * deliberately enables or disables this mechanism. |
|
| 50 | + */ |
|
| 51 | + protected const LOCAL_CACHE_OBJECT_THRESHOLD = 2000; |
|
| 52 | + |
|
| 53 | + protected ?ICache $localNameToDnCache = null; |
|
| 54 | + |
|
| 55 | + /** @var array caches Names (value) by DN (key) */ |
|
| 56 | + protected array $cache = []; |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * @param IDBConnection $dbc |
|
| 60 | + */ |
|
| 61 | + public function __construct( |
|
| 62 | + protected IDBConnection $dbc, |
|
| 63 | + protected ICacheFactory $cacheFactory, |
|
| 64 | + protected IAppConfig $config, |
|
| 65 | + protected bool $isCLI, |
|
| 66 | + ) { |
|
| 67 | + $this->initLocalCache(); |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + protected function initLocalCache(): void { |
|
| 71 | + if ($this->isCLI || !$this->cacheFactory->isLocalCacheAvailable()) { |
|
| 72 | + return; |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + $useLocalCache = $this->config->getValueString('user_ldap', 'use_local_mapping_cache', 'auto', false); |
|
| 76 | + if ($useLocalCache !== 'yes' && $useLocalCache !== 'auto') { |
|
| 77 | + return; |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + $section = \str_contains($this->getTableName(), 'user') ? 'u/' : 'g/'; |
|
| 81 | + $this->localNameToDnCache = $this->cacheFactory->createLocal('ldap/map/' . $section); |
|
| 82 | + |
|
| 83 | + // We use the cache as well to store whether it shall be used. If the |
|
| 84 | + // answer was no, we unset it again. |
|
| 85 | + if ($useLocalCache === 'auto' && !$this->useCacheByUserCount()) { |
|
| 86 | + $this->localNameToDnCache = null; |
|
| 87 | + } |
|
| 88 | + } |
|
| 89 | + |
|
| 90 | + protected function useCacheByUserCount(): bool { |
|
| 91 | + $use = $this->localNameToDnCache->get('use'); |
|
| 92 | + if ($use !== null) { |
|
| 93 | + return $use; |
|
| 94 | + } |
|
| 95 | + |
|
| 96 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 97 | + $q = $qb->selectAlias($qb->createFunction('COUNT(owncloud_name)'), 'count') |
|
| 98 | + ->from($this->getTableName()); |
|
| 99 | + $q->setMaxResults(self::LOCAL_CACHE_OBJECT_THRESHOLD + 1); |
|
| 100 | + $result = $q->executeQuery(); |
|
| 101 | + $row = $result->fetchAssociative(); |
|
| 102 | + $result->closeCursor(); |
|
| 103 | + |
|
| 104 | + $use = (int)$row['count'] <= self::LOCAL_CACHE_OBJECT_THRESHOLD; |
|
| 105 | + $this->localNameToDnCache->set('use', $use, self::LOCAL_USER_COUNT_TTL); |
|
| 106 | + return $use; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + /** |
|
| 110 | + * checks whether a provided string represents an existing table col |
|
| 111 | + * |
|
| 112 | + * @param string $col |
|
| 113 | + * @return bool |
|
| 114 | + */ |
|
| 115 | + public function isColNameValid($col) { |
|
| 116 | + switch ($col) { |
|
| 117 | + case 'ldap_dn': |
|
| 118 | + case 'ldap_dn_hash': |
|
| 119 | + case 'owncloud_name': |
|
| 120 | + case 'directory_uuid': |
|
| 121 | + return true; |
|
| 122 | + default: |
|
| 123 | + return false; |
|
| 124 | + } |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * Gets the value of one column based on a provided value of another column |
|
| 129 | + * |
|
| 130 | + * @param string $fetchCol |
|
| 131 | + * @param string $compareCol |
|
| 132 | + * @param string $search |
|
| 133 | + * @return string|false |
|
| 134 | + * @throws \Exception |
|
| 135 | + */ |
|
| 136 | + protected function getXbyY($fetchCol, $compareCol, $search) { |
|
| 137 | + if (!$this->isColNameValid($fetchCol)) { |
|
| 138 | + //this is used internally only, but we don't want to risk |
|
| 139 | + //having SQL injection at all. |
|
| 140 | + throw new \Exception('Invalid Column Name'); |
|
| 141 | + } |
|
| 142 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 143 | + $qb->select($fetchCol) |
|
| 144 | + ->from($this->getTableName()) |
|
| 145 | + ->where($qb->expr()->eq($compareCol, $qb->createNamedParameter($search))); |
|
| 146 | + |
|
| 147 | + try { |
|
| 148 | + $res = $qb->executeQuery(); |
|
| 149 | + $data = $res->fetchOne(); |
|
| 150 | + $res->closeCursor(); |
|
| 151 | + return $data; |
|
| 152 | + } catch (Exception $e) { |
|
| 153 | + return false; |
|
| 154 | + } |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + /** |
|
| 158 | + * Performs a DELETE or UPDATE query to the database. |
|
| 159 | + * |
|
| 160 | + * @param IPreparedStatement $statement |
|
| 161 | + * @param array $parameters |
|
| 162 | + * @return bool true if at least one row was modified, false otherwise |
|
| 163 | + */ |
|
| 164 | + protected function modify(IPreparedStatement $statement, $parameters) { |
|
| 165 | + try { |
|
| 166 | + $result = $statement->execute($parameters); |
|
| 167 | + $updated = $result->rowCount() > 0; |
|
| 168 | + $result->closeCursor(); |
|
| 169 | + return $updated; |
|
| 170 | + } catch (Exception $e) { |
|
| 171 | + return false; |
|
| 172 | + } |
|
| 173 | + } |
|
| 174 | + |
|
| 175 | + /** |
|
| 176 | + * Gets the LDAP DN based on the provided name. |
|
| 177 | + */ |
|
| 178 | + public function getDNByName(string $name): string|false { |
|
| 179 | + $dn = array_search($name, $this->cache, true); |
|
| 180 | + if ($dn === false) { |
|
| 181 | + $dn = $this->localNameToDnCache?->get($name); |
|
| 182 | + if ($dn === null) { |
|
| 183 | + $dn = $this->getXbyY('ldap_dn', 'owncloud_name', $name); |
|
| 184 | + if ($dn !== false) { |
|
| 185 | + $this->cache[$dn] = $name; |
|
| 186 | + } |
|
| 187 | + $this->localNameToDnCache?->set($name, $dn, self::LOCAL_CACHE_TTL); |
|
| 188 | + } |
|
| 189 | + } |
|
| 190 | + return $dn ?? false; |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * Updates the DN based on the given UUID |
|
| 195 | + * |
|
| 196 | + * @param string $fdn |
|
| 197 | + * @param string $uuid |
|
| 198 | + * @return bool |
|
| 199 | + */ |
|
| 200 | + public function setDNbyUUID($fdn, $uuid) { |
|
| 201 | + $oldDn = $this->getDnByUUID($uuid); |
|
| 202 | + $statement = $this->dbc->prepare(' |
|
| 203 | 203 | UPDATE `' . $this->getTableName() . '` |
| 204 | 204 | SET `ldap_dn_hash` = ?, `ldap_dn` = ? |
| 205 | 205 | WHERE `directory_uuid` = ? |
| 206 | 206 | '); |
| 207 | 207 | |
| 208 | - $r = $this->modify($statement, [$this->getDNHash($fdn), $fdn, $uuid]); |
|
| 209 | - if ($r) { |
|
| 210 | - if (is_string($oldDn) && isset($this->cache[$oldDn])) { |
|
| 211 | - $userId = $this->cache[$oldDn]; |
|
| 212 | - } |
|
| 213 | - $userId = $userId ?? $this->getNameByUUID($uuid); |
|
| 214 | - if ($userId) { |
|
| 215 | - $this->cache[$fdn] = $userId; |
|
| 216 | - $this->localNameToDnCache?->set($userId, $fdn, self::LOCAL_CACHE_TTL); |
|
| 217 | - } |
|
| 218 | - unset($this->cache[$oldDn]); |
|
| 219 | - } |
|
| 220 | - |
|
| 221 | - return $r; |
|
| 222 | - } |
|
| 223 | - |
|
| 224 | - /** |
|
| 225 | - * Updates the UUID based on the given DN |
|
| 226 | - * |
|
| 227 | - * required by Migration/UUIDFix |
|
| 228 | - * |
|
| 229 | - * @param $uuid |
|
| 230 | - * @param $fdn |
|
| 231 | - * @return bool |
|
| 232 | - */ |
|
| 233 | - public function setUUIDbyDN($uuid, $fdn): bool { |
|
| 234 | - $statement = $this->dbc->prepare(' |
|
| 208 | + $r = $this->modify($statement, [$this->getDNHash($fdn), $fdn, $uuid]); |
|
| 209 | + if ($r) { |
|
| 210 | + if (is_string($oldDn) && isset($this->cache[$oldDn])) { |
|
| 211 | + $userId = $this->cache[$oldDn]; |
|
| 212 | + } |
|
| 213 | + $userId = $userId ?? $this->getNameByUUID($uuid); |
|
| 214 | + if ($userId) { |
|
| 215 | + $this->cache[$fdn] = $userId; |
|
| 216 | + $this->localNameToDnCache?->set($userId, $fdn, self::LOCAL_CACHE_TTL); |
|
| 217 | + } |
|
| 218 | + unset($this->cache[$oldDn]); |
|
| 219 | + } |
|
| 220 | + |
|
| 221 | + return $r; |
|
| 222 | + } |
|
| 223 | + |
|
| 224 | + /** |
|
| 225 | + * Updates the UUID based on the given DN |
|
| 226 | + * |
|
| 227 | + * required by Migration/UUIDFix |
|
| 228 | + * |
|
| 229 | + * @param $uuid |
|
| 230 | + * @param $fdn |
|
| 231 | + * @return bool |
|
| 232 | + */ |
|
| 233 | + public function setUUIDbyDN($uuid, $fdn): bool { |
|
| 234 | + $statement = $this->dbc->prepare(' |
|
| 235 | 235 | UPDATE `' . $this->getTableName() . '` |
| 236 | 236 | SET `directory_uuid` = ? |
| 237 | 237 | WHERE `ldap_dn_hash` = ? |
| 238 | 238 | '); |
| 239 | 239 | |
| 240 | - unset($this->cache[$fdn]); |
|
| 241 | - |
|
| 242 | - return $this->modify($statement, [$uuid, $this->getDNHash($fdn)]); |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - /** |
|
| 246 | - * Get the hash to store in database column ldap_dn_hash for a given dn |
|
| 247 | - */ |
|
| 248 | - protected function getDNHash(string $fdn): string { |
|
| 249 | - return hash('sha256', $fdn, false); |
|
| 250 | - } |
|
| 251 | - |
|
| 252 | - /** |
|
| 253 | - * Gets the name based on the provided LDAP DN. |
|
| 254 | - * |
|
| 255 | - * @param string $fdn |
|
| 256 | - * @return string|false |
|
| 257 | - */ |
|
| 258 | - public function getNameByDN($fdn) { |
|
| 259 | - if (!isset($this->cache[$fdn])) { |
|
| 260 | - $this->cache[$fdn] = $this->getXbyY('owncloud_name', 'ldap_dn_hash', $this->getDNHash($fdn)); |
|
| 261 | - } |
|
| 262 | - return $this->cache[$fdn]; |
|
| 263 | - } |
|
| 264 | - |
|
| 265 | - /** |
|
| 266 | - * @param array<string> $hashList |
|
| 267 | - */ |
|
| 268 | - protected function prepareListOfIdsQuery(array $hashList): IQueryBuilder { |
|
| 269 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 270 | - $qb->select('owncloud_name', 'ldap_dn_hash', 'ldap_dn') |
|
| 271 | - ->from($this->getTableName(false)) |
|
| 272 | - ->where($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($hashList, IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 273 | - return $qb; |
|
| 274 | - } |
|
| 275 | - |
|
| 276 | - protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$results): void { |
|
| 277 | - $stmt = $qb->executeQuery(); |
|
| 278 | - while ($entry = $stmt->fetchAssociative()) { |
|
| 279 | - $results[$entry['ldap_dn']] = $entry['owncloud_name']; |
|
| 280 | - $this->cache[$entry['ldap_dn']] = $entry['owncloud_name']; |
|
| 281 | - } |
|
| 282 | - $stmt->closeCursor(); |
|
| 283 | - } |
|
| 284 | - |
|
| 285 | - /** |
|
| 286 | - * @param array<string> $fdns |
|
| 287 | - * @return array<string,string> |
|
| 288 | - */ |
|
| 289 | - public function getListOfIdsByDn(array $fdns): array { |
|
| 290 | - $totalDBParamLimit = 65000; |
|
| 291 | - $sliceSize = 1000; |
|
| 292 | - $maxSlices = $this->dbc->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE ? 9 : $totalDBParamLimit / $sliceSize; |
|
| 293 | - $results = []; |
|
| 294 | - |
|
| 295 | - $slice = 1; |
|
| 296 | - $fdns = array_map([$this, 'getDNHash'], $fdns); |
|
| 297 | - $fdnsSlice = count($fdns) > $sliceSize ? array_slice($fdns, 0, $sliceSize) : $fdns; |
|
| 298 | - $qb = $this->prepareListOfIdsQuery($fdnsSlice); |
|
| 299 | - |
|
| 300 | - while (isset($fdnsSlice[999])) { |
|
| 301 | - // Oracle does not allow more than 1000 values in the IN list, |
|
| 302 | - // but allows slicing |
|
| 303 | - $slice++; |
|
| 304 | - $fdnsSlice = array_slice($fdns, $sliceSize * ($slice - 1), $sliceSize); |
|
| 305 | - |
|
| 306 | - /** @see https://github.com/vimeo/psalm/issues/4995 */ |
|
| 307 | - /** @psalm-suppress TypeDoesNotContainType */ |
|
| 308 | - if (!isset($qb)) { |
|
| 309 | - $qb = $this->prepareListOfIdsQuery($fdnsSlice); |
|
| 310 | - continue; |
|
| 311 | - } |
|
| 312 | - |
|
| 313 | - if (!empty($fdnsSlice)) { |
|
| 314 | - $qb->orWhere($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($fdnsSlice, IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 315 | - } |
|
| 316 | - |
|
| 317 | - if ($slice % $maxSlices === 0) { |
|
| 318 | - $this->collectResultsFromListOfIdsQuery($qb, $results); |
|
| 319 | - unset($qb); |
|
| 320 | - } |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - if (isset($qb)) { |
|
| 324 | - $this->collectResultsFromListOfIdsQuery($qb, $results); |
|
| 325 | - } |
|
| 326 | - |
|
| 327 | - return $results; |
|
| 328 | - } |
|
| 329 | - |
|
| 330 | - /** |
|
| 331 | - * Searches mapped names by the giving string in the name column |
|
| 332 | - * |
|
| 333 | - * @return string[] |
|
| 334 | - */ |
|
| 335 | - public function getNamesBySearch(string $search, string $prefixMatch = '', string $postfixMatch = ''): array { |
|
| 336 | - $statement = $this->dbc->prepare(' |
|
| 240 | + unset($this->cache[$fdn]); |
|
| 241 | + |
|
| 242 | + return $this->modify($statement, [$uuid, $this->getDNHash($fdn)]); |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + /** |
|
| 246 | + * Get the hash to store in database column ldap_dn_hash for a given dn |
|
| 247 | + */ |
|
| 248 | + protected function getDNHash(string $fdn): string { |
|
| 249 | + return hash('sha256', $fdn, false); |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + /** |
|
| 253 | + * Gets the name based on the provided LDAP DN. |
|
| 254 | + * |
|
| 255 | + * @param string $fdn |
|
| 256 | + * @return string|false |
|
| 257 | + */ |
|
| 258 | + public function getNameByDN($fdn) { |
|
| 259 | + if (!isset($this->cache[$fdn])) { |
|
| 260 | + $this->cache[$fdn] = $this->getXbyY('owncloud_name', 'ldap_dn_hash', $this->getDNHash($fdn)); |
|
| 261 | + } |
|
| 262 | + return $this->cache[$fdn]; |
|
| 263 | + } |
|
| 264 | + |
|
| 265 | + /** |
|
| 266 | + * @param array<string> $hashList |
|
| 267 | + */ |
|
| 268 | + protected function prepareListOfIdsQuery(array $hashList): IQueryBuilder { |
|
| 269 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 270 | + $qb->select('owncloud_name', 'ldap_dn_hash', 'ldap_dn') |
|
| 271 | + ->from($this->getTableName(false)) |
|
| 272 | + ->where($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($hashList, IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 273 | + return $qb; |
|
| 274 | + } |
|
| 275 | + |
|
| 276 | + protected function collectResultsFromListOfIdsQuery(IQueryBuilder $qb, array &$results): void { |
|
| 277 | + $stmt = $qb->executeQuery(); |
|
| 278 | + while ($entry = $stmt->fetchAssociative()) { |
|
| 279 | + $results[$entry['ldap_dn']] = $entry['owncloud_name']; |
|
| 280 | + $this->cache[$entry['ldap_dn']] = $entry['owncloud_name']; |
|
| 281 | + } |
|
| 282 | + $stmt->closeCursor(); |
|
| 283 | + } |
|
| 284 | + |
|
| 285 | + /** |
|
| 286 | + * @param array<string> $fdns |
|
| 287 | + * @return array<string,string> |
|
| 288 | + */ |
|
| 289 | + public function getListOfIdsByDn(array $fdns): array { |
|
| 290 | + $totalDBParamLimit = 65000; |
|
| 291 | + $sliceSize = 1000; |
|
| 292 | + $maxSlices = $this->dbc->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE ? 9 : $totalDBParamLimit / $sliceSize; |
|
| 293 | + $results = []; |
|
| 294 | + |
|
| 295 | + $slice = 1; |
|
| 296 | + $fdns = array_map([$this, 'getDNHash'], $fdns); |
|
| 297 | + $fdnsSlice = count($fdns) > $sliceSize ? array_slice($fdns, 0, $sliceSize) : $fdns; |
|
| 298 | + $qb = $this->prepareListOfIdsQuery($fdnsSlice); |
|
| 299 | + |
|
| 300 | + while (isset($fdnsSlice[999])) { |
|
| 301 | + // Oracle does not allow more than 1000 values in the IN list, |
|
| 302 | + // but allows slicing |
|
| 303 | + $slice++; |
|
| 304 | + $fdnsSlice = array_slice($fdns, $sliceSize * ($slice - 1), $sliceSize); |
|
| 305 | + |
|
| 306 | + /** @see https://github.com/vimeo/psalm/issues/4995 */ |
|
| 307 | + /** @psalm-suppress TypeDoesNotContainType */ |
|
| 308 | + if (!isset($qb)) { |
|
| 309 | + $qb = $this->prepareListOfIdsQuery($fdnsSlice); |
|
| 310 | + continue; |
|
| 311 | + } |
|
| 312 | + |
|
| 313 | + if (!empty($fdnsSlice)) { |
|
| 314 | + $qb->orWhere($qb->expr()->in('ldap_dn_hash', $qb->createNamedParameter($fdnsSlice, IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 315 | + } |
|
| 316 | + |
|
| 317 | + if ($slice % $maxSlices === 0) { |
|
| 318 | + $this->collectResultsFromListOfIdsQuery($qb, $results); |
|
| 319 | + unset($qb); |
|
| 320 | + } |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + if (isset($qb)) { |
|
| 324 | + $this->collectResultsFromListOfIdsQuery($qb, $results); |
|
| 325 | + } |
|
| 326 | + |
|
| 327 | + return $results; |
|
| 328 | + } |
|
| 329 | + |
|
| 330 | + /** |
|
| 331 | + * Searches mapped names by the giving string in the name column |
|
| 332 | + * |
|
| 333 | + * @return string[] |
|
| 334 | + */ |
|
| 335 | + public function getNamesBySearch(string $search, string $prefixMatch = '', string $postfixMatch = ''): array { |
|
| 336 | + $statement = $this->dbc->prepare(' |
|
| 337 | 337 | SELECT `owncloud_name` |
| 338 | 338 | FROM `' . $this->getTableName() . '` |
| 339 | 339 | WHERE `owncloud_name` LIKE ? |
| 340 | 340 | '); |
| 341 | 341 | |
| 342 | - try { |
|
| 343 | - $res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]); |
|
| 344 | - } catch (Exception $e) { |
|
| 345 | - return []; |
|
| 346 | - } |
|
| 347 | - $names = []; |
|
| 348 | - while ($row = $res->fetchAssociative()) { |
|
| 349 | - $names[] = $row['owncloud_name']; |
|
| 350 | - } |
|
| 351 | - return $names; |
|
| 352 | - } |
|
| 353 | - |
|
| 354 | - /** |
|
| 355 | - * Gets the name based on the provided LDAP UUID. |
|
| 356 | - * |
|
| 357 | - * @param string $uuid |
|
| 358 | - * @return string|false |
|
| 359 | - */ |
|
| 360 | - public function getNameByUUID($uuid) { |
|
| 361 | - return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid); |
|
| 362 | - } |
|
| 363 | - |
|
| 364 | - public function getDnByUUID($uuid) { |
|
| 365 | - return $this->getXbyY('ldap_dn', 'directory_uuid', $uuid); |
|
| 366 | - } |
|
| 367 | - |
|
| 368 | - /** |
|
| 369 | - * Gets the UUID based on the provided LDAP DN |
|
| 370 | - * |
|
| 371 | - * @param string $dn |
|
| 372 | - * @return false|string |
|
| 373 | - * @throws \Exception |
|
| 374 | - */ |
|
| 375 | - public function getUUIDByDN($dn) { |
|
| 376 | - return $this->getXbyY('directory_uuid', 'ldap_dn_hash', $this->getDNHash($dn)); |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - public function getList(int $offset = 0, ?int $limit = null, bool $invalidatedOnly = false): array { |
|
| 380 | - $select = $this->dbc->getQueryBuilder(); |
|
| 381 | - $select->selectAlias('ldap_dn', 'dn') |
|
| 382 | - ->selectAlias('owncloud_name', 'name') |
|
| 383 | - ->selectAlias('directory_uuid', 'uuid') |
|
| 384 | - ->from($this->getTableName()) |
|
| 385 | - ->setMaxResults($limit) |
|
| 386 | - ->setFirstResult($offset); |
|
| 387 | - |
|
| 388 | - if ($invalidatedOnly) { |
|
| 389 | - $select->where($select->expr()->like('directory_uuid', $select->createNamedParameter('invalidated_%'))); |
|
| 390 | - } |
|
| 391 | - |
|
| 392 | - $result = $select->executeQuery(); |
|
| 393 | - $entries = $result->fetchAllAssociative(); |
|
| 394 | - $result->closeCursor(); |
|
| 395 | - |
|
| 396 | - return $entries; |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - /** |
|
| 400 | - * attempts to map the given entry |
|
| 401 | - * |
|
| 402 | - * @param string $fdn fully distinguished name (from LDAP) |
|
| 403 | - * @param string $name |
|
| 404 | - * @param string $uuid a unique identifier as used in LDAP |
|
| 405 | - * @return bool |
|
| 406 | - */ |
|
| 407 | - public function map($fdn, $name, $uuid) { |
|
| 408 | - if (mb_strlen($fdn) > 4000) { |
|
| 409 | - Server::get(LoggerInterface::class)->error( |
|
| 410 | - 'Cannot map, because the DN exceeds 4000 characters: {dn}', |
|
| 411 | - [ |
|
| 412 | - 'app' => 'user_ldap', |
|
| 413 | - 'dn' => $fdn, |
|
| 414 | - ] |
|
| 415 | - ); |
|
| 416 | - return false; |
|
| 417 | - } |
|
| 418 | - |
|
| 419 | - $row = [ |
|
| 420 | - 'ldap_dn_hash' => $this->getDNHash($fdn), |
|
| 421 | - 'ldap_dn' => $fdn, |
|
| 422 | - 'owncloud_name' => $name, |
|
| 423 | - 'directory_uuid' => $uuid |
|
| 424 | - ]; |
|
| 425 | - |
|
| 426 | - try { |
|
| 427 | - $result = $this->dbc->insertIfNotExist($this->getTableName(), $row); |
|
| 428 | - if ((bool)$result === true) { |
|
| 429 | - $this->cache[$fdn] = $name; |
|
| 430 | - $this->localNameToDnCache?->set($name, $fdn, self::LOCAL_CACHE_TTL); |
|
| 431 | - } |
|
| 432 | - // insertIfNotExist returns values as int |
|
| 433 | - return (bool)$result; |
|
| 434 | - } catch (\Exception $e) { |
|
| 435 | - return false; |
|
| 436 | - } |
|
| 437 | - } |
|
| 438 | - |
|
| 439 | - /** |
|
| 440 | - * removes a mapping based on the owncloud_name of the entry |
|
| 441 | - * |
|
| 442 | - * @param string $name |
|
| 443 | - * @return bool |
|
| 444 | - */ |
|
| 445 | - public function unmap($name) { |
|
| 446 | - $statement = $this->dbc->prepare(' |
|
| 342 | + try { |
|
| 343 | + $res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]); |
|
| 344 | + } catch (Exception $e) { |
|
| 345 | + return []; |
|
| 346 | + } |
|
| 347 | + $names = []; |
|
| 348 | + while ($row = $res->fetchAssociative()) { |
|
| 349 | + $names[] = $row['owncloud_name']; |
|
| 350 | + } |
|
| 351 | + return $names; |
|
| 352 | + } |
|
| 353 | + |
|
| 354 | + /** |
|
| 355 | + * Gets the name based on the provided LDAP UUID. |
|
| 356 | + * |
|
| 357 | + * @param string $uuid |
|
| 358 | + * @return string|false |
|
| 359 | + */ |
|
| 360 | + public function getNameByUUID($uuid) { |
|
| 361 | + return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid); |
|
| 362 | + } |
|
| 363 | + |
|
| 364 | + public function getDnByUUID($uuid) { |
|
| 365 | + return $this->getXbyY('ldap_dn', 'directory_uuid', $uuid); |
|
| 366 | + } |
|
| 367 | + |
|
| 368 | + /** |
|
| 369 | + * Gets the UUID based on the provided LDAP DN |
|
| 370 | + * |
|
| 371 | + * @param string $dn |
|
| 372 | + * @return false|string |
|
| 373 | + * @throws \Exception |
|
| 374 | + */ |
|
| 375 | + public function getUUIDByDN($dn) { |
|
| 376 | + return $this->getXbyY('directory_uuid', 'ldap_dn_hash', $this->getDNHash($dn)); |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + public function getList(int $offset = 0, ?int $limit = null, bool $invalidatedOnly = false): array { |
|
| 380 | + $select = $this->dbc->getQueryBuilder(); |
|
| 381 | + $select->selectAlias('ldap_dn', 'dn') |
|
| 382 | + ->selectAlias('owncloud_name', 'name') |
|
| 383 | + ->selectAlias('directory_uuid', 'uuid') |
|
| 384 | + ->from($this->getTableName()) |
|
| 385 | + ->setMaxResults($limit) |
|
| 386 | + ->setFirstResult($offset); |
|
| 387 | + |
|
| 388 | + if ($invalidatedOnly) { |
|
| 389 | + $select->where($select->expr()->like('directory_uuid', $select->createNamedParameter('invalidated_%'))); |
|
| 390 | + } |
|
| 391 | + |
|
| 392 | + $result = $select->executeQuery(); |
|
| 393 | + $entries = $result->fetchAllAssociative(); |
|
| 394 | + $result->closeCursor(); |
|
| 395 | + |
|
| 396 | + return $entries; |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + /** |
|
| 400 | + * attempts to map the given entry |
|
| 401 | + * |
|
| 402 | + * @param string $fdn fully distinguished name (from LDAP) |
|
| 403 | + * @param string $name |
|
| 404 | + * @param string $uuid a unique identifier as used in LDAP |
|
| 405 | + * @return bool |
|
| 406 | + */ |
|
| 407 | + public function map($fdn, $name, $uuid) { |
|
| 408 | + if (mb_strlen($fdn) > 4000) { |
|
| 409 | + Server::get(LoggerInterface::class)->error( |
|
| 410 | + 'Cannot map, because the DN exceeds 4000 characters: {dn}', |
|
| 411 | + [ |
|
| 412 | + 'app' => 'user_ldap', |
|
| 413 | + 'dn' => $fdn, |
|
| 414 | + ] |
|
| 415 | + ); |
|
| 416 | + return false; |
|
| 417 | + } |
|
| 418 | + |
|
| 419 | + $row = [ |
|
| 420 | + 'ldap_dn_hash' => $this->getDNHash($fdn), |
|
| 421 | + 'ldap_dn' => $fdn, |
|
| 422 | + 'owncloud_name' => $name, |
|
| 423 | + 'directory_uuid' => $uuid |
|
| 424 | + ]; |
|
| 425 | + |
|
| 426 | + try { |
|
| 427 | + $result = $this->dbc->insertIfNotExist($this->getTableName(), $row); |
|
| 428 | + if ((bool)$result === true) { |
|
| 429 | + $this->cache[$fdn] = $name; |
|
| 430 | + $this->localNameToDnCache?->set($name, $fdn, self::LOCAL_CACHE_TTL); |
|
| 431 | + } |
|
| 432 | + // insertIfNotExist returns values as int |
|
| 433 | + return (bool)$result; |
|
| 434 | + } catch (\Exception $e) { |
|
| 435 | + return false; |
|
| 436 | + } |
|
| 437 | + } |
|
| 438 | + |
|
| 439 | + /** |
|
| 440 | + * removes a mapping based on the owncloud_name of the entry |
|
| 441 | + * |
|
| 442 | + * @param string $name |
|
| 443 | + * @return bool |
|
| 444 | + */ |
|
| 445 | + public function unmap($name) { |
|
| 446 | + $statement = $this->dbc->prepare(' |
|
| 447 | 447 | DELETE FROM `' . $this->getTableName() . '` |
| 448 | 448 | WHERE `owncloud_name` = ?'); |
| 449 | 449 | |
| 450 | - $dn = array_search($name, $this->cache); |
|
| 451 | - if ($dn !== false) { |
|
| 452 | - unset($this->cache[$dn]); |
|
| 453 | - } |
|
| 454 | - $this->localNameToDnCache?->remove($name); |
|
| 455 | - |
|
| 456 | - return $this->modify($statement, [$name]); |
|
| 457 | - } |
|
| 458 | - |
|
| 459 | - /** |
|
| 460 | - * Truncates the mapping table |
|
| 461 | - * |
|
| 462 | - * @return bool |
|
| 463 | - */ |
|
| 464 | - public function clear() { |
|
| 465 | - $sql = $this->dbc |
|
| 466 | - ->getDatabasePlatform() |
|
| 467 | - ->getTruncateTableSQL('`' . $this->getTableName() . '`'); |
|
| 468 | - try { |
|
| 469 | - $this->dbc->executeQuery($sql); |
|
| 470 | - $this->localNameToDnCache?->clear(); |
|
| 471 | - |
|
| 472 | - return true; |
|
| 473 | - } catch (Exception $e) { |
|
| 474 | - return false; |
|
| 475 | - } |
|
| 476 | - } |
|
| 477 | - |
|
| 478 | - /** |
|
| 479 | - * clears the mapping table one by one and executing a callback with |
|
| 480 | - * each row's id (=owncloud_name col) |
|
| 481 | - * |
|
| 482 | - * @param callable $preCallback |
|
| 483 | - * @param callable $postCallback |
|
| 484 | - * @return bool true on success, false when at least one row was not |
|
| 485 | - * deleted |
|
| 486 | - */ |
|
| 487 | - public function clearCb(callable $preCallback, callable $postCallback): bool { |
|
| 488 | - $picker = $this->dbc->getQueryBuilder(); |
|
| 489 | - $picker->select('owncloud_name') |
|
| 490 | - ->from($this->getTableName()); |
|
| 491 | - $cursor = $picker->executeQuery(); |
|
| 492 | - $result = true; |
|
| 493 | - while (($id = $cursor->fetchOne()) !== false) { |
|
| 494 | - $preCallback($id); |
|
| 495 | - if ($isUnmapped = $this->unmap($id)) { |
|
| 496 | - $postCallback($id); |
|
| 497 | - } |
|
| 498 | - $result = $result && $isUnmapped; |
|
| 499 | - } |
|
| 500 | - $cursor->closeCursor(); |
|
| 501 | - return $result; |
|
| 502 | - } |
|
| 503 | - |
|
| 504 | - /** |
|
| 505 | - * returns the number of entries in the mappings table |
|
| 506 | - * |
|
| 507 | - * @return int |
|
| 508 | - */ |
|
| 509 | - public function count(): int { |
|
| 510 | - $query = $this->dbc->getQueryBuilder(); |
|
| 511 | - $query->select($query->func()->count('ldap_dn_hash')) |
|
| 512 | - ->from($this->getTableName()); |
|
| 513 | - $res = $query->executeQuery(); |
|
| 514 | - $count = $res->fetchOne(); |
|
| 515 | - $res->closeCursor(); |
|
| 516 | - return (int)$count; |
|
| 517 | - } |
|
| 518 | - |
|
| 519 | - public function countInvalidated(): int { |
|
| 520 | - $query = $this->dbc->getQueryBuilder(); |
|
| 521 | - $query->select($query->func()->count('ldap_dn_hash')) |
|
| 522 | - ->from($this->getTableName()) |
|
| 523 | - ->where($query->expr()->like('directory_uuid', $query->createNamedParameter('invalidated_%'))); |
|
| 524 | - $res = $query->executeQuery(); |
|
| 525 | - $count = $res->fetchOne(); |
|
| 526 | - $res->closeCursor(); |
|
| 527 | - return (int)$count; |
|
| 528 | - } |
|
| 450 | + $dn = array_search($name, $this->cache); |
|
| 451 | + if ($dn !== false) { |
|
| 452 | + unset($this->cache[$dn]); |
|
| 453 | + } |
|
| 454 | + $this->localNameToDnCache?->remove($name); |
|
| 455 | + |
|
| 456 | + return $this->modify($statement, [$name]); |
|
| 457 | + } |
|
| 458 | + |
|
| 459 | + /** |
|
| 460 | + * Truncates the mapping table |
|
| 461 | + * |
|
| 462 | + * @return bool |
|
| 463 | + */ |
|
| 464 | + public function clear() { |
|
| 465 | + $sql = $this->dbc |
|
| 466 | + ->getDatabasePlatform() |
|
| 467 | + ->getTruncateTableSQL('`' . $this->getTableName() . '`'); |
|
| 468 | + try { |
|
| 469 | + $this->dbc->executeQuery($sql); |
|
| 470 | + $this->localNameToDnCache?->clear(); |
|
| 471 | + |
|
| 472 | + return true; |
|
| 473 | + } catch (Exception $e) { |
|
| 474 | + return false; |
|
| 475 | + } |
|
| 476 | + } |
|
| 477 | + |
|
| 478 | + /** |
|
| 479 | + * clears the mapping table one by one and executing a callback with |
|
| 480 | + * each row's id (=owncloud_name col) |
|
| 481 | + * |
|
| 482 | + * @param callable $preCallback |
|
| 483 | + * @param callable $postCallback |
|
| 484 | + * @return bool true on success, false when at least one row was not |
|
| 485 | + * deleted |
|
| 486 | + */ |
|
| 487 | + public function clearCb(callable $preCallback, callable $postCallback): bool { |
|
| 488 | + $picker = $this->dbc->getQueryBuilder(); |
|
| 489 | + $picker->select('owncloud_name') |
|
| 490 | + ->from($this->getTableName()); |
|
| 491 | + $cursor = $picker->executeQuery(); |
|
| 492 | + $result = true; |
|
| 493 | + while (($id = $cursor->fetchOne()) !== false) { |
|
| 494 | + $preCallback($id); |
|
| 495 | + if ($isUnmapped = $this->unmap($id)) { |
|
| 496 | + $postCallback($id); |
|
| 497 | + } |
|
| 498 | + $result = $result && $isUnmapped; |
|
| 499 | + } |
|
| 500 | + $cursor->closeCursor(); |
|
| 501 | + return $result; |
|
| 502 | + } |
|
| 503 | + |
|
| 504 | + /** |
|
| 505 | + * returns the number of entries in the mappings table |
|
| 506 | + * |
|
| 507 | + * @return int |
|
| 508 | + */ |
|
| 509 | + public function count(): int { |
|
| 510 | + $query = $this->dbc->getQueryBuilder(); |
|
| 511 | + $query->select($query->func()->count('ldap_dn_hash')) |
|
| 512 | + ->from($this->getTableName()); |
|
| 513 | + $res = $query->executeQuery(); |
|
| 514 | + $count = $res->fetchOne(); |
|
| 515 | + $res->closeCursor(); |
|
| 516 | + return (int)$count; |
|
| 517 | + } |
|
| 518 | + |
|
| 519 | + public function countInvalidated(): int { |
|
| 520 | + $query = $this->dbc->getQueryBuilder(); |
|
| 521 | + $query->select($query->func()->count('ldap_dn_hash')) |
|
| 522 | + ->from($this->getTableName()) |
|
| 523 | + ->where($query->expr()->like('directory_uuid', $query->createNamedParameter('invalidated_%'))); |
|
| 524 | + $res = $query->executeQuery(); |
|
| 525 | + $count = $res->fetchOne(); |
|
| 526 | + $res->closeCursor(); |
|
| 527 | + return (int)$count; |
|
| 528 | + } |
|
| 529 | 529 | } |
@@ -78,7 +78,7 @@ discard block |
||
| 78 | 78 | } |
| 79 | 79 | |
| 80 | 80 | $section = \str_contains($this->getTableName(), 'user') ? 'u/' : 'g/'; |
| 81 | - $this->localNameToDnCache = $this->cacheFactory->createLocal('ldap/map/' . $section); |
|
| 81 | + $this->localNameToDnCache = $this->cacheFactory->createLocal('ldap/map/'.$section); |
|
| 82 | 82 | |
| 83 | 83 | // We use the cache as well to store whether it shall be used. If the |
| 84 | 84 | // answer was no, we unset it again. |
@@ -101,7 +101,7 @@ discard block |
||
| 101 | 101 | $row = $result->fetchAssociative(); |
| 102 | 102 | $result->closeCursor(); |
| 103 | 103 | |
| 104 | - $use = (int)$row['count'] <= self::LOCAL_CACHE_OBJECT_THRESHOLD; |
|
| 104 | + $use = (int) $row['count'] <= self::LOCAL_CACHE_OBJECT_THRESHOLD; |
|
| 105 | 105 | $this->localNameToDnCache->set('use', $use, self::LOCAL_USER_COUNT_TTL); |
| 106 | 106 | return $use; |
| 107 | 107 | } |
@@ -175,7 +175,7 @@ discard block |
||
| 175 | 175 | /** |
| 176 | 176 | * Gets the LDAP DN based on the provided name. |
| 177 | 177 | */ |
| 178 | - public function getDNByName(string $name): string|false { |
|
| 178 | + public function getDNByName(string $name): string | false { |
|
| 179 | 179 | $dn = array_search($name, $this->cache, true); |
| 180 | 180 | if ($dn === false) { |
| 181 | 181 | $dn = $this->localNameToDnCache?->get($name); |
@@ -200,7 +200,7 @@ discard block |
||
| 200 | 200 | public function setDNbyUUID($fdn, $uuid) { |
| 201 | 201 | $oldDn = $this->getDnByUUID($uuid); |
| 202 | 202 | $statement = $this->dbc->prepare(' |
| 203 | - UPDATE `' . $this->getTableName() . '` |
|
| 203 | + UPDATE `' . $this->getTableName().'` |
|
| 204 | 204 | SET `ldap_dn_hash` = ?, `ldap_dn` = ? |
| 205 | 205 | WHERE `directory_uuid` = ? |
| 206 | 206 | '); |
@@ -232,7 +232,7 @@ discard block |
||
| 232 | 232 | */ |
| 233 | 233 | public function setUUIDbyDN($uuid, $fdn): bool { |
| 234 | 234 | $statement = $this->dbc->prepare(' |
| 235 | - UPDATE `' . $this->getTableName() . '` |
|
| 235 | + UPDATE `' . $this->getTableName().'` |
|
| 236 | 236 | SET `directory_uuid` = ? |
| 237 | 237 | WHERE `ldap_dn_hash` = ? |
| 238 | 238 | '); |
@@ -335,12 +335,12 @@ discard block |
||
| 335 | 335 | public function getNamesBySearch(string $search, string $prefixMatch = '', string $postfixMatch = ''): array { |
| 336 | 336 | $statement = $this->dbc->prepare(' |
| 337 | 337 | SELECT `owncloud_name` |
| 338 | - FROM `' . $this->getTableName() . '` |
|
| 338 | + FROM `' . $this->getTableName().'` |
|
| 339 | 339 | WHERE `owncloud_name` LIKE ? |
| 340 | 340 | '); |
| 341 | 341 | |
| 342 | 342 | try { |
| 343 | - $res = $statement->execute([$prefixMatch . $this->dbc->escapeLikeParameter($search) . $postfixMatch]); |
|
| 343 | + $res = $statement->execute([$prefixMatch.$this->dbc->escapeLikeParameter($search).$postfixMatch]); |
|
| 344 | 344 | } catch (Exception $e) { |
| 345 | 345 | return []; |
| 346 | 346 | } |
@@ -425,12 +425,12 @@ discard block |
||
| 425 | 425 | |
| 426 | 426 | try { |
| 427 | 427 | $result = $this->dbc->insertIfNotExist($this->getTableName(), $row); |
| 428 | - if ((bool)$result === true) { |
|
| 428 | + if ((bool) $result === true) { |
|
| 429 | 429 | $this->cache[$fdn] = $name; |
| 430 | 430 | $this->localNameToDnCache?->set($name, $fdn, self::LOCAL_CACHE_TTL); |
| 431 | 431 | } |
| 432 | 432 | // insertIfNotExist returns values as int |
| 433 | - return (bool)$result; |
|
| 433 | + return (bool) $result; |
|
| 434 | 434 | } catch (\Exception $e) { |
| 435 | 435 | return false; |
| 436 | 436 | } |
@@ -444,7 +444,7 @@ discard block |
||
| 444 | 444 | */ |
| 445 | 445 | public function unmap($name) { |
| 446 | 446 | $statement = $this->dbc->prepare(' |
| 447 | - DELETE FROM `' . $this->getTableName() . '` |
|
| 447 | + DELETE FROM `' . $this->getTableName().'` |
|
| 448 | 448 | WHERE `owncloud_name` = ?'); |
| 449 | 449 | |
| 450 | 450 | $dn = array_search($name, $this->cache); |
@@ -464,7 +464,7 @@ discard block |
||
| 464 | 464 | public function clear() { |
| 465 | 465 | $sql = $this->dbc |
| 466 | 466 | ->getDatabasePlatform() |
| 467 | - ->getTruncateTableSQL('`' . $this->getTableName() . '`'); |
|
| 467 | + ->getTruncateTableSQL('`'.$this->getTableName().'`'); |
|
| 468 | 468 | try { |
| 469 | 469 | $this->dbc->executeQuery($sql); |
| 470 | 470 | $this->localNameToDnCache?->clear(); |
@@ -513,7 +513,7 @@ discard block |
||
| 513 | 513 | $res = $query->executeQuery(); |
| 514 | 514 | $count = $res->fetchOne(); |
| 515 | 515 | $res->closeCursor(); |
| 516 | - return (int)$count; |
|
| 516 | + return (int) $count; |
|
| 517 | 517 | } |
| 518 | 518 | |
| 519 | 519 | public function countInvalidated(): int { |
@@ -524,6 +524,6 @@ discard block |
||
| 524 | 524 | $res = $query->executeQuery(); |
| 525 | 525 | $count = $res->fetchOne(); |
| 526 | 526 | $res->closeCursor(); |
| 527 | - return (int)$count; |
|
| 527 | + return (int) $count; |
|
| 528 | 528 | } |
| 529 | 529 | } |
@@ -17,56 +17,56 @@ |
||
| 17 | 17 | * @template-extends QBMapper<GroupMembership> |
| 18 | 18 | */ |
| 19 | 19 | class GroupMembershipMapper extends QBMapper { |
| 20 | - public function __construct(IDBConnection $db) { |
|
| 21 | - parent::__construct($db, 'ldap_group_membership', GroupMembership::class); |
|
| 22 | - } |
|
| 20 | + public function __construct(IDBConnection $db) { |
|
| 21 | + parent::__construct($db, 'ldap_group_membership', GroupMembership::class); |
|
| 22 | + } |
|
| 23 | 23 | |
| 24 | - /** |
|
| 25 | - * @return string[] |
|
| 26 | - */ |
|
| 27 | - public function getKnownGroups(): array { |
|
| 28 | - $query = $this->db->getQueryBuilder(); |
|
| 29 | - $result = $query->selectDistinct('groupid') |
|
| 30 | - ->from($this->getTableName()) |
|
| 31 | - ->executeQuery(); |
|
| 24 | + /** |
|
| 25 | + * @return string[] |
|
| 26 | + */ |
|
| 27 | + public function getKnownGroups(): array { |
|
| 28 | + $query = $this->db->getQueryBuilder(); |
|
| 29 | + $result = $query->selectDistinct('groupid') |
|
| 30 | + ->from($this->getTableName()) |
|
| 31 | + ->executeQuery(); |
|
| 32 | 32 | |
| 33 | - $groups = array_column($result->fetchAllAssociative(), 'groupid'); |
|
| 34 | - $result->closeCursor(); |
|
| 35 | - return $groups; |
|
| 36 | - } |
|
| 33 | + $groups = array_column($result->fetchAllAssociative(), 'groupid'); |
|
| 34 | + $result->closeCursor(); |
|
| 35 | + return $groups; |
|
| 36 | + } |
|
| 37 | 37 | |
| 38 | - /** |
|
| 39 | - * @return GroupMembership[] |
|
| 40 | - */ |
|
| 41 | - public function findGroupMemberships(string $groupid): array { |
|
| 42 | - $qb = $this->db->getQueryBuilder(); |
|
| 43 | - $select = $qb->select('*') |
|
| 44 | - ->from($this->getTableName()) |
|
| 45 | - ->where($qb->expr()->eq('groupid', $qb->createNamedParameter($groupid))); |
|
| 38 | + /** |
|
| 39 | + * @return GroupMembership[] |
|
| 40 | + */ |
|
| 41 | + public function findGroupMemberships(string $groupid): array { |
|
| 42 | + $qb = $this->db->getQueryBuilder(); |
|
| 43 | + $select = $qb->select('*') |
|
| 44 | + ->from($this->getTableName()) |
|
| 45 | + ->where($qb->expr()->eq('groupid', $qb->createNamedParameter($groupid))); |
|
| 46 | 46 | |
| 47 | - return $this->findEntities($select); |
|
| 48 | - } |
|
| 47 | + return $this->findEntities($select); |
|
| 48 | + } |
|
| 49 | 49 | |
| 50 | - /** |
|
| 51 | - * @return GroupMembership[] |
|
| 52 | - */ |
|
| 53 | - public function findGroupMembershipsForUser(string $userid): array { |
|
| 54 | - $qb = $this->db->getQueryBuilder(); |
|
| 55 | - $select = $qb->select('*') |
|
| 56 | - ->from($this->getTableName()) |
|
| 57 | - ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userid))); |
|
| 50 | + /** |
|
| 51 | + * @return GroupMembership[] |
|
| 52 | + */ |
|
| 53 | + public function findGroupMembershipsForUser(string $userid): array { |
|
| 54 | + $qb = $this->db->getQueryBuilder(); |
|
| 55 | + $select = $qb->select('*') |
|
| 56 | + ->from($this->getTableName()) |
|
| 57 | + ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userid))); |
|
| 58 | 58 | |
| 59 | - return $this->findEntities($select); |
|
| 60 | - } |
|
| 59 | + return $this->findEntities($select); |
|
| 60 | + } |
|
| 61 | 61 | |
| 62 | - public function deleteGroups(array $removedGroups): void { |
|
| 63 | - $query = $this->db->getQueryBuilder(); |
|
| 64 | - $query->delete($this->getTableName()) |
|
| 65 | - ->where($query->expr()->in('groupid', $query->createParameter('groupids'))); |
|
| 62 | + public function deleteGroups(array $removedGroups): void { |
|
| 63 | + $query = $this->db->getQueryBuilder(); |
|
| 64 | + $query->delete($this->getTableName()) |
|
| 65 | + ->where($query->expr()->in('groupid', $query->createParameter('groupids'))); |
|
| 66 | 66 | |
| 67 | - foreach (array_chunk($removedGroups, 1000) as $removedGroupsChunk) { |
|
| 68 | - $query->setParameter('groupids', $removedGroupsChunk, IQueryBuilder::PARAM_STR_ARRAY); |
|
| 69 | - $query->executeStatement(); |
|
| 70 | - } |
|
| 71 | - } |
|
| 67 | + foreach (array_chunk($removedGroups, 1000) as $removedGroupsChunk) { |
|
| 68 | + $query->setParameter('groupids', $removedGroupsChunk, IQueryBuilder::PARAM_STR_ARRAY); |
|
| 69 | + $query->executeStatement(); |
|
| 70 | + } |
|
| 71 | + } |
|
| 72 | 72 | } |
@@ -17,92 +17,92 @@ |
||
| 17 | 17 | use OCP\Migration\SimpleMigrationStep; |
| 18 | 18 | |
| 19 | 19 | class Version1190Date20230706134108 extends SimpleMigrationStep { |
| 20 | - public function __construct( |
|
| 21 | - private IDBConnection $dbc, |
|
| 22 | - ) { |
|
| 23 | - } |
|
| 20 | + public function __construct( |
|
| 21 | + private IDBConnection $dbc, |
|
| 22 | + ) { |
|
| 23 | + } |
|
| 24 | 24 | |
| 25 | - public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 26 | - } |
|
| 25 | + public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 29 | - /** @var ISchemaWrapper $schema */ |
|
| 30 | - $schema = $schemaClosure(); |
|
| 28 | + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 29 | + /** @var ISchemaWrapper $schema */ |
|
| 30 | + $schema = $schemaClosure(); |
|
| 31 | 31 | |
| 32 | - if (!$schema->hasTable('ldap_group_membership')) { |
|
| 33 | - $table = $schema->createTable('ldap_group_membership'); |
|
| 34 | - $table->addColumn('id', Types::BIGINT, [ |
|
| 35 | - 'autoincrement' => true, |
|
| 36 | - 'notnull' => true, |
|
| 37 | - ]); |
|
| 38 | - $table->addColumn('groupid', Types::STRING, [ |
|
| 39 | - 'notnull' => true, |
|
| 40 | - 'length' => 255, |
|
| 41 | - 'default' => '', |
|
| 42 | - ]); |
|
| 43 | - $table->addColumn('userid', Types::STRING, [ |
|
| 44 | - 'notnull' => true, |
|
| 45 | - 'length' => 64, |
|
| 46 | - 'default' => '', |
|
| 47 | - ]); |
|
| 48 | - $table->setPrimaryKey(['id']); |
|
| 49 | - $table->addUniqueIndex(['groupid', 'userid'], 'user_ldap_membership_unique'); |
|
| 50 | - return $schema; |
|
| 51 | - } else { |
|
| 52 | - return null; |
|
| 53 | - } |
|
| 54 | - } |
|
| 32 | + if (!$schema->hasTable('ldap_group_membership')) { |
|
| 33 | + $table = $schema->createTable('ldap_group_membership'); |
|
| 34 | + $table->addColumn('id', Types::BIGINT, [ |
|
| 35 | + 'autoincrement' => true, |
|
| 36 | + 'notnull' => true, |
|
| 37 | + ]); |
|
| 38 | + $table->addColumn('groupid', Types::STRING, [ |
|
| 39 | + 'notnull' => true, |
|
| 40 | + 'length' => 255, |
|
| 41 | + 'default' => '', |
|
| 42 | + ]); |
|
| 43 | + $table->addColumn('userid', Types::STRING, [ |
|
| 44 | + 'notnull' => true, |
|
| 45 | + 'length' => 64, |
|
| 46 | + 'default' => '', |
|
| 47 | + ]); |
|
| 48 | + $table->setPrimaryKey(['id']); |
|
| 49 | + $table->addUniqueIndex(['groupid', 'userid'], 'user_ldap_membership_unique'); |
|
| 50 | + return $schema; |
|
| 51 | + } else { |
|
| 52 | + return null; |
|
| 53 | + } |
|
| 54 | + } |
|
| 55 | 55 | |
| 56 | - public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 57 | - /** @var ISchemaWrapper $schema */ |
|
| 58 | - $schema = $schemaClosure(); |
|
| 56 | + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 57 | + /** @var ISchemaWrapper $schema */ |
|
| 58 | + $schema = $schemaClosure(); |
|
| 59 | 59 | |
| 60 | - if (!$schema->hasTable('ldap_group_members')) { |
|
| 61 | - // Old table does not exist |
|
| 62 | - return; |
|
| 63 | - } |
|
| 60 | + if (!$schema->hasTable('ldap_group_members')) { |
|
| 61 | + // Old table does not exist |
|
| 62 | + return; |
|
| 63 | + } |
|
| 64 | 64 | |
| 65 | - $output->startProgress(); |
|
| 66 | - $this->copyGroupMembershipData(); |
|
| 67 | - $output->finishProgress(); |
|
| 68 | - } |
|
| 65 | + $output->startProgress(); |
|
| 66 | + $this->copyGroupMembershipData(); |
|
| 67 | + $output->finishProgress(); |
|
| 68 | + } |
|
| 69 | 69 | |
| 70 | - protected function copyGroupMembershipData(): void { |
|
| 71 | - $insert = $this->dbc->getQueryBuilder(); |
|
| 72 | - $insert->insert('ldap_group_membership') |
|
| 73 | - ->values([ |
|
| 74 | - 'userid' => $insert->createParameter('userid'), |
|
| 75 | - 'groupid' => $insert->createParameter('groupid'), |
|
| 76 | - ]); |
|
| 70 | + protected function copyGroupMembershipData(): void { |
|
| 71 | + $insert = $this->dbc->getQueryBuilder(); |
|
| 72 | + $insert->insert('ldap_group_membership') |
|
| 73 | + ->values([ |
|
| 74 | + 'userid' => $insert->createParameter('userid'), |
|
| 75 | + 'groupid' => $insert->createParameter('groupid'), |
|
| 76 | + ]); |
|
| 77 | 77 | |
| 78 | - $query = $this->dbc->getQueryBuilder(); |
|
| 79 | - $query->select('*') |
|
| 80 | - ->from('ldap_group_members'); |
|
| 78 | + $query = $this->dbc->getQueryBuilder(); |
|
| 79 | + $query->select('*') |
|
| 80 | + ->from('ldap_group_members'); |
|
| 81 | 81 | |
| 82 | - $result = $query->executeQuery(); |
|
| 83 | - while ($row = $result->fetchAssociative()) { |
|
| 84 | - $knownUsers = unserialize($row['owncloudusers']); |
|
| 85 | - if (!is_array($knownUsers)) { |
|
| 86 | - /* Unserialize failed or data was incorrect in database, ignore */ |
|
| 87 | - continue; |
|
| 88 | - } |
|
| 89 | - $knownUsers = array_unique($knownUsers); |
|
| 90 | - foreach ($knownUsers as $knownUser) { |
|
| 91 | - try { |
|
| 92 | - $insert |
|
| 93 | - ->setParameter('groupid', $row['owncloudname']) |
|
| 94 | - ->setParameter('userid', $knownUser) |
|
| 95 | - ; |
|
| 82 | + $result = $query->executeQuery(); |
|
| 83 | + while ($row = $result->fetchAssociative()) { |
|
| 84 | + $knownUsers = unserialize($row['owncloudusers']); |
|
| 85 | + if (!is_array($knownUsers)) { |
|
| 86 | + /* Unserialize failed or data was incorrect in database, ignore */ |
|
| 87 | + continue; |
|
| 88 | + } |
|
| 89 | + $knownUsers = array_unique($knownUsers); |
|
| 90 | + foreach ($knownUsers as $knownUser) { |
|
| 91 | + try { |
|
| 92 | + $insert |
|
| 93 | + ->setParameter('groupid', $row['owncloudname']) |
|
| 94 | + ->setParameter('userid', $knownUser) |
|
| 95 | + ; |
|
| 96 | 96 | |
| 97 | - $insert->executeStatement(); |
|
| 98 | - } catch (\OCP\DB\Exception $e) { |
|
| 99 | - /* |
|
| 97 | + $insert->executeStatement(); |
|
| 98 | + } catch (\OCP\DB\Exception $e) { |
|
| 99 | + /* |
|
| 100 | 100 | * If it fails on unique constaint violation it may just be left over value from previous half-migration |
| 101 | 101 | * If it fails on something else, ignore as well, data will be filled by background job later anyway |
| 102 | 102 | */ |
| 103 | - } |
|
| 104 | - } |
|
| 105 | - } |
|
| 106 | - $result->closeCursor(); |
|
| 107 | - } |
|
| 103 | + } |
|
| 104 | + } |
|
| 105 | + } |
|
| 106 | + $result->closeCursor(); |
|
| 107 | + } |
|
| 108 | 108 | } |
@@ -22,245 +22,245 @@ |
||
| 22 | 22 | |
| 23 | 23 | class Version1130Date20211102154716 extends SimpleMigrationStep { |
| 24 | 24 | |
| 25 | - /** @var string[] */ |
|
| 26 | - private $hashColumnAddedToTables = []; |
|
| 25 | + /** @var string[] */ |
|
| 26 | + private $hashColumnAddedToTables = []; |
|
| 27 | 27 | |
| 28 | - public function __construct( |
|
| 29 | - private IDBConnection $dbc, |
|
| 30 | - private LoggerInterface $logger, |
|
| 31 | - ) { |
|
| 32 | - } |
|
| 28 | + public function __construct( |
|
| 29 | + private IDBConnection $dbc, |
|
| 30 | + private LoggerInterface $logger, |
|
| 31 | + ) { |
|
| 32 | + } |
|
| 33 | 33 | |
| 34 | - public function getName() { |
|
| 35 | - return 'Adjust LDAP user and group ldap_dn column lengths and add ldap_dn_hash columns'; |
|
| 36 | - } |
|
| 34 | + public function getName() { |
|
| 35 | + return 'Adjust LDAP user and group ldap_dn column lengths and add ldap_dn_hash columns'; |
|
| 36 | + } |
|
| 37 | 37 | |
| 38 | - public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 39 | - foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 40 | - $this->processDuplicateUUIDs($tableName); |
|
| 41 | - } |
|
| 38 | + public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { |
|
| 39 | + foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 40 | + $this->processDuplicateUUIDs($tableName); |
|
| 41 | + } |
|
| 42 | 42 | |
| 43 | - /** @var ISchemaWrapper $schema */ |
|
| 44 | - $schema = $schemaClosure(); |
|
| 45 | - if ($schema->hasTable('ldap_group_mapping_backup')) { |
|
| 46 | - // Previous upgrades of a broken release might have left an incomplete |
|
| 47 | - // ldap_group_mapping_backup table. No need to recreate, but it |
|
| 48 | - // should be empty. |
|
| 49 | - // TRUNCATE is not available from Query Builder, but faster than DELETE FROM. |
|
| 50 | - $sql = $this->dbc->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*ldap_group_mapping_backup`', false); |
|
| 51 | - $this->dbc->executeStatement($sql); |
|
| 52 | - } |
|
| 53 | - } |
|
| 43 | + /** @var ISchemaWrapper $schema */ |
|
| 44 | + $schema = $schemaClosure(); |
|
| 45 | + if ($schema->hasTable('ldap_group_mapping_backup')) { |
|
| 46 | + // Previous upgrades of a broken release might have left an incomplete |
|
| 47 | + // ldap_group_mapping_backup table. No need to recreate, but it |
|
| 48 | + // should be empty. |
|
| 49 | + // TRUNCATE is not available from Query Builder, but faster than DELETE FROM. |
|
| 50 | + $sql = $this->dbc->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*ldap_group_mapping_backup`', false); |
|
| 51 | + $this->dbc->executeStatement($sql); |
|
| 52 | + } |
|
| 53 | + } |
|
| 54 | 54 | |
| 55 | - /** |
|
| 56 | - * @param IOutput $output |
|
| 57 | - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 58 | - * @param array $options |
|
| 59 | - * @return null|ISchemaWrapper |
|
| 60 | - */ |
|
| 61 | - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 62 | - /** @var ISchemaWrapper $schema */ |
|
| 63 | - $schema = $schemaClosure(); |
|
| 55 | + /** |
|
| 56 | + * @param IOutput $output |
|
| 57 | + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 58 | + * @param array $options |
|
| 59 | + * @return null|ISchemaWrapper |
|
| 60 | + */ |
|
| 61 | + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 62 | + /** @var ISchemaWrapper $schema */ |
|
| 63 | + $schema = $schemaClosure(); |
|
| 64 | 64 | |
| 65 | - $changeSchema = false; |
|
| 66 | - foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 67 | - $table = $schema->getTable($tableName); |
|
| 68 | - if (!$table->hasColumn('ldap_dn_hash')) { |
|
| 69 | - $table->addColumn('ldap_dn_hash', Types::STRING, [ |
|
| 70 | - 'notnull' => false, |
|
| 71 | - 'length' => 64, |
|
| 72 | - ]); |
|
| 73 | - $changeSchema = true; |
|
| 74 | - $this->hashColumnAddedToTables[] = $tableName; |
|
| 75 | - } |
|
| 76 | - $column = $table->getColumn('ldap_dn'); |
|
| 77 | - if ($tableName === 'ldap_user_mapping') { |
|
| 78 | - if ($column->getLength() < 4000) { |
|
| 79 | - $column->setLength(4000); |
|
| 80 | - $changeSchema = true; |
|
| 81 | - } |
|
| 65 | + $changeSchema = false; |
|
| 66 | + foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 67 | + $table = $schema->getTable($tableName); |
|
| 68 | + if (!$table->hasColumn('ldap_dn_hash')) { |
|
| 69 | + $table->addColumn('ldap_dn_hash', Types::STRING, [ |
|
| 70 | + 'notnull' => false, |
|
| 71 | + 'length' => 64, |
|
| 72 | + ]); |
|
| 73 | + $changeSchema = true; |
|
| 74 | + $this->hashColumnAddedToTables[] = $tableName; |
|
| 75 | + } |
|
| 76 | + $column = $table->getColumn('ldap_dn'); |
|
| 77 | + if ($tableName === 'ldap_user_mapping') { |
|
| 78 | + if ($column->getLength() < 4000) { |
|
| 79 | + $column->setLength(4000); |
|
| 80 | + $changeSchema = true; |
|
| 81 | + } |
|
| 82 | 82 | |
| 83 | - if ($table->hasIndex('ldap_dn_users')) { |
|
| 84 | - $table->dropIndex('ldap_dn_users'); |
|
| 85 | - $changeSchema = true; |
|
| 86 | - } |
|
| 87 | - if (!$table->hasIndex('ldap_user_dn_hashes')) { |
|
| 88 | - $table->addUniqueIndex(['ldap_dn_hash'], 'ldap_user_dn_hashes'); |
|
| 89 | - $changeSchema = true; |
|
| 90 | - } |
|
| 91 | - if (!$table->hasIndex('ldap_user_directory_uuid')) { |
|
| 92 | - $table->addUniqueIndex(['directory_uuid'], 'ldap_user_directory_uuid'); |
|
| 93 | - $changeSchema = true; |
|
| 94 | - } |
|
| 95 | - } elseif (!$schema->hasTable('ldap_group_mapping_backup')) { |
|
| 96 | - // We need to copy the table twice to be able to change primary key, prepare the backup table |
|
| 97 | - $table2 = $schema->createTable('ldap_group_mapping_backup'); |
|
| 98 | - $table2->addColumn('ldap_dn', Types::STRING, [ |
|
| 99 | - 'notnull' => true, |
|
| 100 | - 'length' => 4000, |
|
| 101 | - 'default' => '', |
|
| 102 | - ]); |
|
| 103 | - $table2->addColumn('owncloud_name', Types::STRING, [ |
|
| 104 | - 'notnull' => true, |
|
| 105 | - 'length' => 64, |
|
| 106 | - 'default' => '', |
|
| 107 | - ]); |
|
| 108 | - $table2->addColumn('directory_uuid', Types::STRING, [ |
|
| 109 | - 'notnull' => true, |
|
| 110 | - 'length' => 255, |
|
| 111 | - 'default' => '', |
|
| 112 | - ]); |
|
| 113 | - $table2->addColumn('ldap_dn_hash', Types::STRING, [ |
|
| 114 | - 'notnull' => false, |
|
| 115 | - 'length' => 64, |
|
| 116 | - ]); |
|
| 117 | - $table2->setPrimaryKey(['owncloud_name'], 'lgm_backup_primary'); |
|
| 118 | - $changeSchema = true; |
|
| 119 | - } |
|
| 120 | - } |
|
| 83 | + if ($table->hasIndex('ldap_dn_users')) { |
|
| 84 | + $table->dropIndex('ldap_dn_users'); |
|
| 85 | + $changeSchema = true; |
|
| 86 | + } |
|
| 87 | + if (!$table->hasIndex('ldap_user_dn_hashes')) { |
|
| 88 | + $table->addUniqueIndex(['ldap_dn_hash'], 'ldap_user_dn_hashes'); |
|
| 89 | + $changeSchema = true; |
|
| 90 | + } |
|
| 91 | + if (!$table->hasIndex('ldap_user_directory_uuid')) { |
|
| 92 | + $table->addUniqueIndex(['directory_uuid'], 'ldap_user_directory_uuid'); |
|
| 93 | + $changeSchema = true; |
|
| 94 | + } |
|
| 95 | + } elseif (!$schema->hasTable('ldap_group_mapping_backup')) { |
|
| 96 | + // We need to copy the table twice to be able to change primary key, prepare the backup table |
|
| 97 | + $table2 = $schema->createTable('ldap_group_mapping_backup'); |
|
| 98 | + $table2->addColumn('ldap_dn', Types::STRING, [ |
|
| 99 | + 'notnull' => true, |
|
| 100 | + 'length' => 4000, |
|
| 101 | + 'default' => '', |
|
| 102 | + ]); |
|
| 103 | + $table2->addColumn('owncloud_name', Types::STRING, [ |
|
| 104 | + 'notnull' => true, |
|
| 105 | + 'length' => 64, |
|
| 106 | + 'default' => '', |
|
| 107 | + ]); |
|
| 108 | + $table2->addColumn('directory_uuid', Types::STRING, [ |
|
| 109 | + 'notnull' => true, |
|
| 110 | + 'length' => 255, |
|
| 111 | + 'default' => '', |
|
| 112 | + ]); |
|
| 113 | + $table2->addColumn('ldap_dn_hash', Types::STRING, [ |
|
| 114 | + 'notnull' => false, |
|
| 115 | + 'length' => 64, |
|
| 116 | + ]); |
|
| 117 | + $table2->setPrimaryKey(['owncloud_name'], 'lgm_backup_primary'); |
|
| 118 | + $changeSchema = true; |
|
| 119 | + } |
|
| 120 | + } |
|
| 121 | 121 | |
| 122 | - return $changeSchema ? $schema : null; |
|
| 123 | - } |
|
| 122 | + return $changeSchema ? $schema : null; |
|
| 123 | + } |
|
| 124 | 124 | |
| 125 | - /** |
|
| 126 | - * @param IOutput $output |
|
| 127 | - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 128 | - * @param array $options |
|
| 129 | - */ |
|
| 130 | - public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { |
|
| 131 | - $this->handleDNHashes('ldap_group_mapping'); |
|
| 132 | - $this->handleDNHashes('ldap_user_mapping'); |
|
| 133 | - } |
|
| 125 | + /** |
|
| 126 | + * @param IOutput $output |
|
| 127 | + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 128 | + * @param array $options |
|
| 129 | + */ |
|
| 130 | + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { |
|
| 131 | + $this->handleDNHashes('ldap_group_mapping'); |
|
| 132 | + $this->handleDNHashes('ldap_user_mapping'); |
|
| 133 | + } |
|
| 134 | 134 | |
| 135 | - protected function handleDNHashes(string $table): void { |
|
| 136 | - $select = $this->getSelectQuery($table); |
|
| 137 | - $update = $this->getUpdateQuery($table); |
|
| 135 | + protected function handleDNHashes(string $table): void { |
|
| 136 | + $select = $this->getSelectQuery($table); |
|
| 137 | + $update = $this->getUpdateQuery($table); |
|
| 138 | 138 | |
| 139 | - $result = $select->executeQuery(); |
|
| 140 | - while ($row = $result->fetchAssociative()) { |
|
| 141 | - $dnHash = hash('sha256', $row['ldap_dn'], false); |
|
| 142 | - $update->setParameter('name', $row['owncloud_name']); |
|
| 143 | - $update->setParameter('dn_hash', $dnHash); |
|
| 144 | - try { |
|
| 145 | - $update->executeStatement(); |
|
| 146 | - } catch (Exception $e) { |
|
| 147 | - $this->logger->error('Failed to add hash "{dnHash}" ("{name}" of {table})', |
|
| 148 | - [ |
|
| 149 | - 'app' => 'user_ldap', |
|
| 150 | - 'name' => $row['owncloud_name'], |
|
| 151 | - 'dnHash' => $dnHash, |
|
| 152 | - 'table' => $table, |
|
| 153 | - 'exception' => $e, |
|
| 154 | - ] |
|
| 155 | - ); |
|
| 156 | - } |
|
| 157 | - } |
|
| 158 | - $result->closeCursor(); |
|
| 159 | - } |
|
| 139 | + $result = $select->executeQuery(); |
|
| 140 | + while ($row = $result->fetchAssociative()) { |
|
| 141 | + $dnHash = hash('sha256', $row['ldap_dn'], false); |
|
| 142 | + $update->setParameter('name', $row['owncloud_name']); |
|
| 143 | + $update->setParameter('dn_hash', $dnHash); |
|
| 144 | + try { |
|
| 145 | + $update->executeStatement(); |
|
| 146 | + } catch (Exception $e) { |
|
| 147 | + $this->logger->error('Failed to add hash "{dnHash}" ("{name}" of {table})', |
|
| 148 | + [ |
|
| 149 | + 'app' => 'user_ldap', |
|
| 150 | + 'name' => $row['owncloud_name'], |
|
| 151 | + 'dnHash' => $dnHash, |
|
| 152 | + 'table' => $table, |
|
| 153 | + 'exception' => $e, |
|
| 154 | + ] |
|
| 155 | + ); |
|
| 156 | + } |
|
| 157 | + } |
|
| 158 | + $result->closeCursor(); |
|
| 159 | + } |
|
| 160 | 160 | |
| 161 | - protected function getSelectQuery(string $table): IQueryBuilder { |
|
| 162 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 163 | - $qb->select('owncloud_name', 'ldap_dn') |
|
| 164 | - ->from($table); |
|
| 161 | + protected function getSelectQuery(string $table): IQueryBuilder { |
|
| 162 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 163 | + $qb->select('owncloud_name', 'ldap_dn') |
|
| 164 | + ->from($table); |
|
| 165 | 165 | |
| 166 | - // when added we may run into risk that it's read from a DB node |
|
| 167 | - // where the column is not present. Then the where clause is also |
|
| 168 | - // not necessary since all rows qualify. |
|
| 169 | - if (!in_array($table, $this->hashColumnAddedToTables, true)) { |
|
| 170 | - $qb->where($qb->expr()->isNull('ldap_dn_hash')); |
|
| 171 | - } |
|
| 166 | + // when added we may run into risk that it's read from a DB node |
|
| 167 | + // where the column is not present. Then the where clause is also |
|
| 168 | + // not necessary since all rows qualify. |
|
| 169 | + if (!in_array($table, $this->hashColumnAddedToTables, true)) { |
|
| 170 | + $qb->where($qb->expr()->isNull('ldap_dn_hash')); |
|
| 171 | + } |
|
| 172 | 172 | |
| 173 | - return $qb; |
|
| 174 | - } |
|
| 173 | + return $qb; |
|
| 174 | + } |
|
| 175 | 175 | |
| 176 | - protected function getUpdateQuery(string $table): IQueryBuilder { |
|
| 177 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 178 | - $qb->update($table) |
|
| 179 | - ->set('ldap_dn_hash', $qb->createParameter('dn_hash')) |
|
| 180 | - ->where($qb->expr()->eq('owncloud_name', $qb->createParameter('name'))); |
|
| 181 | - return $qb; |
|
| 182 | - } |
|
| 176 | + protected function getUpdateQuery(string $table): IQueryBuilder { |
|
| 177 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 178 | + $qb->update($table) |
|
| 179 | + ->set('ldap_dn_hash', $qb->createParameter('dn_hash')) |
|
| 180 | + ->where($qb->expr()->eq('owncloud_name', $qb->createParameter('name'))); |
|
| 181 | + return $qb; |
|
| 182 | + } |
|
| 183 | 183 | |
| 184 | - /** |
|
| 185 | - * @throws Exception |
|
| 186 | - */ |
|
| 187 | - protected function processDuplicateUUIDs(string $table): void { |
|
| 188 | - $uuids = $this->getDuplicatedUuids($table); |
|
| 189 | - $idsWithUuidToInvalidate = []; |
|
| 190 | - foreach ($uuids as $uuid) { |
|
| 191 | - array_push($idsWithUuidToInvalidate, ...$this->getNextcloudIdsByUuid($table, $uuid)); |
|
| 192 | - } |
|
| 193 | - $this->invalidateUuids($table, $idsWithUuidToInvalidate); |
|
| 194 | - } |
|
| 184 | + /** |
|
| 185 | + * @throws Exception |
|
| 186 | + */ |
|
| 187 | + protected function processDuplicateUUIDs(string $table): void { |
|
| 188 | + $uuids = $this->getDuplicatedUuids($table); |
|
| 189 | + $idsWithUuidToInvalidate = []; |
|
| 190 | + foreach ($uuids as $uuid) { |
|
| 191 | + array_push($idsWithUuidToInvalidate, ...$this->getNextcloudIdsByUuid($table, $uuid)); |
|
| 192 | + } |
|
| 193 | + $this->invalidateUuids($table, $idsWithUuidToInvalidate); |
|
| 194 | + } |
|
| 195 | 195 | |
| 196 | - /** |
|
| 197 | - * @throws Exception |
|
| 198 | - */ |
|
| 199 | - protected function invalidateUuids(string $table, array $idList): void { |
|
| 200 | - $update = $this->dbc->getQueryBuilder(); |
|
| 201 | - $update->update($table) |
|
| 202 | - ->set('directory_uuid', $update->createParameter('invalidatedUuid')) |
|
| 203 | - ->where($update->expr()->eq('owncloud_name', $update->createParameter('nextcloudId'))); |
|
| 196 | + /** |
|
| 197 | + * @throws Exception |
|
| 198 | + */ |
|
| 199 | + protected function invalidateUuids(string $table, array $idList): void { |
|
| 200 | + $update = $this->dbc->getQueryBuilder(); |
|
| 201 | + $update->update($table) |
|
| 202 | + ->set('directory_uuid', $update->createParameter('invalidatedUuid')) |
|
| 203 | + ->where($update->expr()->eq('owncloud_name', $update->createParameter('nextcloudId'))); |
|
| 204 | 204 | |
| 205 | - while ($nextcloudId = array_shift($idList)) { |
|
| 206 | - $update->setParameter('nextcloudId', $nextcloudId); |
|
| 207 | - $update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6))); |
|
| 208 | - try { |
|
| 209 | - $update->executeStatement(); |
|
| 210 | - $this->logger->warning( |
|
| 211 | - '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.', |
|
| 212 | - [ |
|
| 213 | - 'app' => 'user_ldap', |
|
| 214 | - 'nid' => $nextcloudId, |
|
| 215 | - ] |
|
| 216 | - ); |
|
| 217 | - } catch (Exception $e) { |
|
| 218 | - // Catch possible, but unlikely duplications if new invalidated errors. |
|
| 219 | - // There is the theoretical chance of an infinity loop is, when |
|
| 220 | - // the constraint violation has a different background. I cannot |
|
| 221 | - // think of one at the moment. |
|
| 222 | - if ($e->getReason() !== Exception::REASON_CONSTRAINT_VIOLATION) { |
|
| 223 | - throw $e; |
|
| 224 | - } |
|
| 225 | - $idList[] = $nextcloudId; |
|
| 226 | - } |
|
| 227 | - } |
|
| 228 | - } |
|
| 205 | + while ($nextcloudId = array_shift($idList)) { |
|
| 206 | + $update->setParameter('nextcloudId', $nextcloudId); |
|
| 207 | + $update->setParameter('invalidatedUuid', 'invalidated_' . \bin2hex(\random_bytes(6))); |
|
| 208 | + try { |
|
| 209 | + $update->executeStatement(); |
|
| 210 | + $this->logger->warning( |
|
| 211 | + '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.', |
|
| 212 | + [ |
|
| 213 | + 'app' => 'user_ldap', |
|
| 214 | + 'nid' => $nextcloudId, |
|
| 215 | + ] |
|
| 216 | + ); |
|
| 217 | + } catch (Exception $e) { |
|
| 218 | + // Catch possible, but unlikely duplications if new invalidated errors. |
|
| 219 | + // There is the theoretical chance of an infinity loop is, when |
|
| 220 | + // the constraint violation has a different background. I cannot |
|
| 221 | + // think of one at the moment. |
|
| 222 | + if ($e->getReason() !== Exception::REASON_CONSTRAINT_VIOLATION) { |
|
| 223 | + throw $e; |
|
| 224 | + } |
|
| 225 | + $idList[] = $nextcloudId; |
|
| 226 | + } |
|
| 227 | + } |
|
| 228 | + } |
|
| 229 | 229 | |
| 230 | - /** |
|
| 231 | - * @throws \OCP\DB\Exception |
|
| 232 | - * @return array<string> |
|
| 233 | - */ |
|
| 234 | - protected function getNextcloudIdsByUuid(string $table, string $uuid): array { |
|
| 235 | - $select = $this->dbc->getQueryBuilder(); |
|
| 236 | - $select->select('owncloud_name') |
|
| 237 | - ->from($table) |
|
| 238 | - ->where($select->expr()->eq('directory_uuid', $select->createNamedParameter($uuid))); |
|
| 230 | + /** |
|
| 231 | + * @throws \OCP\DB\Exception |
|
| 232 | + * @return array<string> |
|
| 233 | + */ |
|
| 234 | + protected function getNextcloudIdsByUuid(string $table, string $uuid): array { |
|
| 235 | + $select = $this->dbc->getQueryBuilder(); |
|
| 236 | + $select->select('owncloud_name') |
|
| 237 | + ->from($table) |
|
| 238 | + ->where($select->expr()->eq('directory_uuid', $select->createNamedParameter($uuid))); |
|
| 239 | 239 | |
| 240 | - $result = $select->executeQuery(); |
|
| 241 | - $idList = []; |
|
| 242 | - while (($id = $result->fetchOne()) !== false) { |
|
| 243 | - $idList[] = $id; |
|
| 244 | - } |
|
| 245 | - $result->closeCursor(); |
|
| 246 | - return $idList; |
|
| 247 | - } |
|
| 240 | + $result = $select->executeQuery(); |
|
| 241 | + $idList = []; |
|
| 242 | + while (($id = $result->fetchOne()) !== false) { |
|
| 243 | + $idList[] = $id; |
|
| 244 | + } |
|
| 245 | + $result->closeCursor(); |
|
| 246 | + return $idList; |
|
| 247 | + } |
|
| 248 | 248 | |
| 249 | - /** |
|
| 250 | - * @return Generator<string> |
|
| 251 | - * @throws \OCP\DB\Exception |
|
| 252 | - */ |
|
| 253 | - protected function getDuplicatedUuids(string $table): Generator { |
|
| 254 | - $select = $this->dbc->getQueryBuilder(); |
|
| 255 | - $select->select('directory_uuid') |
|
| 256 | - ->from($table) |
|
| 257 | - ->groupBy('directory_uuid') |
|
| 258 | - ->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1))); |
|
| 249 | + /** |
|
| 250 | + * @return Generator<string> |
|
| 251 | + * @throws \OCP\DB\Exception |
|
| 252 | + */ |
|
| 253 | + protected function getDuplicatedUuids(string $table): Generator { |
|
| 254 | + $select = $this->dbc->getQueryBuilder(); |
|
| 255 | + $select->select('directory_uuid') |
|
| 256 | + ->from($table) |
|
| 257 | + ->groupBy('directory_uuid') |
|
| 258 | + ->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1))); |
|
| 259 | 259 | |
| 260 | - $result = $select->executeQuery(); |
|
| 261 | - while (($uuid = $result->fetchOne()) !== false) { |
|
| 262 | - yield $uuid; |
|
| 263 | - } |
|
| 264 | - $result->closeCursor(); |
|
| 265 | - } |
|
| 260 | + $result = $select->executeQuery(); |
|
| 261 | + while (($uuid = $result->fetchOne()) !== false) { |
|
| 262 | + yield $uuid; |
|
| 263 | + } |
|
| 264 | + $result->closeCursor(); |
|
| 265 | + } |
|
| 266 | 266 | } |
@@ -14,37 +14,37 @@ |
||
| 14 | 14 | |
| 15 | 15 | abstract class GroupMappingMigration extends SimpleMigrationStep { |
| 16 | 16 | |
| 17 | - public function __construct( |
|
| 18 | - private IDBConnection $dbc, |
|
| 19 | - ) { |
|
| 20 | - } |
|
| 21 | - |
|
| 22 | - protected function copyGroupMappingData(string $sourceTable, string $destinationTable): void { |
|
| 23 | - $insert = $this->dbc->getQueryBuilder(); |
|
| 24 | - $insert->insert($destinationTable) |
|
| 25 | - ->values([ |
|
| 26 | - 'ldap_dn' => $insert->createParameter('ldap_dn'), |
|
| 27 | - 'owncloud_name' => $insert->createParameter('owncloud_name'), |
|
| 28 | - 'directory_uuid' => $insert->createParameter('directory_uuid'), |
|
| 29 | - 'ldap_dn_hash' => $insert->createParameter('ldap_dn_hash'), |
|
| 30 | - ]); |
|
| 31 | - |
|
| 32 | - $query = $this->dbc->getQueryBuilder(); |
|
| 33 | - $query->select('*') |
|
| 34 | - ->from($sourceTable); |
|
| 35 | - |
|
| 36 | - |
|
| 37 | - $result = $query->executeQuery(); |
|
| 38 | - while ($row = $result->fetchAssociative()) { |
|
| 39 | - $insert |
|
| 40 | - ->setParameter('ldap_dn', $row['ldap_dn']) |
|
| 41 | - ->setParameter('owncloud_name', $row['owncloud_name']) |
|
| 42 | - ->setParameter('directory_uuid', $row['directory_uuid']) |
|
| 43 | - ->setParameter('ldap_dn_hash', $row['ldap_dn_hash']) |
|
| 44 | - ; |
|
| 45 | - |
|
| 46 | - $insert->executeStatement(); |
|
| 47 | - } |
|
| 48 | - $result->closeCursor(); |
|
| 49 | - } |
|
| 17 | + public function __construct( |
|
| 18 | + private IDBConnection $dbc, |
|
| 19 | + ) { |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + protected function copyGroupMappingData(string $sourceTable, string $destinationTable): void { |
|
| 23 | + $insert = $this->dbc->getQueryBuilder(); |
|
| 24 | + $insert->insert($destinationTable) |
|
| 25 | + ->values([ |
|
| 26 | + 'ldap_dn' => $insert->createParameter('ldap_dn'), |
|
| 27 | + 'owncloud_name' => $insert->createParameter('owncloud_name'), |
|
| 28 | + 'directory_uuid' => $insert->createParameter('directory_uuid'), |
|
| 29 | + 'ldap_dn_hash' => $insert->createParameter('ldap_dn_hash'), |
|
| 30 | + ]); |
|
| 31 | + |
|
| 32 | + $query = $this->dbc->getQueryBuilder(); |
|
| 33 | + $query->select('*') |
|
| 34 | + ->from($sourceTable); |
|
| 35 | + |
|
| 36 | + |
|
| 37 | + $result = $query->executeQuery(); |
|
| 38 | + while ($row = $result->fetchAssociative()) { |
|
| 39 | + $insert |
|
| 40 | + ->setParameter('ldap_dn', $row['ldap_dn']) |
|
| 41 | + ->setParameter('owncloud_name', $row['owncloud_name']) |
|
| 42 | + ->setParameter('directory_uuid', $row['directory_uuid']) |
|
| 43 | + ->setParameter('ldap_dn_hash', $row['ldap_dn_hash']) |
|
| 44 | + ; |
|
| 45 | + |
|
| 46 | + $insert->executeStatement(); |
|
| 47 | + } |
|
| 48 | + $result->closeCursor(); |
|
| 49 | + } |
|
| 50 | 50 | } |
@@ -22,110 +22,110 @@ |
||
| 22 | 22 | |
| 23 | 23 | class Version1120Date20210917155206 extends SimpleMigrationStep { |
| 24 | 24 | |
| 25 | - public function __construct( |
|
| 26 | - private IDBConnection $dbc, |
|
| 27 | - private IUserManager $userManager, |
|
| 28 | - private LoggerInterface $logger, |
|
| 29 | - ) { |
|
| 30 | - } |
|
| 31 | - |
|
| 32 | - public function getName() { |
|
| 33 | - return 'Adjust LDAP user and group id column lengths to match server lengths'; |
|
| 34 | - } |
|
| 35 | - |
|
| 36 | - /** |
|
| 37 | - * @param IOutput $output |
|
| 38 | - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 39 | - * @param array $options |
|
| 40 | - */ |
|
| 41 | - public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 42 | - // ensure that there is no user or group id longer than 64char in LDAP table |
|
| 43 | - $this->handleIDs('ldap_group_mapping', false); |
|
| 44 | - $this->handleIDs('ldap_user_mapping', true); |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - /** |
|
| 48 | - * @param IOutput $output |
|
| 49 | - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 50 | - * @param array $options |
|
| 51 | - * @return null|ISchemaWrapper |
|
| 52 | - */ |
|
| 53 | - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 54 | - /** @var ISchemaWrapper $schema */ |
|
| 55 | - $schema = $schemaClosure(); |
|
| 56 | - |
|
| 57 | - $changeSchema = false; |
|
| 58 | - foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 59 | - $table = $schema->getTable($tableName); |
|
| 60 | - $column = $table->getColumn('owncloud_name'); |
|
| 61 | - if ($column->getLength() > 64) { |
|
| 62 | - $column->setLength(64); |
|
| 63 | - $changeSchema = true; |
|
| 64 | - } |
|
| 65 | - } |
|
| 66 | - |
|
| 67 | - return $changeSchema ? $schema : null; |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - protected function handleIDs(string $table, bool $emitHooks) { |
|
| 71 | - $select = $this->getSelectQuery($table); |
|
| 72 | - $update = $this->getUpdateQuery($table); |
|
| 73 | - |
|
| 74 | - $result = $select->executeQuery(); |
|
| 75 | - while ($row = $result->fetchAssociative()) { |
|
| 76 | - $newId = hash('sha256', $row['owncloud_name'], false); |
|
| 77 | - if ($emitHooks) { |
|
| 78 | - $this->emitUnassign($row['owncloud_name'], true); |
|
| 79 | - } |
|
| 80 | - $update->setParameter('uuid', $row['directory_uuid']); |
|
| 81 | - $update->setParameter('newId', $newId); |
|
| 82 | - try { |
|
| 83 | - $update->executeStatement(); |
|
| 84 | - if ($emitHooks) { |
|
| 85 | - $this->emitUnassign($row['owncloud_name'], false); |
|
| 86 | - $this->emitAssign($newId); |
|
| 87 | - } |
|
| 88 | - } catch (Exception $e) { |
|
| 89 | - $this->logger->error('Failed to shorten owncloud_name "{oldId}" to "{newId}" (UUID: "{uuid}" of {table})', |
|
| 90 | - [ |
|
| 91 | - 'app' => 'user_ldap', |
|
| 92 | - 'oldId' => $row['owncloud_name'], |
|
| 93 | - 'newId' => $newId, |
|
| 94 | - 'uuid' => $row['directory_uuid'], |
|
| 95 | - 'table' => $table, |
|
| 96 | - 'exception' => $e, |
|
| 97 | - ] |
|
| 98 | - ); |
|
| 99 | - } |
|
| 100 | - } |
|
| 101 | - $result->closeCursor(); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - protected function getSelectQuery(string $table): IQueryBuilder { |
|
| 105 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 106 | - $qb->select('owncloud_name', 'directory_uuid') |
|
| 107 | - ->from($table) |
|
| 108 | - ->where($qb->expr()->gt($qb->func()->octetLength('owncloud_name'), $qb->createNamedParameter('64'), IQueryBuilder::PARAM_INT)); |
|
| 109 | - return $qb; |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - protected function getUpdateQuery(string $table): IQueryBuilder { |
|
| 113 | - $qb = $this->dbc->getQueryBuilder(); |
|
| 114 | - $qb->update($table) |
|
| 115 | - ->set('owncloud_name', $qb->createParameter('newId')) |
|
| 116 | - ->where($qb->expr()->eq('directory_uuid', $qb->createParameter('uuid'))); |
|
| 117 | - return $qb; |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - protected function emitUnassign(string $oldId, bool $pre): void { |
|
| 121 | - if ($this->userManager instanceof PublicEmitter) { |
|
| 122 | - $this->userManager->emit('\OC\User', $pre ? 'pre' : 'post' . 'UnassignedUserId', [$oldId]); |
|
| 123 | - } |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - protected function emitAssign(string $newId): void { |
|
| 127 | - if ($this->userManager instanceof PublicEmitter) { |
|
| 128 | - $this->userManager->emit('\OC\User', 'assignedUserId', [$newId]); |
|
| 129 | - } |
|
| 130 | - } |
|
| 25 | + public function __construct( |
|
| 26 | + private IDBConnection $dbc, |
|
| 27 | + private IUserManager $userManager, |
|
| 28 | + private LoggerInterface $logger, |
|
| 29 | + ) { |
|
| 30 | + } |
|
| 31 | + |
|
| 32 | + public function getName() { |
|
| 33 | + return 'Adjust LDAP user and group id column lengths to match server lengths'; |
|
| 34 | + } |
|
| 35 | + |
|
| 36 | + /** |
|
| 37 | + * @param IOutput $output |
|
| 38 | + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 39 | + * @param array $options |
|
| 40 | + */ |
|
| 41 | + public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { |
|
| 42 | + // ensure that there is no user or group id longer than 64char in LDAP table |
|
| 43 | + $this->handleIDs('ldap_group_mapping', false); |
|
| 44 | + $this->handleIDs('ldap_user_mapping', true); |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + /** |
|
| 48 | + * @param IOutput $output |
|
| 49 | + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
|
| 50 | + * @param array $options |
|
| 51 | + * @return null|ISchemaWrapper |
|
| 52 | + */ |
|
| 53 | + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 54 | + /** @var ISchemaWrapper $schema */ |
|
| 55 | + $schema = $schemaClosure(); |
|
| 56 | + |
|
| 57 | + $changeSchema = false; |
|
| 58 | + foreach (['ldap_user_mapping', 'ldap_group_mapping'] as $tableName) { |
|
| 59 | + $table = $schema->getTable($tableName); |
|
| 60 | + $column = $table->getColumn('owncloud_name'); |
|
| 61 | + if ($column->getLength() > 64) { |
|
| 62 | + $column->setLength(64); |
|
| 63 | + $changeSchema = true; |
|
| 64 | + } |
|
| 65 | + } |
|
| 66 | + |
|
| 67 | + return $changeSchema ? $schema : null; |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + protected function handleIDs(string $table, bool $emitHooks) { |
|
| 71 | + $select = $this->getSelectQuery($table); |
|
| 72 | + $update = $this->getUpdateQuery($table); |
|
| 73 | + |
|
| 74 | + $result = $select->executeQuery(); |
|
| 75 | + while ($row = $result->fetchAssociative()) { |
|
| 76 | + $newId = hash('sha256', $row['owncloud_name'], false); |
|
| 77 | + if ($emitHooks) { |
|
| 78 | + $this->emitUnassign($row['owncloud_name'], true); |
|
| 79 | + } |
|
| 80 | + $update->setParameter('uuid', $row['directory_uuid']); |
|
| 81 | + $update->setParameter('newId', $newId); |
|
| 82 | + try { |
|
| 83 | + $update->executeStatement(); |
|
| 84 | + if ($emitHooks) { |
|
| 85 | + $this->emitUnassign($row['owncloud_name'], false); |
|
| 86 | + $this->emitAssign($newId); |
|
| 87 | + } |
|
| 88 | + } catch (Exception $e) { |
|
| 89 | + $this->logger->error('Failed to shorten owncloud_name "{oldId}" to "{newId}" (UUID: "{uuid}" of {table})', |
|
| 90 | + [ |
|
| 91 | + 'app' => 'user_ldap', |
|
| 92 | + 'oldId' => $row['owncloud_name'], |
|
| 93 | + 'newId' => $newId, |
|
| 94 | + 'uuid' => $row['directory_uuid'], |
|
| 95 | + 'table' => $table, |
|
| 96 | + 'exception' => $e, |
|
| 97 | + ] |
|
| 98 | + ); |
|
| 99 | + } |
|
| 100 | + } |
|
| 101 | + $result->closeCursor(); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + protected function getSelectQuery(string $table): IQueryBuilder { |
|
| 105 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 106 | + $qb->select('owncloud_name', 'directory_uuid') |
|
| 107 | + ->from($table) |
|
| 108 | + ->where($qb->expr()->gt($qb->func()->octetLength('owncloud_name'), $qb->createNamedParameter('64'), IQueryBuilder::PARAM_INT)); |
|
| 109 | + return $qb; |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + protected function getUpdateQuery(string $table): IQueryBuilder { |
|
| 113 | + $qb = $this->dbc->getQueryBuilder(); |
|
| 114 | + $qb->update($table) |
|
| 115 | + ->set('owncloud_name', $qb->createParameter('newId')) |
|
| 116 | + ->where($qb->expr()->eq('directory_uuid', $qb->createParameter('uuid'))); |
|
| 117 | + return $qb; |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + protected function emitUnassign(string $oldId, bool $pre): void { |
|
| 121 | + if ($this->userManager instanceof PublicEmitter) { |
|
| 122 | + $this->userManager->emit('\OC\User', $pre ? 'pre' : 'post' . 'UnassignedUserId', [$oldId]); |
|
| 123 | + } |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + protected function emitAssign(string $newId): void { |
|
| 127 | + if ($this->userManager instanceof PublicEmitter) { |
|
| 128 | + $this->userManager->emit('\OC\User', 'assignedUserId', [$newId]); |
|
| 129 | + } |
|
| 130 | + } |
|
| 131 | 131 | } |
@@ -39,916 +39,916 @@ |
||
| 39 | 39 | */ |
| 40 | 40 | #[\PHPUnit\Framework\Attributes\Group('DB')] |
| 41 | 41 | class FederatedShareProviderTest extends \Test\TestCase { |
| 42 | - protected IDBConnection $connection; |
|
| 43 | - protected AddressHandler&MockObject $addressHandler; |
|
| 44 | - protected Notifications&MockObject $notifications; |
|
| 45 | - protected TokenHandler&MockObject $tokenHandler; |
|
| 46 | - protected IL10N $l; |
|
| 47 | - protected LoggerInterface $logger; |
|
| 48 | - protected IRootFolder&MockObject $rootFolder; |
|
| 49 | - protected IConfig&MockObject $config; |
|
| 50 | - protected IUserManager&MockObject $userManager; |
|
| 51 | - protected \OCP\GlobalScale\IConfig&MockObject $gsConfig; |
|
| 52 | - protected IManager $shareManager; |
|
| 53 | - protected FederatedShareProvider $provider; |
|
| 54 | - protected IContactsManager&MockObject $contactsManager; |
|
| 55 | - private ICloudIdManager $cloudIdManager; |
|
| 56 | - private ICloudFederationProviderManager&MockObject $cloudFederationProviderManager; |
|
| 57 | - |
|
| 58 | - protected function setUp(): void { |
|
| 59 | - parent::setUp(); |
|
| 60 | - |
|
| 61 | - $this->connection = Server::get(IDBConnection::class); |
|
| 62 | - $this->notifications = $this->createMock(Notifications::class); |
|
| 63 | - $this->tokenHandler = $this->createMock(TokenHandler::class); |
|
| 64 | - $this->l = $this->createMock(IL10N::class); |
|
| 65 | - $this->l->method('t') |
|
| 66 | - ->willReturnCallback(function ($text, $parameters = []) { |
|
| 67 | - return vsprintf($text, $parameters); |
|
| 68 | - }); |
|
| 69 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
| 70 | - $this->rootFolder = $this->createMock(IRootFolder::class); |
|
| 71 | - $this->config = $this->createMock(IConfig::class); |
|
| 72 | - $this->userManager = $this->createMock(IUserManager::class); |
|
| 73 | - //$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); |
|
| 74 | - $this->addressHandler = $this->createMock(AddressHandler::class); |
|
| 75 | - $this->contactsManager = $this->createMock(IContactsManager::class); |
|
| 76 | - $this->cloudIdManager = new CloudIdManager( |
|
| 77 | - $this->createMock(ICacheFactory::class), |
|
| 78 | - $this->createMock(IEventDispatcher::class), |
|
| 79 | - $this->contactsManager, |
|
| 80 | - $this->createMock(IURLGenerator::class), |
|
| 81 | - $this->userManager, |
|
| 82 | - ); |
|
| 83 | - $this->gsConfig = $this->createMock(\OCP\GlobalScale\IConfig::class); |
|
| 84 | - |
|
| 85 | - $this->userManager->expects($this->any())->method('userExists')->willReturn(true); |
|
| 86 | - |
|
| 87 | - $this->cloudFederationProviderManager = $this->createMock(ICloudFederationProviderManager::class); |
|
| 88 | - |
|
| 89 | - $this->provider = new FederatedShareProvider( |
|
| 90 | - $this->connection, |
|
| 91 | - $this->addressHandler, |
|
| 92 | - $this->notifications, |
|
| 93 | - $this->tokenHandler, |
|
| 94 | - $this->l, |
|
| 95 | - $this->rootFolder, |
|
| 96 | - $this->config, |
|
| 97 | - $this->userManager, |
|
| 98 | - $this->cloudIdManager, |
|
| 99 | - $this->gsConfig, |
|
| 100 | - $this->cloudFederationProviderManager, |
|
| 101 | - $this->logger, |
|
| 102 | - ); |
|
| 103 | - |
|
| 104 | - $this->shareManager = Server::get(IManager::class); |
|
| 105 | - } |
|
| 106 | - |
|
| 107 | - protected function tearDown(): void { |
|
| 108 | - $this->connection->getQueryBuilder()->delete('share')->executeStatement(); |
|
| 109 | - |
|
| 110 | - parent::tearDown(); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - public static function dataTestCreate(): array { |
|
| 114 | - return [ |
|
| 115 | - [null, null], |
|
| 116 | - [new \DateTime('2020-03-01T01:02:03'), '2020-03-01 01:02:03'], |
|
| 117 | - ]; |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCreate')] |
|
| 121 | - public function testCreate(?\DateTime $expirationDate, ?string $expectedDataDate): void { |
|
| 122 | - $share = $this->shareManager->newShare(); |
|
| 123 | - |
|
| 124 | - /** @var File&MockObject $node */ |
|
| 125 | - $node = $this->createMock(File::class); |
|
| 126 | - $node->method('getId')->willReturn(42); |
|
| 127 | - $node->method('getName')->willReturn('myFile'); |
|
| 128 | - |
|
| 129 | - $share->setSharedWith('[email protected]') |
|
| 130 | - ->setSharedBy('sharedBy') |
|
| 131 | - ->setShareOwner('shareOwner') |
|
| 132 | - ->setPermissions(19) |
|
| 133 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 134 | - ->setExpirationDate($expirationDate) |
|
| 135 | - ->setNode($node); |
|
| 136 | - |
|
| 137 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 138 | - |
|
| 139 | - $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 140 | - ->willReturn('http://localhost/'); |
|
| 141 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 142 | - ->willReturn(['user', 'server.com']); |
|
| 143 | - |
|
| 144 | - $this->notifications->expects($this->once()) |
|
| 145 | - ->method('sendRemoteShare') |
|
| 146 | - ->with( |
|
| 147 | - $this->equalTo('token'), |
|
| 148 | - $this->equalTo('[email protected]'), |
|
| 149 | - $this->equalTo('myFile'), |
|
| 150 | - $this->anything(), |
|
| 151 | - 'shareOwner', |
|
| 152 | - 'shareOwner@http://localhost', |
|
| 153 | - 'sharedBy', |
|
| 154 | - 'sharedBy@http://localhost' |
|
| 155 | - ) |
|
| 156 | - ->willReturn(true); |
|
| 157 | - |
|
| 158 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 159 | - |
|
| 160 | - $this->contactsManager->expects($this->any()) |
|
| 161 | - ->method('search') |
|
| 162 | - ->willReturn([]); |
|
| 163 | - |
|
| 164 | - $share = $this->provider->create($share); |
|
| 165 | - |
|
| 166 | - $qb = $this->connection->getQueryBuilder(); |
|
| 167 | - $stmt = $qb->select('*') |
|
| 168 | - ->from('share') |
|
| 169 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 170 | - ->executeQuery(); |
|
| 171 | - |
|
| 172 | - $data = $stmt->fetchAssociative(); |
|
| 173 | - $stmt->closeCursor(); |
|
| 174 | - |
|
| 175 | - $expected = [ |
|
| 176 | - 'share_type' => IShare::TYPE_REMOTE, |
|
| 177 | - 'share_with' => '[email protected]', |
|
| 178 | - 'uid_owner' => 'shareOwner', |
|
| 179 | - 'uid_initiator' => 'sharedBy', |
|
| 180 | - 'item_type' => 'file', |
|
| 181 | - 'item_source' => 42, |
|
| 182 | - 'file_source' => 42, |
|
| 183 | - 'permissions' => 19, |
|
| 184 | - 'accepted' => 0, |
|
| 185 | - 'token' => 'token', |
|
| 186 | - 'expiration' => $expectedDataDate, |
|
| 187 | - ]; |
|
| 188 | - foreach (array_keys($expected) as $key) { |
|
| 189 | - $this->assertEquals($expected[$key], $data[$key], "Assert that value for key '$key' is the same"); |
|
| 190 | - } |
|
| 191 | - |
|
| 192 | - $this->assertEquals($data['id'], $share->getId()); |
|
| 193 | - $this->assertEquals(IShare::TYPE_REMOTE, $share->getShareType()); |
|
| 194 | - $this->assertEquals('[email protected]', $share->getSharedWith()); |
|
| 195 | - $this->assertEquals('sharedBy', $share->getSharedBy()); |
|
| 196 | - $this->assertEquals('shareOwner', $share->getShareOwner()); |
|
| 197 | - $this->assertEquals('file', $share->getNodeType()); |
|
| 198 | - $this->assertEquals(42, $share->getNodeId()); |
|
| 199 | - $this->assertEquals(19, $share->getPermissions()); |
|
| 200 | - $this->assertEquals('token', $share->getToken()); |
|
| 201 | - $this->assertEquals($expirationDate, $share->getExpirationDate()); |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - public function testCreateCouldNotFindServer(): void { |
|
| 205 | - $share = $this->shareManager->newShare(); |
|
| 206 | - |
|
| 207 | - $node = $this->createMock(File::class); |
|
| 208 | - $node->method('getId')->willReturn(42); |
|
| 209 | - $node->method('getName')->willReturn('myFile'); |
|
| 210 | - |
|
| 211 | - $share->setSharedWith('[email protected]') |
|
| 212 | - ->setSharedBy('sharedBy') |
|
| 213 | - ->setShareOwner('shareOwner') |
|
| 214 | - ->setPermissions(19) |
|
| 215 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 216 | - ->setNode($node); |
|
| 217 | - |
|
| 218 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 219 | - |
|
| 220 | - $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 221 | - ->willReturn('http://localhost/'); |
|
| 222 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 223 | - ->willReturn(['user', 'server.com']); |
|
| 224 | - |
|
| 225 | - $this->notifications->expects($this->once()) |
|
| 226 | - ->method('sendRemoteShare') |
|
| 227 | - ->with( |
|
| 228 | - $this->equalTo('token'), |
|
| 229 | - $this->equalTo('[email protected]'), |
|
| 230 | - $this->equalTo('myFile'), |
|
| 231 | - $this->anything(), |
|
| 232 | - 'shareOwner', |
|
| 233 | - 'shareOwner@http://localhost', |
|
| 234 | - 'sharedBy', |
|
| 235 | - 'sharedBy@http://localhost' |
|
| 236 | - )->willReturn(false); |
|
| 237 | - |
|
| 238 | - $this->rootFolder->method('getById') |
|
| 239 | - ->with('42') |
|
| 240 | - ->willReturn([$node]); |
|
| 241 | - |
|
| 242 | - $this->contactsManager->expects($this->any()) |
|
| 243 | - ->method('search') |
|
| 244 | - ->willReturn([]); |
|
| 245 | - |
|
| 246 | - try { |
|
| 247 | - $share = $this->provider->create($share); |
|
| 248 | - $this->fail(); |
|
| 249 | - } catch (\Exception $e) { |
|
| 250 | - $this->assertEquals('Sharing myFile failed, could not find [email protected], maybe the server is currently unreachable or uses a self-signed certificate.', $e->getMessage()); |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - $qb = $this->connection->getQueryBuilder(); |
|
| 254 | - $stmt = $qb->select('*') |
|
| 255 | - ->from('share') |
|
| 256 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 257 | - ->executeQuery(); |
|
| 258 | - |
|
| 259 | - $data = $stmt->fetchAssociative(); |
|
| 260 | - $stmt->closeCursor(); |
|
| 261 | - |
|
| 262 | - $this->assertFalse($data); |
|
| 263 | - } |
|
| 264 | - |
|
| 265 | - public function testCreateException(): void { |
|
| 266 | - $share = $this->shareManager->newShare(); |
|
| 267 | - |
|
| 268 | - $node = $this->createMock(File::class); |
|
| 269 | - $node->method('getId')->willReturn(42); |
|
| 270 | - $node->method('getName')->willReturn('myFile'); |
|
| 271 | - |
|
| 272 | - $share->setSharedWith('[email protected]') |
|
| 273 | - ->setSharedBy('sharedBy') |
|
| 274 | - ->setShareOwner('shareOwner') |
|
| 275 | - ->setPermissions(19) |
|
| 276 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 277 | - ->setNode($node); |
|
| 278 | - |
|
| 279 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 280 | - |
|
| 281 | - $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 282 | - ->willReturn('http://localhost/'); |
|
| 283 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 284 | - ->willReturn(['user', 'server.com']); |
|
| 285 | - |
|
| 286 | - $this->notifications->expects($this->once()) |
|
| 287 | - ->method('sendRemoteShare') |
|
| 288 | - ->with( |
|
| 289 | - $this->equalTo('token'), |
|
| 290 | - $this->equalTo('[email protected]'), |
|
| 291 | - $this->equalTo('myFile'), |
|
| 292 | - $this->anything(), |
|
| 293 | - 'shareOwner', |
|
| 294 | - 'shareOwner@http://localhost', |
|
| 295 | - 'sharedBy', |
|
| 296 | - 'sharedBy@http://localhost' |
|
| 297 | - )->willThrowException(new \Exception('dummy')); |
|
| 298 | - |
|
| 299 | - $this->rootFolder->method('getById') |
|
| 300 | - ->with('42') |
|
| 301 | - ->willReturn([$node]); |
|
| 302 | - |
|
| 303 | - $this->contactsManager->expects($this->any()) |
|
| 304 | - ->method('search') |
|
| 305 | - ->willReturn([]); |
|
| 306 | - |
|
| 307 | - try { |
|
| 308 | - $share = $this->provider->create($share); |
|
| 309 | - $this->fail(); |
|
| 310 | - } catch (\Exception $e) { |
|
| 311 | - $this->assertEquals('Sharing myFile failed, could not find [email protected], maybe the server is currently unreachable or uses a self-signed certificate.', $e->getMessage()); |
|
| 312 | - } |
|
| 313 | - |
|
| 314 | - $qb = $this->connection->getQueryBuilder(); |
|
| 315 | - $stmt = $qb->select('*') |
|
| 316 | - ->from('share') |
|
| 317 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 318 | - ->executeQuery(); |
|
| 319 | - |
|
| 320 | - $data = $stmt->fetchAssociative(); |
|
| 321 | - $stmt->closeCursor(); |
|
| 322 | - |
|
| 323 | - $this->assertFalse($data); |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - public function testCreateShareWithSelf(): void { |
|
| 327 | - $share = $this->shareManager->newShare(); |
|
| 328 | - |
|
| 329 | - $node = $this->createMock(File::class); |
|
| 330 | - $node->method('getId')->willReturn(42); |
|
| 331 | - $node->method('getName')->willReturn('myFile'); |
|
| 332 | - |
|
| 333 | - $this->addressHandler->expects($this->any())->method('compareAddresses') |
|
| 334 | - ->willReturn(true); |
|
| 335 | - |
|
| 336 | - $shareWith = 'sharedBy@localhost'; |
|
| 337 | - |
|
| 338 | - $share->setSharedWith($shareWith) |
|
| 339 | - ->setSharedBy('sharedBy') |
|
| 340 | - ->setShareOwner('shareOwner') |
|
| 341 | - ->setPermissions(19) |
|
| 342 | - ->setNode($node); |
|
| 343 | - |
|
| 344 | - $this->contactsManager->expects($this->any()) |
|
| 345 | - ->method('search') |
|
| 346 | - ->willReturn([]); |
|
| 347 | - |
|
| 348 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 349 | - |
|
| 350 | - try { |
|
| 351 | - $share = $this->provider->create($share); |
|
| 352 | - $this->fail(); |
|
| 353 | - } catch (\Exception $e) { |
|
| 354 | - $this->assertEquals('Not allowed to create a federated share to the same account', $e->getMessage()); |
|
| 355 | - } |
|
| 356 | - |
|
| 357 | - $qb = $this->connection->getQueryBuilder(); |
|
| 358 | - $stmt = $qb->select('*') |
|
| 359 | - ->from('share') |
|
| 360 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 361 | - ->executeQuery(); |
|
| 362 | - |
|
| 363 | - $data = $stmt->fetchAssociative(); |
|
| 364 | - $stmt->closeCursor(); |
|
| 365 | - |
|
| 366 | - $this->assertFalse($data); |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - public function testCreateAlreadyShared(): void { |
|
| 370 | - $share = $this->shareManager->newShare(); |
|
| 371 | - |
|
| 372 | - $node = $this->createMock(File::class); |
|
| 373 | - $node->method('getId')->willReturn(42); |
|
| 374 | - $node->method('getName')->willReturn('myFile'); |
|
| 375 | - |
|
| 376 | - |
|
| 377 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 378 | - ->willReturn(['user', 'server.com']); |
|
| 379 | - |
|
| 380 | - $share->setSharedWith('[email protected]') |
|
| 381 | - ->setSharedBy('sharedBy') |
|
| 382 | - ->setShareOwner('shareOwner') |
|
| 383 | - ->setPermissions(19) |
|
| 384 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 385 | - ->setNode($node); |
|
| 386 | - |
|
| 387 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 388 | - |
|
| 389 | - $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 390 | - ->willReturn('http://localhost/'); |
|
| 391 | - |
|
| 392 | - $this->notifications->expects($this->once()) |
|
| 393 | - ->method('sendRemoteShare') |
|
| 394 | - ->with( |
|
| 395 | - $this->equalTo('token'), |
|
| 396 | - $this->equalTo('[email protected]'), |
|
| 397 | - $this->equalTo('myFile'), |
|
| 398 | - $this->anything(), |
|
| 399 | - 'shareOwner', |
|
| 400 | - 'shareOwner@http://localhost', |
|
| 401 | - 'sharedBy', |
|
| 402 | - 'sharedBy@http://localhost' |
|
| 403 | - )->willReturn(true); |
|
| 404 | - |
|
| 405 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 406 | - |
|
| 407 | - $this->contactsManager->expects($this->any()) |
|
| 408 | - ->method('search') |
|
| 409 | - ->willReturn([]); |
|
| 410 | - |
|
| 411 | - $this->provider->create($share); |
|
| 412 | - |
|
| 413 | - try { |
|
| 414 | - $this->provider->create($share); |
|
| 415 | - } catch (\Exception $e) { |
|
| 416 | - $this->assertEquals('Sharing myFile failed, because this item is already shared with the account [email protected]', $e->getMessage()); |
|
| 417 | - } |
|
| 418 | - } |
|
| 419 | - |
|
| 420 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUpdate')] |
|
| 421 | - public function testUpdate(string $owner, string $sharedBy, ?\DateTime $expirationDate): void { |
|
| 422 | - $this->provider = $this->getMockBuilder(FederatedShareProvider::class) |
|
| 423 | - ->setConstructorArgs( |
|
| 424 | - [ |
|
| 425 | - $this->connection, |
|
| 426 | - $this->addressHandler, |
|
| 427 | - $this->notifications, |
|
| 428 | - $this->tokenHandler, |
|
| 429 | - $this->l, |
|
| 430 | - $this->rootFolder, |
|
| 431 | - $this->config, |
|
| 432 | - $this->userManager, |
|
| 433 | - $this->cloudIdManager, |
|
| 434 | - $this->gsConfig, |
|
| 435 | - $this->cloudFederationProviderManager, |
|
| 436 | - $this->logger, |
|
| 437 | - ] |
|
| 438 | - ) |
|
| 439 | - ->onlyMethods(['sendPermissionUpdate']) |
|
| 440 | - ->getMock(); |
|
| 441 | - |
|
| 442 | - $share = $this->shareManager->newShare(); |
|
| 443 | - |
|
| 444 | - $node = $this->createMock(File::class); |
|
| 445 | - $node->method('getId')->willReturn(42); |
|
| 446 | - $node->method('getName')->willReturn('myFile'); |
|
| 447 | - |
|
| 448 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 449 | - ->willReturn(['user', 'server.com']); |
|
| 450 | - |
|
| 451 | - $share->setSharedWith('[email protected]') |
|
| 452 | - ->setSharedBy($sharedBy) |
|
| 453 | - ->setShareOwner($owner) |
|
| 454 | - ->setPermissions(19) |
|
| 455 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 456 | - ->setExpirationDate(new \DateTime('2019-02-01T01:02:03')) |
|
| 457 | - ->setNode($node); |
|
| 458 | - |
|
| 459 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 460 | - $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 461 | - ->willReturn('http://localhost/'); |
|
| 462 | - |
|
| 463 | - $this->notifications->expects($this->once()) |
|
| 464 | - ->method('sendRemoteShare') |
|
| 465 | - ->with( |
|
| 466 | - $this->equalTo('token'), |
|
| 467 | - $this->equalTo('[email protected]'), |
|
| 468 | - $this->equalTo('myFile'), |
|
| 469 | - $this->anything(), |
|
| 470 | - $owner, |
|
| 471 | - $owner . '@http://localhost', |
|
| 472 | - $sharedBy, |
|
| 473 | - $sharedBy . '@http://localhost' |
|
| 474 | - )->willReturn(true); |
|
| 475 | - |
|
| 476 | - if ($owner === $sharedBy) { |
|
| 477 | - $this->provider->expects($this->never())->method('sendPermissionUpdate'); |
|
| 478 | - } else { |
|
| 479 | - $this->provider->expects($this->once())->method('sendPermissionUpdate'); |
|
| 480 | - } |
|
| 481 | - |
|
| 482 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 483 | - |
|
| 484 | - $this->contactsManager->expects($this->any()) |
|
| 485 | - ->method('search') |
|
| 486 | - ->willReturn([]); |
|
| 487 | - |
|
| 488 | - $share = $this->provider->create($share); |
|
| 489 | - |
|
| 490 | - $share->setPermissions(1); |
|
| 491 | - $share->setExpirationDate($expirationDate); |
|
| 492 | - $this->provider->update($share); |
|
| 493 | - |
|
| 494 | - $share = $this->provider->getShareById($share->getId()); |
|
| 495 | - |
|
| 496 | - $this->assertEquals(1, $share->getPermissions()); |
|
| 497 | - $this->assertEquals($expirationDate, $share->getExpirationDate()); |
|
| 498 | - } |
|
| 499 | - |
|
| 500 | - public static function dataTestUpdate(): array { |
|
| 501 | - return [ |
|
| 502 | - ['sharedBy', 'shareOwner', new \DateTime('2020-03-01T01:02:03')], |
|
| 503 | - ['shareOwner', 'shareOwner', null], |
|
| 504 | - ]; |
|
| 505 | - } |
|
| 506 | - |
|
| 507 | - public function testGetSharedBy(): void { |
|
| 508 | - $node = $this->createMock(File::class); |
|
| 509 | - $node->method('getId')->willReturn(42); |
|
| 510 | - $node->method('getName')->willReturn('myFile'); |
|
| 511 | - |
|
| 512 | - $this->addressHandler->expects($this->never())->method('splitUserRemote'); |
|
| 513 | - |
|
| 514 | - $this->addressHandler->method('generateRemoteURL') |
|
| 515 | - ->willReturn('remoteurl.com'); |
|
| 516 | - |
|
| 517 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 518 | - $this->notifications |
|
| 519 | - ->method('sendRemoteShare') |
|
| 520 | - ->willReturn(true); |
|
| 521 | - |
|
| 522 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 523 | - |
|
| 524 | - $this->contactsManager->expects($this->any()) |
|
| 525 | - ->method('search') |
|
| 526 | - ->willReturn([]); |
|
| 527 | - |
|
| 528 | - $share = $this->shareManager->newShare(); |
|
| 529 | - $share->setSharedWith('[email protected]') |
|
| 530 | - ->setSharedBy('sharedBy') |
|
| 531 | - ->setShareOwner('shareOwner') |
|
| 532 | - ->setPermissions(19) |
|
| 533 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 534 | - ->setNode($node); |
|
| 535 | - $this->provider->create($share); |
|
| 536 | - |
|
| 537 | - $share2 = $this->shareManager->newShare(); |
|
| 538 | - $share2->setSharedWith('[email protected]') |
|
| 539 | - ->setSharedBy('sharedBy2') |
|
| 540 | - ->setShareOwner('shareOwner') |
|
| 541 | - ->setPermissions(19) |
|
| 542 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 543 | - ->setNode($node); |
|
| 544 | - $this->provider->create($share2); |
|
| 545 | - |
|
| 546 | - $shares = $this->provider->getSharesBy('sharedBy', IShare::TYPE_REMOTE, null, false, -1, 0); |
|
| 547 | - |
|
| 548 | - $this->assertCount(1, $shares); |
|
| 549 | - $this->assertEquals('[email protected]', $shares[0]->getSharedWith()); |
|
| 550 | - $this->assertEquals('sharedBy', $shares[0]->getSharedBy()); |
|
| 551 | - } |
|
| 552 | - |
|
| 553 | - public function testGetSharedByWithNode(): void { |
|
| 554 | - $node = $this->createMock(File::class); |
|
| 555 | - $node->method('getId')->willReturn(42); |
|
| 556 | - $node->method('getName')->willReturn('myFile'); |
|
| 557 | - |
|
| 558 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 559 | - $this->notifications |
|
| 560 | - ->method('sendRemoteShare') |
|
| 561 | - ->willReturn(true); |
|
| 562 | - |
|
| 563 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 564 | - |
|
| 565 | - $this->addressHandler->method('generateRemoteURL') |
|
| 566 | - ->willReturn('remoteurl.com'); |
|
| 567 | - |
|
| 568 | - $this->contactsManager->expects($this->any()) |
|
| 569 | - ->method('search') |
|
| 570 | - ->willReturn([]); |
|
| 571 | - |
|
| 572 | - $share = $this->shareManager->newShare(); |
|
| 573 | - $share->setSharedWith('[email protected]') |
|
| 574 | - ->setSharedBy('sharedBy') |
|
| 575 | - ->setShareOwner('shareOwner') |
|
| 576 | - ->setPermissions(19) |
|
| 577 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 578 | - ->setNode($node); |
|
| 579 | - $this->provider->create($share); |
|
| 580 | - |
|
| 581 | - $node2 = $this->getMockBuilder(File::class)->getMock(); |
|
| 582 | - $node2->method('getId')->willReturn(43); |
|
| 583 | - $node2->method('getName')->willReturn('myOtherFile'); |
|
| 584 | - |
|
| 585 | - $share2 = $this->shareManager->newShare(); |
|
| 586 | - $share2->setSharedWith('[email protected]') |
|
| 587 | - ->setSharedBy('sharedBy') |
|
| 588 | - ->setShareOwner('shareOwner') |
|
| 589 | - ->setPermissions(19) |
|
| 590 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 591 | - ->setNode($node2); |
|
| 592 | - $this->provider->create($share2); |
|
| 593 | - |
|
| 594 | - $shares = $this->provider->getSharesBy('sharedBy', IShare::TYPE_REMOTE, $node2, false, -1, 0); |
|
| 595 | - |
|
| 596 | - $this->assertCount(1, $shares); |
|
| 597 | - $this->assertEquals(43, $shares[0]->getNodeId()); |
|
| 598 | - } |
|
| 599 | - |
|
| 600 | - public function testGetSharedByWithReshares(): void { |
|
| 601 | - $node = $this->createMock(File::class); |
|
| 602 | - $node->method('getId')->willReturn(42); |
|
| 603 | - $node->method('getName')->willReturn('myFile'); |
|
| 604 | - |
|
| 605 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 606 | - $this->notifications |
|
| 607 | - ->method('sendRemoteShare') |
|
| 608 | - ->willReturn(true); |
|
| 609 | - |
|
| 610 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 611 | - |
|
| 612 | - $this->addressHandler->method('generateRemoteURL') |
|
| 613 | - ->willReturn('remoteurl.com'); |
|
| 614 | - |
|
| 615 | - $this->contactsManager->expects($this->any()) |
|
| 616 | - ->method('search') |
|
| 617 | - ->willReturn([]); |
|
| 618 | - |
|
| 619 | - $share = $this->shareManager->newShare(); |
|
| 620 | - $share->setSharedWith('[email protected]') |
|
| 621 | - ->setSharedBy('shareOwner') |
|
| 622 | - ->setShareOwner('shareOwner') |
|
| 623 | - ->setPermissions(19) |
|
| 624 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 625 | - ->setNode($node); |
|
| 626 | - $this->provider->create($share); |
|
| 627 | - |
|
| 628 | - $share2 = $this->shareManager->newShare(); |
|
| 629 | - $share2->setSharedWith('[email protected]') |
|
| 630 | - ->setSharedBy('sharedBy') |
|
| 631 | - ->setShareOwner('shareOwner') |
|
| 632 | - ->setPermissions(19) |
|
| 633 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 634 | - ->setNode($node); |
|
| 635 | - $this->provider->create($share2); |
|
| 636 | - |
|
| 637 | - $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_REMOTE, null, true, -1, 0); |
|
| 638 | - |
|
| 639 | - $this->assertCount(2, $shares); |
|
| 640 | - } |
|
| 641 | - |
|
| 642 | - public function testGetSharedByWithLimit(): void { |
|
| 643 | - $node = $this->createMock(File::class); |
|
| 644 | - $node->method('getId')->willReturn(42); |
|
| 645 | - $node->method('getName')->willReturn('myFile'); |
|
| 646 | - |
|
| 647 | - $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 648 | - ->willReturnCallback(function ($uid) { |
|
| 649 | - if ($uid === '[email protected]') { |
|
| 650 | - return ['user', 'server.com']; |
|
| 651 | - } |
|
| 652 | - return ['user2', 'server.com']; |
|
| 653 | - }); |
|
| 654 | - |
|
| 655 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 656 | - $this->notifications |
|
| 657 | - ->method('sendRemoteShare') |
|
| 658 | - ->willReturn(true); |
|
| 659 | - |
|
| 660 | - $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 661 | - |
|
| 662 | - $this->addressHandler->method('generateRemoteURL') |
|
| 663 | - ->willReturn('remoteurl.com'); |
|
| 664 | - |
|
| 665 | - $this->contactsManager->expects($this->any()) |
|
| 666 | - ->method('search') |
|
| 667 | - ->willReturn([]); |
|
| 668 | - |
|
| 669 | - $share = $this->shareManager->newShare(); |
|
| 670 | - $share->setSharedWith('[email protected]') |
|
| 671 | - ->setSharedBy('sharedBy') |
|
| 672 | - ->setShareOwner('shareOwner') |
|
| 673 | - ->setPermissions(19) |
|
| 674 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 675 | - ->setNode($node); |
|
| 676 | - $this->provider->create($share); |
|
| 677 | - |
|
| 678 | - $share2 = $this->shareManager->newShare(); |
|
| 679 | - $share2->setSharedWith('[email protected]') |
|
| 680 | - ->setSharedBy('sharedBy') |
|
| 681 | - ->setShareOwner('shareOwner') |
|
| 682 | - ->setPermissions(19) |
|
| 683 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 684 | - ->setNode($node); |
|
| 685 | - $this->provider->create($share2); |
|
| 686 | - |
|
| 687 | - $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_REMOTE, null, true, 1, 1); |
|
| 688 | - |
|
| 689 | - $this->assertCount(1, $shares); |
|
| 690 | - $this->assertEquals('[email protected]', $shares[0]->getSharedWith()); |
|
| 691 | - } |
|
| 692 | - |
|
| 693 | - public static function dataDeleteUser(): array { |
|
| 694 | - return [ |
|
| 695 | - ['a', 'b', 'c', 'a', true], |
|
| 696 | - ['a', 'b', 'c', 'b', false], |
|
| 697 | - // The recipient is non local. |
|
| 698 | - ['a', 'b', 'c', 'c', false], |
|
| 699 | - ['a', 'b', 'c', 'd', false], |
|
| 700 | - ]; |
|
| 701 | - } |
|
| 702 | - |
|
| 703 | - /** |
|
| 704 | - * |
|
| 705 | - * @param string $owner The owner of the share (uid) |
|
| 706 | - * @param string $initiator The initiator of the share (uid) |
|
| 707 | - * @param string $recipient The recipient of the share (uid/gid/pass) |
|
| 708 | - * @param string $deletedUser The user that is deleted |
|
| 709 | - * @param bool $rowDeleted Is the row deleted in this setup |
|
| 710 | - */ |
|
| 711 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataDeleteUser')] |
|
| 712 | - public function testDeleteUser(string $owner, string $initiator, string $recipient, string $deletedUser, bool $rowDeleted): void { |
|
| 713 | - $qb = $this->connection->getQueryBuilder(); |
|
| 714 | - $qb->insert('share') |
|
| 715 | - ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE)) |
|
| 716 | - ->setValue('uid_owner', $qb->createNamedParameter($owner)) |
|
| 717 | - ->setValue('uid_initiator', $qb->createNamedParameter($initiator)) |
|
| 718 | - ->setValue('share_with', $qb->createNamedParameter($recipient)) |
|
| 719 | - ->setValue('item_type', $qb->createNamedParameter('file')) |
|
| 720 | - ->setValue('item_source', $qb->createNamedParameter(42)) |
|
| 721 | - ->setValue('file_source', $qb->createNamedParameter(42)) |
|
| 722 | - ->executeStatement(); |
|
| 723 | - |
|
| 724 | - $id = $qb->getLastInsertId(); |
|
| 725 | - |
|
| 726 | - $this->provider->userDeleted($deletedUser, IShare::TYPE_REMOTE); |
|
| 727 | - |
|
| 728 | - $qb = $this->connection->getQueryBuilder(); |
|
| 729 | - $qb->select('*') |
|
| 730 | - ->from('share') |
|
| 731 | - ->where( |
|
| 732 | - $qb->expr()->eq('id', $qb->createNamedParameter($id)) |
|
| 733 | - ); |
|
| 734 | - $cursor = $qb->executeQuery(); |
|
| 735 | - $data = $cursor->fetchAllAssociative(); |
|
| 736 | - $cursor->closeCursor(); |
|
| 737 | - |
|
| 738 | - $this->assertCount($rowDeleted ? 0 : 1, $data); |
|
| 739 | - } |
|
| 740 | - |
|
| 741 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsOutgoingServer2serverShareEnabled')] |
|
| 742 | - public function testIsOutgoingServer2serverShareEnabled(bool $internalOnly, string $isEnabled, bool $expected): void { |
|
| 743 | - $this->gsConfig->expects($this->once())->method('onlyInternalFederation') |
|
| 744 | - ->willReturn($internalOnly); |
|
| 745 | - $this->config->expects($this->any())->method('getAppValue') |
|
| 746 | - ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') |
|
| 747 | - ->willReturn($isEnabled); |
|
| 748 | - |
|
| 749 | - $this->assertSame($expected, |
|
| 750 | - $this->provider->isOutgoingServer2serverShareEnabled() |
|
| 751 | - ); |
|
| 752 | - } |
|
| 753 | - |
|
| 754 | - public static function dataTestIsOutgoingServer2serverShareEnabled(): array { |
|
| 755 | - return [ |
|
| 756 | - [false, 'yes', true], |
|
| 757 | - [false, 'no', false], |
|
| 758 | - [true, 'yes', false], |
|
| 759 | - [true, 'no', false], |
|
| 760 | - ]; |
|
| 761 | - } |
|
| 762 | - |
|
| 763 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsIncomingServer2serverShareEnabled')] |
|
| 764 | - public function testIsIncomingServer2serverShareEnabled(bool $onlyInternal, string $isEnabled, bool $expected): void { |
|
| 765 | - $this->gsConfig->expects($this->once())->method('onlyInternalFederation') |
|
| 766 | - ->willReturn($onlyInternal); |
|
| 767 | - $this->config->expects($this->any())->method('getAppValue') |
|
| 768 | - ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') |
|
| 769 | - ->willReturn($isEnabled); |
|
| 770 | - |
|
| 771 | - $this->assertSame($expected, |
|
| 772 | - $this->provider->isIncomingServer2serverShareEnabled() |
|
| 773 | - ); |
|
| 774 | - } |
|
| 775 | - |
|
| 776 | - public static function dataTestIsIncomingServer2serverShareEnabled(): array { |
|
| 777 | - return [ |
|
| 778 | - [false, 'yes', true], |
|
| 779 | - [false, 'no', false], |
|
| 780 | - [true, 'yes', false], |
|
| 781 | - [true, 'no', false], |
|
| 782 | - ]; |
|
| 783 | - } |
|
| 784 | - |
|
| 785 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsLookupServerQueriesEnabled')] |
|
| 786 | - public function testIsLookupServerQueriesEnabled(bool $gsEnabled, string $isEnabled, bool $expected): void { |
|
| 787 | - $this->gsConfig->expects($this->once())->method('isGlobalScaleEnabled') |
|
| 788 | - ->willReturn($gsEnabled); |
|
| 789 | - $this->config->expects($this->any())->method('getAppValue') |
|
| 790 | - ->with('files_sharing', 'lookupServerEnabled', 'no') |
|
| 791 | - ->willReturn($isEnabled); |
|
| 792 | - |
|
| 793 | - $this->assertSame($expected, |
|
| 794 | - $this->provider->isLookupServerQueriesEnabled() |
|
| 795 | - ); |
|
| 796 | - } |
|
| 797 | - |
|
| 798 | - |
|
| 799 | - public static function dataTestIsLookupServerQueriesEnabled(): array { |
|
| 800 | - return [ |
|
| 801 | - [true, 'yes', true], |
|
| 802 | - [true, 'no', true], |
|
| 803 | - // TODO: reenable if we use the lookup server for non-global scale |
|
| 804 | - // [false, 'yes', true], |
|
| 805 | - // [false, 'no', false], |
|
| 806 | - [false, 'no', false], |
|
| 807 | - [false, 'yes', false], |
|
| 808 | - ]; |
|
| 809 | - } |
|
| 810 | - |
|
| 811 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsLookupServerUploadEnabled')] |
|
| 812 | - public function testIsLookupServerUploadEnabled(bool $gsEnabled, string $isEnabled, bool $expected): void { |
|
| 813 | - $this->gsConfig->expects($this->once())->method('isGlobalScaleEnabled') |
|
| 814 | - ->willReturn($gsEnabled); |
|
| 815 | - $this->config->expects($this->any())->method('getAppValue') |
|
| 816 | - ->with('files_sharing', 'lookupServerUploadEnabled', 'no') |
|
| 817 | - ->willReturn($isEnabled); |
|
| 818 | - |
|
| 819 | - $this->assertSame($expected, |
|
| 820 | - $this->provider->isLookupServerUploadEnabled() |
|
| 821 | - ); |
|
| 822 | - } |
|
| 823 | - |
|
| 824 | - public static function dataTestIsLookupServerUploadEnabled(): array { |
|
| 825 | - return [ |
|
| 826 | - [true, 'yes', false], |
|
| 827 | - [true, 'no', false], |
|
| 828 | - // TODO: reenable if we use the lookup server again |
|
| 829 | - // [false, 'yes', true], |
|
| 830 | - // [false, 'no', false], |
|
| 831 | - [false, 'yes', false], |
|
| 832 | - [false, 'no', false], |
|
| 833 | - ]; |
|
| 834 | - } |
|
| 835 | - |
|
| 836 | - public function testGetSharesInFolder(): void { |
|
| 837 | - $userManager = Server::get(IUserManager::class); |
|
| 838 | - $rootFolder = Server::get(IRootFolder::class); |
|
| 839 | - |
|
| 840 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 841 | - $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 842 | - |
|
| 843 | - $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 844 | - $file1 = $folder1->newFile('bar1'); |
|
| 845 | - $file2 = $folder1->newFile('bar2'); |
|
| 846 | - |
|
| 847 | - $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 848 | - $this->notifications |
|
| 849 | - ->method('sendRemoteShare') |
|
| 850 | - ->willReturn(true); |
|
| 851 | - |
|
| 852 | - $this->addressHandler->method('generateRemoteURL') |
|
| 853 | - ->willReturn('remoteurl.com'); |
|
| 854 | - |
|
| 855 | - $this->contactsManager->expects($this->any()) |
|
| 856 | - ->method('search') |
|
| 857 | - ->willReturn([]); |
|
| 858 | - |
|
| 859 | - $share1 = $this->shareManager->newShare(); |
|
| 860 | - $share1->setSharedWith('[email protected]') |
|
| 861 | - ->setSharedBy($u1->getUID()) |
|
| 862 | - ->setShareOwner($u1->getUID()) |
|
| 863 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 864 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 865 | - ->setNode($file1); |
|
| 866 | - $this->provider->create($share1); |
|
| 867 | - |
|
| 868 | - $share2 = $this->shareManager->newShare(); |
|
| 869 | - $share2->setSharedWith('[email protected]') |
|
| 870 | - ->setSharedBy($u2->getUID()) |
|
| 871 | - ->setShareOwner($u1->getUID()) |
|
| 872 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 873 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 874 | - ->setNode($file2); |
|
| 875 | - $this->provider->create($share2); |
|
| 876 | - |
|
| 877 | - $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, false); |
|
| 878 | - $this->assertCount(1, $result); |
|
| 879 | - $this->assertCount(1, $result[$file1->getId()]); |
|
| 880 | - |
|
| 881 | - $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, true); |
|
| 882 | - $this->assertCount(2, $result); |
|
| 883 | - $this->assertCount(1, $result[$file1->getId()]); |
|
| 884 | - $this->assertCount(1, $result[$file2->getId()]); |
|
| 885 | - |
|
| 886 | - $u1->delete(); |
|
| 887 | - $u2->delete(); |
|
| 888 | - } |
|
| 889 | - |
|
| 890 | - public function testGetAccessList(): void { |
|
| 891 | - $userManager = Server::get(IUserManager::class); |
|
| 892 | - $rootFolder = Server::get(IRootFolder::class); |
|
| 893 | - |
|
| 894 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 895 | - |
|
| 896 | - $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 897 | - $file1 = $folder1->newFile('bar1'); |
|
| 898 | - |
|
| 899 | - $this->tokenHandler->expects($this->exactly(2)) |
|
| 900 | - ->method('generateToken') |
|
| 901 | - ->willReturnOnConsecutiveCalls('token1', 'token2'); |
|
| 902 | - $this->notifications->expects($this->atLeastOnce()) |
|
| 903 | - ->method('sendRemoteShare') |
|
| 904 | - ->willReturn(true); |
|
| 905 | - |
|
| 906 | - $this->contactsManager->expects($this->any()) |
|
| 907 | - ->method('search') |
|
| 908 | - ->willReturn([]); |
|
| 909 | - |
|
| 910 | - $result = $this->provider->getAccessList([$file1], true); |
|
| 911 | - $this->assertEquals(['remote' => []], $result); |
|
| 912 | - |
|
| 913 | - $result = $this->provider->getAccessList([$file1], false); |
|
| 914 | - $this->assertEquals(['remote' => false], $result); |
|
| 915 | - |
|
| 916 | - $this->addressHandler->method('generateRemoteURL') |
|
| 917 | - ->willReturn('remoteurl.com'); |
|
| 918 | - |
|
| 919 | - $share1 = $this->shareManager->newShare(); |
|
| 920 | - $share1->setSharedWith('[email protected]') |
|
| 921 | - ->setSharedBy($u1->getUID()) |
|
| 922 | - ->setShareOwner($u1->getUID()) |
|
| 923 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 924 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 925 | - ->setNode($file1); |
|
| 926 | - $this->provider->create($share1); |
|
| 927 | - |
|
| 928 | - $share2 = $this->shareManager->newShare(); |
|
| 929 | - $share2->setSharedWith('foobar@localhost') |
|
| 930 | - ->setSharedBy($u1->getUID()) |
|
| 931 | - ->setShareOwner($u1->getUID()) |
|
| 932 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 933 | - ->setShareType(IShare::TYPE_REMOTE) |
|
| 934 | - ->setNode($file1); |
|
| 935 | - $this->provider->create($share2); |
|
| 936 | - |
|
| 937 | - $result = $this->provider->getAccessList([$file1], true); |
|
| 938 | - $this->assertEquals(['remote' => [ |
|
| 939 | - '[email protected]' => [ |
|
| 940 | - 'token' => 'token1', |
|
| 941 | - 'node_id' => $file1->getId(), |
|
| 942 | - ], |
|
| 943 | - 'foobar@localhost' => [ |
|
| 944 | - 'token' => 'token2', |
|
| 945 | - 'node_id' => $file1->getId(), |
|
| 946 | - ], |
|
| 947 | - ]], $result); |
|
| 948 | - |
|
| 949 | - $result = $this->provider->getAccessList([$file1], false); |
|
| 950 | - $this->assertEquals(['remote' => true], $result); |
|
| 951 | - |
|
| 952 | - $u1->delete(); |
|
| 953 | - } |
|
| 42 | + protected IDBConnection $connection; |
|
| 43 | + protected AddressHandler&MockObject $addressHandler; |
|
| 44 | + protected Notifications&MockObject $notifications; |
|
| 45 | + protected TokenHandler&MockObject $tokenHandler; |
|
| 46 | + protected IL10N $l; |
|
| 47 | + protected LoggerInterface $logger; |
|
| 48 | + protected IRootFolder&MockObject $rootFolder; |
|
| 49 | + protected IConfig&MockObject $config; |
|
| 50 | + protected IUserManager&MockObject $userManager; |
|
| 51 | + protected \OCP\GlobalScale\IConfig&MockObject $gsConfig; |
|
| 52 | + protected IManager $shareManager; |
|
| 53 | + protected FederatedShareProvider $provider; |
|
| 54 | + protected IContactsManager&MockObject $contactsManager; |
|
| 55 | + private ICloudIdManager $cloudIdManager; |
|
| 56 | + private ICloudFederationProviderManager&MockObject $cloudFederationProviderManager; |
|
| 57 | + |
|
| 58 | + protected function setUp(): void { |
|
| 59 | + parent::setUp(); |
|
| 60 | + |
|
| 61 | + $this->connection = Server::get(IDBConnection::class); |
|
| 62 | + $this->notifications = $this->createMock(Notifications::class); |
|
| 63 | + $this->tokenHandler = $this->createMock(TokenHandler::class); |
|
| 64 | + $this->l = $this->createMock(IL10N::class); |
|
| 65 | + $this->l->method('t') |
|
| 66 | + ->willReturnCallback(function ($text, $parameters = []) { |
|
| 67 | + return vsprintf($text, $parameters); |
|
| 68 | + }); |
|
| 69 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
| 70 | + $this->rootFolder = $this->createMock(IRootFolder::class); |
|
| 71 | + $this->config = $this->createMock(IConfig::class); |
|
| 72 | + $this->userManager = $this->createMock(IUserManager::class); |
|
| 73 | + //$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); |
|
| 74 | + $this->addressHandler = $this->createMock(AddressHandler::class); |
|
| 75 | + $this->contactsManager = $this->createMock(IContactsManager::class); |
|
| 76 | + $this->cloudIdManager = new CloudIdManager( |
|
| 77 | + $this->createMock(ICacheFactory::class), |
|
| 78 | + $this->createMock(IEventDispatcher::class), |
|
| 79 | + $this->contactsManager, |
|
| 80 | + $this->createMock(IURLGenerator::class), |
|
| 81 | + $this->userManager, |
|
| 82 | + ); |
|
| 83 | + $this->gsConfig = $this->createMock(\OCP\GlobalScale\IConfig::class); |
|
| 84 | + |
|
| 85 | + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); |
|
| 86 | + |
|
| 87 | + $this->cloudFederationProviderManager = $this->createMock(ICloudFederationProviderManager::class); |
|
| 88 | + |
|
| 89 | + $this->provider = new FederatedShareProvider( |
|
| 90 | + $this->connection, |
|
| 91 | + $this->addressHandler, |
|
| 92 | + $this->notifications, |
|
| 93 | + $this->tokenHandler, |
|
| 94 | + $this->l, |
|
| 95 | + $this->rootFolder, |
|
| 96 | + $this->config, |
|
| 97 | + $this->userManager, |
|
| 98 | + $this->cloudIdManager, |
|
| 99 | + $this->gsConfig, |
|
| 100 | + $this->cloudFederationProviderManager, |
|
| 101 | + $this->logger, |
|
| 102 | + ); |
|
| 103 | + |
|
| 104 | + $this->shareManager = Server::get(IManager::class); |
|
| 105 | + } |
|
| 106 | + |
|
| 107 | + protected function tearDown(): void { |
|
| 108 | + $this->connection->getQueryBuilder()->delete('share')->executeStatement(); |
|
| 109 | + |
|
| 110 | + parent::tearDown(); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + public static function dataTestCreate(): array { |
|
| 114 | + return [ |
|
| 115 | + [null, null], |
|
| 116 | + [new \DateTime('2020-03-01T01:02:03'), '2020-03-01 01:02:03'], |
|
| 117 | + ]; |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCreate')] |
|
| 121 | + public function testCreate(?\DateTime $expirationDate, ?string $expectedDataDate): void { |
|
| 122 | + $share = $this->shareManager->newShare(); |
|
| 123 | + |
|
| 124 | + /** @var File&MockObject $node */ |
|
| 125 | + $node = $this->createMock(File::class); |
|
| 126 | + $node->method('getId')->willReturn(42); |
|
| 127 | + $node->method('getName')->willReturn('myFile'); |
|
| 128 | + |
|
| 129 | + $share->setSharedWith('[email protected]') |
|
| 130 | + ->setSharedBy('sharedBy') |
|
| 131 | + ->setShareOwner('shareOwner') |
|
| 132 | + ->setPermissions(19) |
|
| 133 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 134 | + ->setExpirationDate($expirationDate) |
|
| 135 | + ->setNode($node); |
|
| 136 | + |
|
| 137 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 138 | + |
|
| 139 | + $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 140 | + ->willReturn('http://localhost/'); |
|
| 141 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 142 | + ->willReturn(['user', 'server.com']); |
|
| 143 | + |
|
| 144 | + $this->notifications->expects($this->once()) |
|
| 145 | + ->method('sendRemoteShare') |
|
| 146 | + ->with( |
|
| 147 | + $this->equalTo('token'), |
|
| 148 | + $this->equalTo('[email protected]'), |
|
| 149 | + $this->equalTo('myFile'), |
|
| 150 | + $this->anything(), |
|
| 151 | + 'shareOwner', |
|
| 152 | + 'shareOwner@http://localhost', |
|
| 153 | + 'sharedBy', |
|
| 154 | + 'sharedBy@http://localhost' |
|
| 155 | + ) |
|
| 156 | + ->willReturn(true); |
|
| 157 | + |
|
| 158 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 159 | + |
|
| 160 | + $this->contactsManager->expects($this->any()) |
|
| 161 | + ->method('search') |
|
| 162 | + ->willReturn([]); |
|
| 163 | + |
|
| 164 | + $share = $this->provider->create($share); |
|
| 165 | + |
|
| 166 | + $qb = $this->connection->getQueryBuilder(); |
|
| 167 | + $stmt = $qb->select('*') |
|
| 168 | + ->from('share') |
|
| 169 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 170 | + ->executeQuery(); |
|
| 171 | + |
|
| 172 | + $data = $stmt->fetchAssociative(); |
|
| 173 | + $stmt->closeCursor(); |
|
| 174 | + |
|
| 175 | + $expected = [ |
|
| 176 | + 'share_type' => IShare::TYPE_REMOTE, |
|
| 177 | + 'share_with' => '[email protected]', |
|
| 178 | + 'uid_owner' => 'shareOwner', |
|
| 179 | + 'uid_initiator' => 'sharedBy', |
|
| 180 | + 'item_type' => 'file', |
|
| 181 | + 'item_source' => 42, |
|
| 182 | + 'file_source' => 42, |
|
| 183 | + 'permissions' => 19, |
|
| 184 | + 'accepted' => 0, |
|
| 185 | + 'token' => 'token', |
|
| 186 | + 'expiration' => $expectedDataDate, |
|
| 187 | + ]; |
|
| 188 | + foreach (array_keys($expected) as $key) { |
|
| 189 | + $this->assertEquals($expected[$key], $data[$key], "Assert that value for key '$key' is the same"); |
|
| 190 | + } |
|
| 191 | + |
|
| 192 | + $this->assertEquals($data['id'], $share->getId()); |
|
| 193 | + $this->assertEquals(IShare::TYPE_REMOTE, $share->getShareType()); |
|
| 194 | + $this->assertEquals('[email protected]', $share->getSharedWith()); |
|
| 195 | + $this->assertEquals('sharedBy', $share->getSharedBy()); |
|
| 196 | + $this->assertEquals('shareOwner', $share->getShareOwner()); |
|
| 197 | + $this->assertEquals('file', $share->getNodeType()); |
|
| 198 | + $this->assertEquals(42, $share->getNodeId()); |
|
| 199 | + $this->assertEquals(19, $share->getPermissions()); |
|
| 200 | + $this->assertEquals('token', $share->getToken()); |
|
| 201 | + $this->assertEquals($expirationDate, $share->getExpirationDate()); |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + public function testCreateCouldNotFindServer(): void { |
|
| 205 | + $share = $this->shareManager->newShare(); |
|
| 206 | + |
|
| 207 | + $node = $this->createMock(File::class); |
|
| 208 | + $node->method('getId')->willReturn(42); |
|
| 209 | + $node->method('getName')->willReturn('myFile'); |
|
| 210 | + |
|
| 211 | + $share->setSharedWith('[email protected]') |
|
| 212 | + ->setSharedBy('sharedBy') |
|
| 213 | + ->setShareOwner('shareOwner') |
|
| 214 | + ->setPermissions(19) |
|
| 215 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 216 | + ->setNode($node); |
|
| 217 | + |
|
| 218 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 219 | + |
|
| 220 | + $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 221 | + ->willReturn('http://localhost/'); |
|
| 222 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 223 | + ->willReturn(['user', 'server.com']); |
|
| 224 | + |
|
| 225 | + $this->notifications->expects($this->once()) |
|
| 226 | + ->method('sendRemoteShare') |
|
| 227 | + ->with( |
|
| 228 | + $this->equalTo('token'), |
|
| 229 | + $this->equalTo('[email protected]'), |
|
| 230 | + $this->equalTo('myFile'), |
|
| 231 | + $this->anything(), |
|
| 232 | + 'shareOwner', |
|
| 233 | + 'shareOwner@http://localhost', |
|
| 234 | + 'sharedBy', |
|
| 235 | + 'sharedBy@http://localhost' |
|
| 236 | + )->willReturn(false); |
|
| 237 | + |
|
| 238 | + $this->rootFolder->method('getById') |
|
| 239 | + ->with('42') |
|
| 240 | + ->willReturn([$node]); |
|
| 241 | + |
|
| 242 | + $this->contactsManager->expects($this->any()) |
|
| 243 | + ->method('search') |
|
| 244 | + ->willReturn([]); |
|
| 245 | + |
|
| 246 | + try { |
|
| 247 | + $share = $this->provider->create($share); |
|
| 248 | + $this->fail(); |
|
| 249 | + } catch (\Exception $e) { |
|
| 250 | + $this->assertEquals('Sharing myFile failed, could not find [email protected], maybe the server is currently unreachable or uses a self-signed certificate.', $e->getMessage()); |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + $qb = $this->connection->getQueryBuilder(); |
|
| 254 | + $stmt = $qb->select('*') |
|
| 255 | + ->from('share') |
|
| 256 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 257 | + ->executeQuery(); |
|
| 258 | + |
|
| 259 | + $data = $stmt->fetchAssociative(); |
|
| 260 | + $stmt->closeCursor(); |
|
| 261 | + |
|
| 262 | + $this->assertFalse($data); |
|
| 263 | + } |
|
| 264 | + |
|
| 265 | + public function testCreateException(): void { |
|
| 266 | + $share = $this->shareManager->newShare(); |
|
| 267 | + |
|
| 268 | + $node = $this->createMock(File::class); |
|
| 269 | + $node->method('getId')->willReturn(42); |
|
| 270 | + $node->method('getName')->willReturn('myFile'); |
|
| 271 | + |
|
| 272 | + $share->setSharedWith('[email protected]') |
|
| 273 | + ->setSharedBy('sharedBy') |
|
| 274 | + ->setShareOwner('shareOwner') |
|
| 275 | + ->setPermissions(19) |
|
| 276 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 277 | + ->setNode($node); |
|
| 278 | + |
|
| 279 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 280 | + |
|
| 281 | + $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 282 | + ->willReturn('http://localhost/'); |
|
| 283 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 284 | + ->willReturn(['user', 'server.com']); |
|
| 285 | + |
|
| 286 | + $this->notifications->expects($this->once()) |
|
| 287 | + ->method('sendRemoteShare') |
|
| 288 | + ->with( |
|
| 289 | + $this->equalTo('token'), |
|
| 290 | + $this->equalTo('[email protected]'), |
|
| 291 | + $this->equalTo('myFile'), |
|
| 292 | + $this->anything(), |
|
| 293 | + 'shareOwner', |
|
| 294 | + 'shareOwner@http://localhost', |
|
| 295 | + 'sharedBy', |
|
| 296 | + 'sharedBy@http://localhost' |
|
| 297 | + )->willThrowException(new \Exception('dummy')); |
|
| 298 | + |
|
| 299 | + $this->rootFolder->method('getById') |
|
| 300 | + ->with('42') |
|
| 301 | + ->willReturn([$node]); |
|
| 302 | + |
|
| 303 | + $this->contactsManager->expects($this->any()) |
|
| 304 | + ->method('search') |
|
| 305 | + ->willReturn([]); |
|
| 306 | + |
|
| 307 | + try { |
|
| 308 | + $share = $this->provider->create($share); |
|
| 309 | + $this->fail(); |
|
| 310 | + } catch (\Exception $e) { |
|
| 311 | + $this->assertEquals('Sharing myFile failed, could not find [email protected], maybe the server is currently unreachable or uses a self-signed certificate.', $e->getMessage()); |
|
| 312 | + } |
|
| 313 | + |
|
| 314 | + $qb = $this->connection->getQueryBuilder(); |
|
| 315 | + $stmt = $qb->select('*') |
|
| 316 | + ->from('share') |
|
| 317 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 318 | + ->executeQuery(); |
|
| 319 | + |
|
| 320 | + $data = $stmt->fetchAssociative(); |
|
| 321 | + $stmt->closeCursor(); |
|
| 322 | + |
|
| 323 | + $this->assertFalse($data); |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + public function testCreateShareWithSelf(): void { |
|
| 327 | + $share = $this->shareManager->newShare(); |
|
| 328 | + |
|
| 329 | + $node = $this->createMock(File::class); |
|
| 330 | + $node->method('getId')->willReturn(42); |
|
| 331 | + $node->method('getName')->willReturn('myFile'); |
|
| 332 | + |
|
| 333 | + $this->addressHandler->expects($this->any())->method('compareAddresses') |
|
| 334 | + ->willReturn(true); |
|
| 335 | + |
|
| 336 | + $shareWith = 'sharedBy@localhost'; |
|
| 337 | + |
|
| 338 | + $share->setSharedWith($shareWith) |
|
| 339 | + ->setSharedBy('sharedBy') |
|
| 340 | + ->setShareOwner('shareOwner') |
|
| 341 | + ->setPermissions(19) |
|
| 342 | + ->setNode($node); |
|
| 343 | + |
|
| 344 | + $this->contactsManager->expects($this->any()) |
|
| 345 | + ->method('search') |
|
| 346 | + ->willReturn([]); |
|
| 347 | + |
|
| 348 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 349 | + |
|
| 350 | + try { |
|
| 351 | + $share = $this->provider->create($share); |
|
| 352 | + $this->fail(); |
|
| 353 | + } catch (\Exception $e) { |
|
| 354 | + $this->assertEquals('Not allowed to create a federated share to the same account', $e->getMessage()); |
|
| 355 | + } |
|
| 356 | + |
|
| 357 | + $qb = $this->connection->getQueryBuilder(); |
|
| 358 | + $stmt = $qb->select('*') |
|
| 359 | + ->from('share') |
|
| 360 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 361 | + ->executeQuery(); |
|
| 362 | + |
|
| 363 | + $data = $stmt->fetchAssociative(); |
|
| 364 | + $stmt->closeCursor(); |
|
| 365 | + |
|
| 366 | + $this->assertFalse($data); |
|
| 367 | + } |
|
| 368 | + |
|
| 369 | + public function testCreateAlreadyShared(): void { |
|
| 370 | + $share = $this->shareManager->newShare(); |
|
| 371 | + |
|
| 372 | + $node = $this->createMock(File::class); |
|
| 373 | + $node->method('getId')->willReturn(42); |
|
| 374 | + $node->method('getName')->willReturn('myFile'); |
|
| 375 | + |
|
| 376 | + |
|
| 377 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 378 | + ->willReturn(['user', 'server.com']); |
|
| 379 | + |
|
| 380 | + $share->setSharedWith('[email protected]') |
|
| 381 | + ->setSharedBy('sharedBy') |
|
| 382 | + ->setShareOwner('shareOwner') |
|
| 383 | + ->setPermissions(19) |
|
| 384 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 385 | + ->setNode($node); |
|
| 386 | + |
|
| 387 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 388 | + |
|
| 389 | + $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 390 | + ->willReturn('http://localhost/'); |
|
| 391 | + |
|
| 392 | + $this->notifications->expects($this->once()) |
|
| 393 | + ->method('sendRemoteShare') |
|
| 394 | + ->with( |
|
| 395 | + $this->equalTo('token'), |
|
| 396 | + $this->equalTo('[email protected]'), |
|
| 397 | + $this->equalTo('myFile'), |
|
| 398 | + $this->anything(), |
|
| 399 | + 'shareOwner', |
|
| 400 | + 'shareOwner@http://localhost', |
|
| 401 | + 'sharedBy', |
|
| 402 | + 'sharedBy@http://localhost' |
|
| 403 | + )->willReturn(true); |
|
| 404 | + |
|
| 405 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 406 | + |
|
| 407 | + $this->contactsManager->expects($this->any()) |
|
| 408 | + ->method('search') |
|
| 409 | + ->willReturn([]); |
|
| 410 | + |
|
| 411 | + $this->provider->create($share); |
|
| 412 | + |
|
| 413 | + try { |
|
| 414 | + $this->provider->create($share); |
|
| 415 | + } catch (\Exception $e) { |
|
| 416 | + $this->assertEquals('Sharing myFile failed, because this item is already shared with the account [email protected]', $e->getMessage()); |
|
| 417 | + } |
|
| 418 | + } |
|
| 419 | + |
|
| 420 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUpdate')] |
|
| 421 | + public function testUpdate(string $owner, string $sharedBy, ?\DateTime $expirationDate): void { |
|
| 422 | + $this->provider = $this->getMockBuilder(FederatedShareProvider::class) |
|
| 423 | + ->setConstructorArgs( |
|
| 424 | + [ |
|
| 425 | + $this->connection, |
|
| 426 | + $this->addressHandler, |
|
| 427 | + $this->notifications, |
|
| 428 | + $this->tokenHandler, |
|
| 429 | + $this->l, |
|
| 430 | + $this->rootFolder, |
|
| 431 | + $this->config, |
|
| 432 | + $this->userManager, |
|
| 433 | + $this->cloudIdManager, |
|
| 434 | + $this->gsConfig, |
|
| 435 | + $this->cloudFederationProviderManager, |
|
| 436 | + $this->logger, |
|
| 437 | + ] |
|
| 438 | + ) |
|
| 439 | + ->onlyMethods(['sendPermissionUpdate']) |
|
| 440 | + ->getMock(); |
|
| 441 | + |
|
| 442 | + $share = $this->shareManager->newShare(); |
|
| 443 | + |
|
| 444 | + $node = $this->createMock(File::class); |
|
| 445 | + $node->method('getId')->willReturn(42); |
|
| 446 | + $node->method('getName')->willReturn('myFile'); |
|
| 447 | + |
|
| 448 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 449 | + ->willReturn(['user', 'server.com']); |
|
| 450 | + |
|
| 451 | + $share->setSharedWith('[email protected]') |
|
| 452 | + ->setSharedBy($sharedBy) |
|
| 453 | + ->setShareOwner($owner) |
|
| 454 | + ->setPermissions(19) |
|
| 455 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 456 | + ->setExpirationDate(new \DateTime('2019-02-01T01:02:03')) |
|
| 457 | + ->setNode($node); |
|
| 458 | + |
|
| 459 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 460 | + $this->addressHandler->expects($this->any())->method('generateRemoteURL') |
|
| 461 | + ->willReturn('http://localhost/'); |
|
| 462 | + |
|
| 463 | + $this->notifications->expects($this->once()) |
|
| 464 | + ->method('sendRemoteShare') |
|
| 465 | + ->with( |
|
| 466 | + $this->equalTo('token'), |
|
| 467 | + $this->equalTo('[email protected]'), |
|
| 468 | + $this->equalTo('myFile'), |
|
| 469 | + $this->anything(), |
|
| 470 | + $owner, |
|
| 471 | + $owner . '@http://localhost', |
|
| 472 | + $sharedBy, |
|
| 473 | + $sharedBy . '@http://localhost' |
|
| 474 | + )->willReturn(true); |
|
| 475 | + |
|
| 476 | + if ($owner === $sharedBy) { |
|
| 477 | + $this->provider->expects($this->never())->method('sendPermissionUpdate'); |
|
| 478 | + } else { |
|
| 479 | + $this->provider->expects($this->once())->method('sendPermissionUpdate'); |
|
| 480 | + } |
|
| 481 | + |
|
| 482 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 483 | + |
|
| 484 | + $this->contactsManager->expects($this->any()) |
|
| 485 | + ->method('search') |
|
| 486 | + ->willReturn([]); |
|
| 487 | + |
|
| 488 | + $share = $this->provider->create($share); |
|
| 489 | + |
|
| 490 | + $share->setPermissions(1); |
|
| 491 | + $share->setExpirationDate($expirationDate); |
|
| 492 | + $this->provider->update($share); |
|
| 493 | + |
|
| 494 | + $share = $this->provider->getShareById($share->getId()); |
|
| 495 | + |
|
| 496 | + $this->assertEquals(1, $share->getPermissions()); |
|
| 497 | + $this->assertEquals($expirationDate, $share->getExpirationDate()); |
|
| 498 | + } |
|
| 499 | + |
|
| 500 | + public static function dataTestUpdate(): array { |
|
| 501 | + return [ |
|
| 502 | + ['sharedBy', 'shareOwner', new \DateTime('2020-03-01T01:02:03')], |
|
| 503 | + ['shareOwner', 'shareOwner', null], |
|
| 504 | + ]; |
|
| 505 | + } |
|
| 506 | + |
|
| 507 | + public function testGetSharedBy(): void { |
|
| 508 | + $node = $this->createMock(File::class); |
|
| 509 | + $node->method('getId')->willReturn(42); |
|
| 510 | + $node->method('getName')->willReturn('myFile'); |
|
| 511 | + |
|
| 512 | + $this->addressHandler->expects($this->never())->method('splitUserRemote'); |
|
| 513 | + |
|
| 514 | + $this->addressHandler->method('generateRemoteURL') |
|
| 515 | + ->willReturn('remoteurl.com'); |
|
| 516 | + |
|
| 517 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 518 | + $this->notifications |
|
| 519 | + ->method('sendRemoteShare') |
|
| 520 | + ->willReturn(true); |
|
| 521 | + |
|
| 522 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 523 | + |
|
| 524 | + $this->contactsManager->expects($this->any()) |
|
| 525 | + ->method('search') |
|
| 526 | + ->willReturn([]); |
|
| 527 | + |
|
| 528 | + $share = $this->shareManager->newShare(); |
|
| 529 | + $share->setSharedWith('[email protected]') |
|
| 530 | + ->setSharedBy('sharedBy') |
|
| 531 | + ->setShareOwner('shareOwner') |
|
| 532 | + ->setPermissions(19) |
|
| 533 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 534 | + ->setNode($node); |
|
| 535 | + $this->provider->create($share); |
|
| 536 | + |
|
| 537 | + $share2 = $this->shareManager->newShare(); |
|
| 538 | + $share2->setSharedWith('[email protected]') |
|
| 539 | + ->setSharedBy('sharedBy2') |
|
| 540 | + ->setShareOwner('shareOwner') |
|
| 541 | + ->setPermissions(19) |
|
| 542 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 543 | + ->setNode($node); |
|
| 544 | + $this->provider->create($share2); |
|
| 545 | + |
|
| 546 | + $shares = $this->provider->getSharesBy('sharedBy', IShare::TYPE_REMOTE, null, false, -1, 0); |
|
| 547 | + |
|
| 548 | + $this->assertCount(1, $shares); |
|
| 549 | + $this->assertEquals('[email protected]', $shares[0]->getSharedWith()); |
|
| 550 | + $this->assertEquals('sharedBy', $shares[0]->getSharedBy()); |
|
| 551 | + } |
|
| 552 | + |
|
| 553 | + public function testGetSharedByWithNode(): void { |
|
| 554 | + $node = $this->createMock(File::class); |
|
| 555 | + $node->method('getId')->willReturn(42); |
|
| 556 | + $node->method('getName')->willReturn('myFile'); |
|
| 557 | + |
|
| 558 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 559 | + $this->notifications |
|
| 560 | + ->method('sendRemoteShare') |
|
| 561 | + ->willReturn(true); |
|
| 562 | + |
|
| 563 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 564 | + |
|
| 565 | + $this->addressHandler->method('generateRemoteURL') |
|
| 566 | + ->willReturn('remoteurl.com'); |
|
| 567 | + |
|
| 568 | + $this->contactsManager->expects($this->any()) |
|
| 569 | + ->method('search') |
|
| 570 | + ->willReturn([]); |
|
| 571 | + |
|
| 572 | + $share = $this->shareManager->newShare(); |
|
| 573 | + $share->setSharedWith('[email protected]') |
|
| 574 | + ->setSharedBy('sharedBy') |
|
| 575 | + ->setShareOwner('shareOwner') |
|
| 576 | + ->setPermissions(19) |
|
| 577 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 578 | + ->setNode($node); |
|
| 579 | + $this->provider->create($share); |
|
| 580 | + |
|
| 581 | + $node2 = $this->getMockBuilder(File::class)->getMock(); |
|
| 582 | + $node2->method('getId')->willReturn(43); |
|
| 583 | + $node2->method('getName')->willReturn('myOtherFile'); |
|
| 584 | + |
|
| 585 | + $share2 = $this->shareManager->newShare(); |
|
| 586 | + $share2->setSharedWith('[email protected]') |
|
| 587 | + ->setSharedBy('sharedBy') |
|
| 588 | + ->setShareOwner('shareOwner') |
|
| 589 | + ->setPermissions(19) |
|
| 590 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 591 | + ->setNode($node2); |
|
| 592 | + $this->provider->create($share2); |
|
| 593 | + |
|
| 594 | + $shares = $this->provider->getSharesBy('sharedBy', IShare::TYPE_REMOTE, $node2, false, -1, 0); |
|
| 595 | + |
|
| 596 | + $this->assertCount(1, $shares); |
|
| 597 | + $this->assertEquals(43, $shares[0]->getNodeId()); |
|
| 598 | + } |
|
| 599 | + |
|
| 600 | + public function testGetSharedByWithReshares(): void { |
|
| 601 | + $node = $this->createMock(File::class); |
|
| 602 | + $node->method('getId')->willReturn(42); |
|
| 603 | + $node->method('getName')->willReturn('myFile'); |
|
| 604 | + |
|
| 605 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 606 | + $this->notifications |
|
| 607 | + ->method('sendRemoteShare') |
|
| 608 | + ->willReturn(true); |
|
| 609 | + |
|
| 610 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 611 | + |
|
| 612 | + $this->addressHandler->method('generateRemoteURL') |
|
| 613 | + ->willReturn('remoteurl.com'); |
|
| 614 | + |
|
| 615 | + $this->contactsManager->expects($this->any()) |
|
| 616 | + ->method('search') |
|
| 617 | + ->willReturn([]); |
|
| 618 | + |
|
| 619 | + $share = $this->shareManager->newShare(); |
|
| 620 | + $share->setSharedWith('[email protected]') |
|
| 621 | + ->setSharedBy('shareOwner') |
|
| 622 | + ->setShareOwner('shareOwner') |
|
| 623 | + ->setPermissions(19) |
|
| 624 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 625 | + ->setNode($node); |
|
| 626 | + $this->provider->create($share); |
|
| 627 | + |
|
| 628 | + $share2 = $this->shareManager->newShare(); |
|
| 629 | + $share2->setSharedWith('[email protected]') |
|
| 630 | + ->setSharedBy('sharedBy') |
|
| 631 | + ->setShareOwner('shareOwner') |
|
| 632 | + ->setPermissions(19) |
|
| 633 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 634 | + ->setNode($node); |
|
| 635 | + $this->provider->create($share2); |
|
| 636 | + |
|
| 637 | + $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_REMOTE, null, true, -1, 0); |
|
| 638 | + |
|
| 639 | + $this->assertCount(2, $shares); |
|
| 640 | + } |
|
| 641 | + |
|
| 642 | + public function testGetSharedByWithLimit(): void { |
|
| 643 | + $node = $this->createMock(File::class); |
|
| 644 | + $node->method('getId')->willReturn(42); |
|
| 645 | + $node->method('getName')->willReturn('myFile'); |
|
| 646 | + |
|
| 647 | + $this->addressHandler->expects($this->any())->method('splitUserRemote') |
|
| 648 | + ->willReturnCallback(function ($uid) { |
|
| 649 | + if ($uid === '[email protected]') { |
|
| 650 | + return ['user', 'server.com']; |
|
| 651 | + } |
|
| 652 | + return ['user2', 'server.com']; |
|
| 653 | + }); |
|
| 654 | + |
|
| 655 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 656 | + $this->notifications |
|
| 657 | + ->method('sendRemoteShare') |
|
| 658 | + ->willReturn(true); |
|
| 659 | + |
|
| 660 | + $this->rootFolder->expects($this->never())->method($this->anything()); |
|
| 661 | + |
|
| 662 | + $this->addressHandler->method('generateRemoteURL') |
|
| 663 | + ->willReturn('remoteurl.com'); |
|
| 664 | + |
|
| 665 | + $this->contactsManager->expects($this->any()) |
|
| 666 | + ->method('search') |
|
| 667 | + ->willReturn([]); |
|
| 668 | + |
|
| 669 | + $share = $this->shareManager->newShare(); |
|
| 670 | + $share->setSharedWith('[email protected]') |
|
| 671 | + ->setSharedBy('sharedBy') |
|
| 672 | + ->setShareOwner('shareOwner') |
|
| 673 | + ->setPermissions(19) |
|
| 674 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 675 | + ->setNode($node); |
|
| 676 | + $this->provider->create($share); |
|
| 677 | + |
|
| 678 | + $share2 = $this->shareManager->newShare(); |
|
| 679 | + $share2->setSharedWith('[email protected]') |
|
| 680 | + ->setSharedBy('sharedBy') |
|
| 681 | + ->setShareOwner('shareOwner') |
|
| 682 | + ->setPermissions(19) |
|
| 683 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 684 | + ->setNode($node); |
|
| 685 | + $this->provider->create($share2); |
|
| 686 | + |
|
| 687 | + $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_REMOTE, null, true, 1, 1); |
|
| 688 | + |
|
| 689 | + $this->assertCount(1, $shares); |
|
| 690 | + $this->assertEquals('[email protected]', $shares[0]->getSharedWith()); |
|
| 691 | + } |
|
| 692 | + |
|
| 693 | + public static function dataDeleteUser(): array { |
|
| 694 | + return [ |
|
| 695 | + ['a', 'b', 'c', 'a', true], |
|
| 696 | + ['a', 'b', 'c', 'b', false], |
|
| 697 | + // The recipient is non local. |
|
| 698 | + ['a', 'b', 'c', 'c', false], |
|
| 699 | + ['a', 'b', 'c', 'd', false], |
|
| 700 | + ]; |
|
| 701 | + } |
|
| 702 | + |
|
| 703 | + /** |
|
| 704 | + * |
|
| 705 | + * @param string $owner The owner of the share (uid) |
|
| 706 | + * @param string $initiator The initiator of the share (uid) |
|
| 707 | + * @param string $recipient The recipient of the share (uid/gid/pass) |
|
| 708 | + * @param string $deletedUser The user that is deleted |
|
| 709 | + * @param bool $rowDeleted Is the row deleted in this setup |
|
| 710 | + */ |
|
| 711 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataDeleteUser')] |
|
| 712 | + public function testDeleteUser(string $owner, string $initiator, string $recipient, string $deletedUser, bool $rowDeleted): void { |
|
| 713 | + $qb = $this->connection->getQueryBuilder(); |
|
| 714 | + $qb->insert('share') |
|
| 715 | + ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE)) |
|
| 716 | + ->setValue('uid_owner', $qb->createNamedParameter($owner)) |
|
| 717 | + ->setValue('uid_initiator', $qb->createNamedParameter($initiator)) |
|
| 718 | + ->setValue('share_with', $qb->createNamedParameter($recipient)) |
|
| 719 | + ->setValue('item_type', $qb->createNamedParameter('file')) |
|
| 720 | + ->setValue('item_source', $qb->createNamedParameter(42)) |
|
| 721 | + ->setValue('file_source', $qb->createNamedParameter(42)) |
|
| 722 | + ->executeStatement(); |
|
| 723 | + |
|
| 724 | + $id = $qb->getLastInsertId(); |
|
| 725 | + |
|
| 726 | + $this->provider->userDeleted($deletedUser, IShare::TYPE_REMOTE); |
|
| 727 | + |
|
| 728 | + $qb = $this->connection->getQueryBuilder(); |
|
| 729 | + $qb->select('*') |
|
| 730 | + ->from('share') |
|
| 731 | + ->where( |
|
| 732 | + $qb->expr()->eq('id', $qb->createNamedParameter($id)) |
|
| 733 | + ); |
|
| 734 | + $cursor = $qb->executeQuery(); |
|
| 735 | + $data = $cursor->fetchAllAssociative(); |
|
| 736 | + $cursor->closeCursor(); |
|
| 737 | + |
|
| 738 | + $this->assertCount($rowDeleted ? 0 : 1, $data); |
|
| 739 | + } |
|
| 740 | + |
|
| 741 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsOutgoingServer2serverShareEnabled')] |
|
| 742 | + public function testIsOutgoingServer2serverShareEnabled(bool $internalOnly, string $isEnabled, bool $expected): void { |
|
| 743 | + $this->gsConfig->expects($this->once())->method('onlyInternalFederation') |
|
| 744 | + ->willReturn($internalOnly); |
|
| 745 | + $this->config->expects($this->any())->method('getAppValue') |
|
| 746 | + ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') |
|
| 747 | + ->willReturn($isEnabled); |
|
| 748 | + |
|
| 749 | + $this->assertSame($expected, |
|
| 750 | + $this->provider->isOutgoingServer2serverShareEnabled() |
|
| 751 | + ); |
|
| 752 | + } |
|
| 753 | + |
|
| 754 | + public static function dataTestIsOutgoingServer2serverShareEnabled(): array { |
|
| 755 | + return [ |
|
| 756 | + [false, 'yes', true], |
|
| 757 | + [false, 'no', false], |
|
| 758 | + [true, 'yes', false], |
|
| 759 | + [true, 'no', false], |
|
| 760 | + ]; |
|
| 761 | + } |
|
| 762 | + |
|
| 763 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsIncomingServer2serverShareEnabled')] |
|
| 764 | + public function testIsIncomingServer2serverShareEnabled(bool $onlyInternal, string $isEnabled, bool $expected): void { |
|
| 765 | + $this->gsConfig->expects($this->once())->method('onlyInternalFederation') |
|
| 766 | + ->willReturn($onlyInternal); |
|
| 767 | + $this->config->expects($this->any())->method('getAppValue') |
|
| 768 | + ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') |
|
| 769 | + ->willReturn($isEnabled); |
|
| 770 | + |
|
| 771 | + $this->assertSame($expected, |
|
| 772 | + $this->provider->isIncomingServer2serverShareEnabled() |
|
| 773 | + ); |
|
| 774 | + } |
|
| 775 | + |
|
| 776 | + public static function dataTestIsIncomingServer2serverShareEnabled(): array { |
|
| 777 | + return [ |
|
| 778 | + [false, 'yes', true], |
|
| 779 | + [false, 'no', false], |
|
| 780 | + [true, 'yes', false], |
|
| 781 | + [true, 'no', false], |
|
| 782 | + ]; |
|
| 783 | + } |
|
| 784 | + |
|
| 785 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsLookupServerQueriesEnabled')] |
|
| 786 | + public function testIsLookupServerQueriesEnabled(bool $gsEnabled, string $isEnabled, bool $expected): void { |
|
| 787 | + $this->gsConfig->expects($this->once())->method('isGlobalScaleEnabled') |
|
| 788 | + ->willReturn($gsEnabled); |
|
| 789 | + $this->config->expects($this->any())->method('getAppValue') |
|
| 790 | + ->with('files_sharing', 'lookupServerEnabled', 'no') |
|
| 791 | + ->willReturn($isEnabled); |
|
| 792 | + |
|
| 793 | + $this->assertSame($expected, |
|
| 794 | + $this->provider->isLookupServerQueriesEnabled() |
|
| 795 | + ); |
|
| 796 | + } |
|
| 797 | + |
|
| 798 | + |
|
| 799 | + public static function dataTestIsLookupServerQueriesEnabled(): array { |
|
| 800 | + return [ |
|
| 801 | + [true, 'yes', true], |
|
| 802 | + [true, 'no', true], |
|
| 803 | + // TODO: reenable if we use the lookup server for non-global scale |
|
| 804 | + // [false, 'yes', true], |
|
| 805 | + // [false, 'no', false], |
|
| 806 | + [false, 'no', false], |
|
| 807 | + [false, 'yes', false], |
|
| 808 | + ]; |
|
| 809 | + } |
|
| 810 | + |
|
| 811 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsLookupServerUploadEnabled')] |
|
| 812 | + public function testIsLookupServerUploadEnabled(bool $gsEnabled, string $isEnabled, bool $expected): void { |
|
| 813 | + $this->gsConfig->expects($this->once())->method('isGlobalScaleEnabled') |
|
| 814 | + ->willReturn($gsEnabled); |
|
| 815 | + $this->config->expects($this->any())->method('getAppValue') |
|
| 816 | + ->with('files_sharing', 'lookupServerUploadEnabled', 'no') |
|
| 817 | + ->willReturn($isEnabled); |
|
| 818 | + |
|
| 819 | + $this->assertSame($expected, |
|
| 820 | + $this->provider->isLookupServerUploadEnabled() |
|
| 821 | + ); |
|
| 822 | + } |
|
| 823 | + |
|
| 824 | + public static function dataTestIsLookupServerUploadEnabled(): array { |
|
| 825 | + return [ |
|
| 826 | + [true, 'yes', false], |
|
| 827 | + [true, 'no', false], |
|
| 828 | + // TODO: reenable if we use the lookup server again |
|
| 829 | + // [false, 'yes', true], |
|
| 830 | + // [false, 'no', false], |
|
| 831 | + [false, 'yes', false], |
|
| 832 | + [false, 'no', false], |
|
| 833 | + ]; |
|
| 834 | + } |
|
| 835 | + |
|
| 836 | + public function testGetSharesInFolder(): void { |
|
| 837 | + $userManager = Server::get(IUserManager::class); |
|
| 838 | + $rootFolder = Server::get(IRootFolder::class); |
|
| 839 | + |
|
| 840 | + $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 841 | + $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 842 | + |
|
| 843 | + $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 844 | + $file1 = $folder1->newFile('bar1'); |
|
| 845 | + $file2 = $folder1->newFile('bar2'); |
|
| 846 | + |
|
| 847 | + $this->tokenHandler->method('generateToken')->willReturn('token'); |
|
| 848 | + $this->notifications |
|
| 849 | + ->method('sendRemoteShare') |
|
| 850 | + ->willReturn(true); |
|
| 851 | + |
|
| 852 | + $this->addressHandler->method('generateRemoteURL') |
|
| 853 | + ->willReturn('remoteurl.com'); |
|
| 854 | + |
|
| 855 | + $this->contactsManager->expects($this->any()) |
|
| 856 | + ->method('search') |
|
| 857 | + ->willReturn([]); |
|
| 858 | + |
|
| 859 | + $share1 = $this->shareManager->newShare(); |
|
| 860 | + $share1->setSharedWith('[email protected]') |
|
| 861 | + ->setSharedBy($u1->getUID()) |
|
| 862 | + ->setShareOwner($u1->getUID()) |
|
| 863 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 864 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 865 | + ->setNode($file1); |
|
| 866 | + $this->provider->create($share1); |
|
| 867 | + |
|
| 868 | + $share2 = $this->shareManager->newShare(); |
|
| 869 | + $share2->setSharedWith('[email protected]') |
|
| 870 | + ->setSharedBy($u2->getUID()) |
|
| 871 | + ->setShareOwner($u1->getUID()) |
|
| 872 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 873 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 874 | + ->setNode($file2); |
|
| 875 | + $this->provider->create($share2); |
|
| 876 | + |
|
| 877 | + $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, false); |
|
| 878 | + $this->assertCount(1, $result); |
|
| 879 | + $this->assertCount(1, $result[$file1->getId()]); |
|
| 880 | + |
|
| 881 | + $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, true); |
|
| 882 | + $this->assertCount(2, $result); |
|
| 883 | + $this->assertCount(1, $result[$file1->getId()]); |
|
| 884 | + $this->assertCount(1, $result[$file2->getId()]); |
|
| 885 | + |
|
| 886 | + $u1->delete(); |
|
| 887 | + $u2->delete(); |
|
| 888 | + } |
|
| 889 | + |
|
| 890 | + public function testGetAccessList(): void { |
|
| 891 | + $userManager = Server::get(IUserManager::class); |
|
| 892 | + $rootFolder = Server::get(IRootFolder::class); |
|
| 893 | + |
|
| 894 | + $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 895 | + |
|
| 896 | + $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 897 | + $file1 = $folder1->newFile('bar1'); |
|
| 898 | + |
|
| 899 | + $this->tokenHandler->expects($this->exactly(2)) |
|
| 900 | + ->method('generateToken') |
|
| 901 | + ->willReturnOnConsecutiveCalls('token1', 'token2'); |
|
| 902 | + $this->notifications->expects($this->atLeastOnce()) |
|
| 903 | + ->method('sendRemoteShare') |
|
| 904 | + ->willReturn(true); |
|
| 905 | + |
|
| 906 | + $this->contactsManager->expects($this->any()) |
|
| 907 | + ->method('search') |
|
| 908 | + ->willReturn([]); |
|
| 909 | + |
|
| 910 | + $result = $this->provider->getAccessList([$file1], true); |
|
| 911 | + $this->assertEquals(['remote' => []], $result); |
|
| 912 | + |
|
| 913 | + $result = $this->provider->getAccessList([$file1], false); |
|
| 914 | + $this->assertEquals(['remote' => false], $result); |
|
| 915 | + |
|
| 916 | + $this->addressHandler->method('generateRemoteURL') |
|
| 917 | + ->willReturn('remoteurl.com'); |
|
| 918 | + |
|
| 919 | + $share1 = $this->shareManager->newShare(); |
|
| 920 | + $share1->setSharedWith('[email protected]') |
|
| 921 | + ->setSharedBy($u1->getUID()) |
|
| 922 | + ->setShareOwner($u1->getUID()) |
|
| 923 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 924 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 925 | + ->setNode($file1); |
|
| 926 | + $this->provider->create($share1); |
|
| 927 | + |
|
| 928 | + $share2 = $this->shareManager->newShare(); |
|
| 929 | + $share2->setSharedWith('foobar@localhost') |
|
| 930 | + ->setSharedBy($u1->getUID()) |
|
| 931 | + ->setShareOwner($u1->getUID()) |
|
| 932 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 933 | + ->setShareType(IShare::TYPE_REMOTE) |
|
| 934 | + ->setNode($file1); |
|
| 935 | + $this->provider->create($share2); |
|
| 936 | + |
|
| 937 | + $result = $this->provider->getAccessList([$file1], true); |
|
| 938 | + $this->assertEquals(['remote' => [ |
|
| 939 | + '[email protected]' => [ |
|
| 940 | + 'token' => 'token1', |
|
| 941 | + 'node_id' => $file1->getId(), |
|
| 942 | + ], |
|
| 943 | + 'foobar@localhost' => [ |
|
| 944 | + 'token' => 'token2', |
|
| 945 | + 'node_id' => $file1->getId(), |
|
| 946 | + ], |
|
| 947 | + ]], $result); |
|
| 948 | + |
|
| 949 | + $result = $this->provider->getAccessList([$file1], false); |
|
| 950 | + $this->assertEquals(['remote' => true], $result); |
|
| 951 | + |
|
| 952 | + $u1->delete(); |
|
| 953 | + } |
|
| 954 | 954 | } |
@@ -35,1055 +35,1055 @@ |
||
| 35 | 35 | * @package OCA\FederatedFileSharing |
| 36 | 36 | */ |
| 37 | 37 | class FederatedShareProvider implements IShareProvider, IShareProviderSupportsAllSharesInFolder { |
| 38 | - public const SHARE_TYPE_REMOTE = 6; |
|
| 39 | - |
|
| 40 | - /** @var string */ |
|
| 41 | - private $externalShareTable = 'share_external'; |
|
| 42 | - |
|
| 43 | - /** @var array list of supported share types */ |
|
| 44 | - private $supportedShareType = [IShare::TYPE_REMOTE_GROUP, IShare::TYPE_REMOTE, IShare::TYPE_CIRCLE]; |
|
| 45 | - |
|
| 46 | - /** |
|
| 47 | - * DefaultShareProvider constructor. |
|
| 48 | - */ |
|
| 49 | - public function __construct( |
|
| 50 | - private IDBConnection $dbConnection, |
|
| 51 | - private AddressHandler $addressHandler, |
|
| 52 | - private Notifications $notifications, |
|
| 53 | - private TokenHandler $tokenHandler, |
|
| 54 | - private IL10N $l, |
|
| 55 | - private IRootFolder $rootFolder, |
|
| 56 | - private IConfig $config, |
|
| 57 | - private IUserManager $userManager, |
|
| 58 | - private ICloudIdManager $cloudIdManager, |
|
| 59 | - private \OCP\GlobalScale\IConfig $gsConfig, |
|
| 60 | - private ICloudFederationProviderManager $cloudFederationProviderManager, |
|
| 61 | - private LoggerInterface $logger, |
|
| 62 | - ) { |
|
| 63 | - } |
|
| 64 | - |
|
| 65 | - /** |
|
| 66 | - * Return the identifier of this provider. |
|
| 67 | - * |
|
| 68 | - * @return string Containing only [a-zA-Z0-9] |
|
| 69 | - */ |
|
| 70 | - public function identifier() { |
|
| 71 | - return 'ocFederatedSharing'; |
|
| 72 | - } |
|
| 73 | - |
|
| 74 | - /** |
|
| 75 | - * Share a path |
|
| 76 | - * |
|
| 77 | - * @param IShare $share |
|
| 78 | - * @return IShare The share object |
|
| 79 | - * @throws ShareNotFound |
|
| 80 | - * @throws \Exception |
|
| 81 | - */ |
|
| 82 | - public function create(IShare $share) { |
|
| 83 | - $shareWith = $share->getSharedWith(); |
|
| 84 | - $itemSource = $share->getNodeId(); |
|
| 85 | - $itemType = $share->getNodeType(); |
|
| 86 | - $permissions = $share->getPermissions(); |
|
| 87 | - $sharedBy = $share->getSharedBy(); |
|
| 88 | - $shareType = $share->getShareType(); |
|
| 89 | - $expirationDate = $share->getExpirationDate(); |
|
| 90 | - |
|
| 91 | - if ($shareType === IShare::TYPE_REMOTE_GROUP |
|
| 92 | - && !$this->isOutgoingServer2serverGroupShareEnabled() |
|
| 93 | - ) { |
|
| 94 | - $message = 'It is not allowed to send federated group shares from this server.'; |
|
| 95 | - $message_t = $this->l->t('It is not allowed to send federated group shares from this server.'); |
|
| 96 | - $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 97 | - throw new \Exception($message_t); |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - /* |
|
| 38 | + public const SHARE_TYPE_REMOTE = 6; |
|
| 39 | + |
|
| 40 | + /** @var string */ |
|
| 41 | + private $externalShareTable = 'share_external'; |
|
| 42 | + |
|
| 43 | + /** @var array list of supported share types */ |
|
| 44 | + private $supportedShareType = [IShare::TYPE_REMOTE_GROUP, IShare::TYPE_REMOTE, IShare::TYPE_CIRCLE]; |
|
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * DefaultShareProvider constructor. |
|
| 48 | + */ |
|
| 49 | + public function __construct( |
|
| 50 | + private IDBConnection $dbConnection, |
|
| 51 | + private AddressHandler $addressHandler, |
|
| 52 | + private Notifications $notifications, |
|
| 53 | + private TokenHandler $tokenHandler, |
|
| 54 | + private IL10N $l, |
|
| 55 | + private IRootFolder $rootFolder, |
|
| 56 | + private IConfig $config, |
|
| 57 | + private IUserManager $userManager, |
|
| 58 | + private ICloudIdManager $cloudIdManager, |
|
| 59 | + private \OCP\GlobalScale\IConfig $gsConfig, |
|
| 60 | + private ICloudFederationProviderManager $cloudFederationProviderManager, |
|
| 61 | + private LoggerInterface $logger, |
|
| 62 | + ) { |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + /** |
|
| 66 | + * Return the identifier of this provider. |
|
| 67 | + * |
|
| 68 | + * @return string Containing only [a-zA-Z0-9] |
|
| 69 | + */ |
|
| 70 | + public function identifier() { |
|
| 71 | + return 'ocFederatedSharing'; |
|
| 72 | + } |
|
| 73 | + |
|
| 74 | + /** |
|
| 75 | + * Share a path |
|
| 76 | + * |
|
| 77 | + * @param IShare $share |
|
| 78 | + * @return IShare The share object |
|
| 79 | + * @throws ShareNotFound |
|
| 80 | + * @throws \Exception |
|
| 81 | + */ |
|
| 82 | + public function create(IShare $share) { |
|
| 83 | + $shareWith = $share->getSharedWith(); |
|
| 84 | + $itemSource = $share->getNodeId(); |
|
| 85 | + $itemType = $share->getNodeType(); |
|
| 86 | + $permissions = $share->getPermissions(); |
|
| 87 | + $sharedBy = $share->getSharedBy(); |
|
| 88 | + $shareType = $share->getShareType(); |
|
| 89 | + $expirationDate = $share->getExpirationDate(); |
|
| 90 | + |
|
| 91 | + if ($shareType === IShare::TYPE_REMOTE_GROUP |
|
| 92 | + && !$this->isOutgoingServer2serverGroupShareEnabled() |
|
| 93 | + ) { |
|
| 94 | + $message = 'It is not allowed to send federated group shares from this server.'; |
|
| 95 | + $message_t = $this->l->t('It is not allowed to send federated group shares from this server.'); |
|
| 96 | + $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 97 | + throw new \Exception($message_t); |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + /* |
|
| 101 | 101 | * Check if file is not already shared with the remote user |
| 102 | 102 | */ |
| 103 | - $alreadyShared = $this->getSharedWith($shareWith, IShare::TYPE_REMOTE, $share->getNode(), 1, 0); |
|
| 104 | - $alreadySharedGroup = $this->getSharedWith($shareWith, IShare::TYPE_REMOTE_GROUP, $share->getNode(), 1, 0); |
|
| 105 | - if (!empty($alreadyShared) || !empty($alreadySharedGroup)) { |
|
| 106 | - $message = 'Sharing %1$s failed, because this item is already shared with %2$s'; |
|
| 107 | - $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with the account %2$s', [$share->getNode()->getName(), $shareWith]); |
|
| 108 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 109 | - throw new \Exception($message_t); |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - |
|
| 113 | - // don't allow federated shares if source and target server are the same |
|
| 114 | - $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
| 115 | - $currentServer = $this->addressHandler->generateRemoteURL(); |
|
| 116 | - $currentUser = $sharedBy; |
|
| 117 | - if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
| 118 | - $message = 'Not allowed to create a federated share to the same account.'; |
|
| 119 | - $message_t = $this->l->t('Not allowed to create a federated share to the same account'); |
|
| 120 | - $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 121 | - throw new \Exception($message_t); |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - // Federated shares always have read permissions |
|
| 125 | - if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) { |
|
| 126 | - $message = 'Federated shares require read permissions'; |
|
| 127 | - $message_t = $this->l->t('Federated shares require read permissions'); |
|
| 128 | - $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 129 | - throw new \Exception($message_t); |
|
| 130 | - } |
|
| 131 | - |
|
| 132 | - $share->setSharedWith($cloudId->getId()); |
|
| 133 | - |
|
| 134 | - try { |
|
| 135 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
| 136 | - } catch (ShareNotFound $e) { |
|
| 137 | - $remoteShare = null; |
|
| 138 | - } |
|
| 139 | - |
|
| 140 | - if ($remoteShare) { |
|
| 141 | - try { |
|
| 142 | - $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
| 143 | - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType, $expirationDate); |
|
| 144 | - $share->setId($shareId); |
|
| 145 | - [$token, $remoteId] = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
| 146 | - // remote share was create successfully if we get a valid token as return |
|
| 147 | - $send = is_string($token) && $token !== ''; |
|
| 148 | - } catch (\Exception $e) { |
|
| 149 | - // fall back to old re-share behavior if the remote server |
|
| 150 | - // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
| 151 | - $this->removeShareFromTable($share); |
|
| 152 | - $shareId = $this->createFederatedShare($share); |
|
| 153 | - } |
|
| 154 | - if ($send) { |
|
| 155 | - $this->updateSuccessfulReshare($shareId, $token); |
|
| 156 | - $this->storeRemoteId($shareId, $remoteId); |
|
| 157 | - } else { |
|
| 158 | - $this->removeShareFromTable($share); |
|
| 159 | - $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
| 160 | - throw new \Exception($message_t); |
|
| 161 | - } |
|
| 162 | - } else { |
|
| 163 | - $shareId = $this->createFederatedShare($share); |
|
| 164 | - } |
|
| 165 | - |
|
| 166 | - $data = $this->getRawShare($shareId); |
|
| 167 | - return $this->createShareObject($data); |
|
| 168 | - } |
|
| 169 | - |
|
| 170 | - /** |
|
| 171 | - * create federated share and inform the recipient |
|
| 172 | - * |
|
| 173 | - * @param IShare $share |
|
| 174 | - * @return int |
|
| 175 | - * @throws ShareNotFound |
|
| 176 | - * @throws \Exception |
|
| 177 | - */ |
|
| 178 | - protected function createFederatedShare(IShare $share) { |
|
| 179 | - $token = $this->tokenHandler->generateToken(); |
|
| 180 | - $shareId = $this->addShareToDB( |
|
| 181 | - $share->getNodeId(), |
|
| 182 | - $share->getNodeType(), |
|
| 183 | - $share->getSharedWith(), |
|
| 184 | - $share->getSharedBy(), |
|
| 185 | - $share->getShareOwner(), |
|
| 186 | - $share->getPermissions(), |
|
| 187 | - $token, |
|
| 188 | - $share->getShareType(), |
|
| 189 | - $share->getExpirationDate() |
|
| 190 | - ); |
|
| 191 | - |
|
| 192 | - $failure = false; |
|
| 193 | - |
|
| 194 | - try { |
|
| 195 | - $sharedByFederatedId = $share->getSharedBy(); |
|
| 196 | - if ($this->userManager->userExists($sharedByFederatedId)) { |
|
| 197 | - $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
| 198 | - $sharedByFederatedId = $cloudId->getId(); |
|
| 199 | - } |
|
| 200 | - $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
| 201 | - $send = $this->notifications->sendRemoteShare( |
|
| 202 | - $token, |
|
| 203 | - $share->getSharedWith(), |
|
| 204 | - $share->getNode()->getName(), |
|
| 205 | - $shareId, |
|
| 206 | - $share->getShareOwner(), |
|
| 207 | - $ownerCloudId->getId(), |
|
| 208 | - $share->getSharedBy(), |
|
| 209 | - $sharedByFederatedId, |
|
| 210 | - $share->getShareType() |
|
| 211 | - ); |
|
| 212 | - |
|
| 213 | - if ($send === false) { |
|
| 214 | - $failure = true; |
|
| 215 | - } |
|
| 216 | - } catch (\Exception $e) { |
|
| 217 | - $this->logger->error('Failed to notify remote server of federated share, removing share.', [ |
|
| 218 | - 'app' => 'federatedfilesharing', |
|
| 219 | - 'exception' => $e, |
|
| 220 | - ]); |
|
| 221 | - $failure = true; |
|
| 222 | - } |
|
| 223 | - |
|
| 224 | - if ($failure) { |
|
| 225 | - $this->removeShareFromTableById($shareId); |
|
| 226 | - $message_t = $this->l->t('Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
| 227 | - [$share->getNode()->getName(), $share->getSharedWith()]); |
|
| 228 | - throw new \Exception($message_t); |
|
| 229 | - } |
|
| 230 | - |
|
| 231 | - return $shareId; |
|
| 232 | - } |
|
| 233 | - |
|
| 234 | - /** |
|
| 235 | - * @param string $shareWith |
|
| 236 | - * @param IShare $share |
|
| 237 | - * @param string $shareId internal share Id |
|
| 238 | - * @return array |
|
| 239 | - * @throws \Exception |
|
| 240 | - */ |
|
| 241 | - protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
| 242 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
| 243 | - $token = $remoteShare['share_token']; |
|
| 244 | - $remoteId = $remoteShare['remote_id']; |
|
| 245 | - $remote = $remoteShare['remote']; |
|
| 246 | - |
|
| 247 | - [$token, $remoteId] = $this->notifications->requestReShare( |
|
| 248 | - $token, |
|
| 249 | - $remoteId, |
|
| 250 | - $shareId, |
|
| 251 | - $remote, |
|
| 252 | - $shareWith, |
|
| 253 | - $share->getPermissions(), |
|
| 254 | - $share->getNode()->getName(), |
|
| 255 | - $share->getShareType(), |
|
| 256 | - ); |
|
| 257 | - |
|
| 258 | - return [$token, $remoteId]; |
|
| 259 | - } |
|
| 260 | - |
|
| 261 | - /** |
|
| 262 | - * get federated share from the share_external table but exclude mounted link shares |
|
| 263 | - * |
|
| 264 | - * @param IShare $share |
|
| 265 | - * @return array |
|
| 266 | - * @throws ShareNotFound |
|
| 267 | - */ |
|
| 268 | - protected function getShareFromExternalShareTable(IShare $share) { |
|
| 269 | - $query = $this->dbConnection->getQueryBuilder(); |
|
| 270 | - $query->select('*')->from($this->externalShareTable) |
|
| 271 | - ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
| 272 | - ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
| 273 | - $qResult = $query->executeQuery(); |
|
| 274 | - $result = $qResult->fetchAllAssociative(); |
|
| 275 | - $qResult->closeCursor(); |
|
| 276 | - |
|
| 277 | - if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
| 278 | - return $result[0]; |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - throw new ShareNotFound('share not found in share_external table'); |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - /** |
|
| 285 | - * add share to the database and return the ID |
|
| 286 | - * |
|
| 287 | - * @param int $itemSource |
|
| 288 | - * @param string $itemType |
|
| 289 | - * @param string $shareWith |
|
| 290 | - * @param string $sharedBy |
|
| 291 | - * @param string $uidOwner |
|
| 292 | - * @param int $permissions |
|
| 293 | - * @param string $token |
|
| 294 | - * @param int $shareType |
|
| 295 | - * @param \DateTime $expirationDate |
|
| 296 | - * @return int |
|
| 297 | - */ |
|
| 298 | - private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType, $expirationDate) { |
|
| 299 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 300 | - $qb->insert('share') |
|
| 301 | - ->setValue('share_type', $qb->createNamedParameter($shareType)) |
|
| 302 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 303 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 304 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 305 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 306 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 307 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 308 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 309 | - ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 310 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
| 311 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 312 | - |
|
| 313 | - /* |
|
| 103 | + $alreadyShared = $this->getSharedWith($shareWith, IShare::TYPE_REMOTE, $share->getNode(), 1, 0); |
|
| 104 | + $alreadySharedGroup = $this->getSharedWith($shareWith, IShare::TYPE_REMOTE_GROUP, $share->getNode(), 1, 0); |
|
| 105 | + if (!empty($alreadyShared) || !empty($alreadySharedGroup)) { |
|
| 106 | + $message = 'Sharing %1$s failed, because this item is already shared with %2$s'; |
|
| 107 | + $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with the account %2$s', [$share->getNode()->getName(), $shareWith]); |
|
| 108 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 109 | + throw new \Exception($message_t); |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + |
|
| 113 | + // don't allow federated shares if source and target server are the same |
|
| 114 | + $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
| 115 | + $currentServer = $this->addressHandler->generateRemoteURL(); |
|
| 116 | + $currentUser = $sharedBy; |
|
| 117 | + if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
| 118 | + $message = 'Not allowed to create a federated share to the same account.'; |
|
| 119 | + $message_t = $this->l->t('Not allowed to create a federated share to the same account'); |
|
| 120 | + $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 121 | + throw new \Exception($message_t); |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + // Federated shares always have read permissions |
|
| 125 | + if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) { |
|
| 126 | + $message = 'Federated shares require read permissions'; |
|
| 127 | + $message_t = $this->l->t('Federated shares require read permissions'); |
|
| 128 | + $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
| 129 | + throw new \Exception($message_t); |
|
| 130 | + } |
|
| 131 | + |
|
| 132 | + $share->setSharedWith($cloudId->getId()); |
|
| 133 | + |
|
| 134 | + try { |
|
| 135 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
| 136 | + } catch (ShareNotFound $e) { |
|
| 137 | + $remoteShare = null; |
|
| 138 | + } |
|
| 139 | + |
|
| 140 | + if ($remoteShare) { |
|
| 141 | + try { |
|
| 142 | + $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
| 143 | + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType, $expirationDate); |
|
| 144 | + $share->setId($shareId); |
|
| 145 | + [$token, $remoteId] = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
| 146 | + // remote share was create successfully if we get a valid token as return |
|
| 147 | + $send = is_string($token) && $token !== ''; |
|
| 148 | + } catch (\Exception $e) { |
|
| 149 | + // fall back to old re-share behavior if the remote server |
|
| 150 | + // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
| 151 | + $this->removeShareFromTable($share); |
|
| 152 | + $shareId = $this->createFederatedShare($share); |
|
| 153 | + } |
|
| 154 | + if ($send) { |
|
| 155 | + $this->updateSuccessfulReshare($shareId, $token); |
|
| 156 | + $this->storeRemoteId($shareId, $remoteId); |
|
| 157 | + } else { |
|
| 158 | + $this->removeShareFromTable($share); |
|
| 159 | + $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
| 160 | + throw new \Exception($message_t); |
|
| 161 | + } |
|
| 162 | + } else { |
|
| 163 | + $shareId = $this->createFederatedShare($share); |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + $data = $this->getRawShare($shareId); |
|
| 167 | + return $this->createShareObject($data); |
|
| 168 | + } |
|
| 169 | + |
|
| 170 | + /** |
|
| 171 | + * create federated share and inform the recipient |
|
| 172 | + * |
|
| 173 | + * @param IShare $share |
|
| 174 | + * @return int |
|
| 175 | + * @throws ShareNotFound |
|
| 176 | + * @throws \Exception |
|
| 177 | + */ |
|
| 178 | + protected function createFederatedShare(IShare $share) { |
|
| 179 | + $token = $this->tokenHandler->generateToken(); |
|
| 180 | + $shareId = $this->addShareToDB( |
|
| 181 | + $share->getNodeId(), |
|
| 182 | + $share->getNodeType(), |
|
| 183 | + $share->getSharedWith(), |
|
| 184 | + $share->getSharedBy(), |
|
| 185 | + $share->getShareOwner(), |
|
| 186 | + $share->getPermissions(), |
|
| 187 | + $token, |
|
| 188 | + $share->getShareType(), |
|
| 189 | + $share->getExpirationDate() |
|
| 190 | + ); |
|
| 191 | + |
|
| 192 | + $failure = false; |
|
| 193 | + |
|
| 194 | + try { |
|
| 195 | + $sharedByFederatedId = $share->getSharedBy(); |
|
| 196 | + if ($this->userManager->userExists($sharedByFederatedId)) { |
|
| 197 | + $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
| 198 | + $sharedByFederatedId = $cloudId->getId(); |
|
| 199 | + } |
|
| 200 | + $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
| 201 | + $send = $this->notifications->sendRemoteShare( |
|
| 202 | + $token, |
|
| 203 | + $share->getSharedWith(), |
|
| 204 | + $share->getNode()->getName(), |
|
| 205 | + $shareId, |
|
| 206 | + $share->getShareOwner(), |
|
| 207 | + $ownerCloudId->getId(), |
|
| 208 | + $share->getSharedBy(), |
|
| 209 | + $sharedByFederatedId, |
|
| 210 | + $share->getShareType() |
|
| 211 | + ); |
|
| 212 | + |
|
| 213 | + if ($send === false) { |
|
| 214 | + $failure = true; |
|
| 215 | + } |
|
| 216 | + } catch (\Exception $e) { |
|
| 217 | + $this->logger->error('Failed to notify remote server of federated share, removing share.', [ |
|
| 218 | + 'app' => 'federatedfilesharing', |
|
| 219 | + 'exception' => $e, |
|
| 220 | + ]); |
|
| 221 | + $failure = true; |
|
| 222 | + } |
|
| 223 | + |
|
| 224 | + if ($failure) { |
|
| 225 | + $this->removeShareFromTableById($shareId); |
|
| 226 | + $message_t = $this->l->t('Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
| 227 | + [$share->getNode()->getName(), $share->getSharedWith()]); |
|
| 228 | + throw new \Exception($message_t); |
|
| 229 | + } |
|
| 230 | + |
|
| 231 | + return $shareId; |
|
| 232 | + } |
|
| 233 | + |
|
| 234 | + /** |
|
| 235 | + * @param string $shareWith |
|
| 236 | + * @param IShare $share |
|
| 237 | + * @param string $shareId internal share Id |
|
| 238 | + * @return array |
|
| 239 | + * @throws \Exception |
|
| 240 | + */ |
|
| 241 | + protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
| 242 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
| 243 | + $token = $remoteShare['share_token']; |
|
| 244 | + $remoteId = $remoteShare['remote_id']; |
|
| 245 | + $remote = $remoteShare['remote']; |
|
| 246 | + |
|
| 247 | + [$token, $remoteId] = $this->notifications->requestReShare( |
|
| 248 | + $token, |
|
| 249 | + $remoteId, |
|
| 250 | + $shareId, |
|
| 251 | + $remote, |
|
| 252 | + $shareWith, |
|
| 253 | + $share->getPermissions(), |
|
| 254 | + $share->getNode()->getName(), |
|
| 255 | + $share->getShareType(), |
|
| 256 | + ); |
|
| 257 | + |
|
| 258 | + return [$token, $remoteId]; |
|
| 259 | + } |
|
| 260 | + |
|
| 261 | + /** |
|
| 262 | + * get federated share from the share_external table but exclude mounted link shares |
|
| 263 | + * |
|
| 264 | + * @param IShare $share |
|
| 265 | + * @return array |
|
| 266 | + * @throws ShareNotFound |
|
| 267 | + */ |
|
| 268 | + protected function getShareFromExternalShareTable(IShare $share) { |
|
| 269 | + $query = $this->dbConnection->getQueryBuilder(); |
|
| 270 | + $query->select('*')->from($this->externalShareTable) |
|
| 271 | + ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
| 272 | + ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
| 273 | + $qResult = $query->executeQuery(); |
|
| 274 | + $result = $qResult->fetchAllAssociative(); |
|
| 275 | + $qResult->closeCursor(); |
|
| 276 | + |
|
| 277 | + if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
| 278 | + return $result[0]; |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + throw new ShareNotFound('share not found in share_external table'); |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + /** |
|
| 285 | + * add share to the database and return the ID |
|
| 286 | + * |
|
| 287 | + * @param int $itemSource |
|
| 288 | + * @param string $itemType |
|
| 289 | + * @param string $shareWith |
|
| 290 | + * @param string $sharedBy |
|
| 291 | + * @param string $uidOwner |
|
| 292 | + * @param int $permissions |
|
| 293 | + * @param string $token |
|
| 294 | + * @param int $shareType |
|
| 295 | + * @param \DateTime $expirationDate |
|
| 296 | + * @return int |
|
| 297 | + */ |
|
| 298 | + private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType, $expirationDate) { |
|
| 299 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 300 | + $qb->insert('share') |
|
| 301 | + ->setValue('share_type', $qb->createNamedParameter($shareType)) |
|
| 302 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 303 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 304 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 305 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 306 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 307 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 308 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 309 | + ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 310 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
| 311 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 312 | + |
|
| 313 | + /* |
|
| 314 | 314 | * Added to fix https://github.com/owncloud/core/issues/22215 |
| 315 | 315 | * Can be removed once we get rid of ajax/share.php |
| 316 | 316 | */ |
| 317 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 318 | - |
|
| 319 | - $qb->executeStatement(); |
|
| 320 | - return $qb->getLastInsertId(); |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - /** |
|
| 324 | - * Update a share |
|
| 325 | - * |
|
| 326 | - * @param IShare $share |
|
| 327 | - * @return IShare The share object |
|
| 328 | - */ |
|
| 329 | - public function update(IShare $share) { |
|
| 330 | - /* |
|
| 317 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 318 | + |
|
| 319 | + $qb->executeStatement(); |
|
| 320 | + return $qb->getLastInsertId(); |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + /** |
|
| 324 | + * Update a share |
|
| 325 | + * |
|
| 326 | + * @param IShare $share |
|
| 327 | + * @return IShare The share object |
|
| 328 | + */ |
|
| 329 | + public function update(IShare $share) { |
|
| 330 | + /* |
|
| 331 | 331 | * We allow updating the permissions of federated shares |
| 332 | 332 | */ |
| 333 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 334 | - $qb->update('share') |
|
| 335 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 336 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 337 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 338 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 339 | - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 340 | - ->executeStatement(); |
|
| 341 | - |
|
| 342 | - // send the updated permission to the owner/initiator, if they are not the same |
|
| 343 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 344 | - $this->sendPermissionUpdate($share); |
|
| 345 | - } |
|
| 346 | - |
|
| 347 | - return $share; |
|
| 348 | - } |
|
| 349 | - |
|
| 350 | - /** |
|
| 351 | - * send the updated permission to the owner/initiator, if they are not the same |
|
| 352 | - * |
|
| 353 | - * @param IShare $share |
|
| 354 | - * @throws ShareNotFound |
|
| 355 | - * @throws HintException |
|
| 356 | - */ |
|
| 357 | - protected function sendPermissionUpdate(IShare $share) { |
|
| 358 | - $remoteId = $this->getRemoteId($share); |
|
| 359 | - // if the local user is the owner we send the permission change to the initiator |
|
| 360 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 361 | - [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
| 362 | - } else { // ... if not we send the permission change to the owner |
|
| 363 | - [, $remote] = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
| 364 | - } |
|
| 365 | - $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
| 366 | - } |
|
| 367 | - |
|
| 368 | - |
|
| 369 | - /** |
|
| 370 | - * update successful reShare with the correct token |
|
| 371 | - * |
|
| 372 | - * @param int $shareId |
|
| 373 | - * @param string $token |
|
| 374 | - */ |
|
| 375 | - protected function updateSuccessfulReShare($shareId, $token) { |
|
| 376 | - $query = $this->dbConnection->getQueryBuilder(); |
|
| 377 | - $query->update('share') |
|
| 378 | - ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
| 379 | - ->set('token', $query->createNamedParameter($token)) |
|
| 380 | - ->executeStatement(); |
|
| 381 | - } |
|
| 382 | - |
|
| 383 | - /** |
|
| 384 | - * store remote ID in federated reShare table |
|
| 385 | - * |
|
| 386 | - * @param $shareId |
|
| 387 | - * @param $remoteId |
|
| 388 | - */ |
|
| 389 | - public function storeRemoteId(int $shareId, string $remoteId): void { |
|
| 390 | - $query = $this->dbConnection->getQueryBuilder(); |
|
| 391 | - $query->insert('federated_reshares') |
|
| 392 | - ->values( |
|
| 393 | - [ |
|
| 394 | - 'share_id' => $query->createNamedParameter($shareId), |
|
| 395 | - 'remote_id' => $query->createNamedParameter($remoteId), |
|
| 396 | - ] |
|
| 397 | - ); |
|
| 398 | - $query->executeStatement(); |
|
| 399 | - } |
|
| 400 | - |
|
| 401 | - /** |
|
| 402 | - * get share ID on remote server for federated re-shares |
|
| 403 | - * |
|
| 404 | - * @param IShare $share |
|
| 405 | - * @return string |
|
| 406 | - * @throws ShareNotFound |
|
| 407 | - */ |
|
| 408 | - public function getRemoteId(IShare $share): string { |
|
| 409 | - $query = $this->dbConnection->getQueryBuilder(); |
|
| 410 | - $query->select('remote_id')->from('federated_reshares') |
|
| 411 | - ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
| 412 | - $result = $query->executeQuery(); |
|
| 413 | - $data = $result->fetchAssociative(); |
|
| 414 | - $result->closeCursor(); |
|
| 415 | - |
|
| 416 | - if (!is_array($data) || !isset($data['remote_id'])) { |
|
| 417 | - throw new ShareNotFound(); |
|
| 418 | - } |
|
| 419 | - |
|
| 420 | - return (string)$data['remote_id']; |
|
| 421 | - } |
|
| 422 | - |
|
| 423 | - /** |
|
| 424 | - * @inheritdoc |
|
| 425 | - */ |
|
| 426 | - public function move(IShare $share, $recipient) { |
|
| 427 | - /* |
|
| 333 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 334 | + $qb->update('share') |
|
| 335 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 336 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 337 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 338 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 339 | + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 340 | + ->executeStatement(); |
|
| 341 | + |
|
| 342 | + // send the updated permission to the owner/initiator, if they are not the same |
|
| 343 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 344 | + $this->sendPermissionUpdate($share); |
|
| 345 | + } |
|
| 346 | + |
|
| 347 | + return $share; |
|
| 348 | + } |
|
| 349 | + |
|
| 350 | + /** |
|
| 351 | + * send the updated permission to the owner/initiator, if they are not the same |
|
| 352 | + * |
|
| 353 | + * @param IShare $share |
|
| 354 | + * @throws ShareNotFound |
|
| 355 | + * @throws HintException |
|
| 356 | + */ |
|
| 357 | + protected function sendPermissionUpdate(IShare $share) { |
|
| 358 | + $remoteId = $this->getRemoteId($share); |
|
| 359 | + // if the local user is the owner we send the permission change to the initiator |
|
| 360 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 361 | + [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
| 362 | + } else { // ... if not we send the permission change to the owner |
|
| 363 | + [, $remote] = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
| 364 | + } |
|
| 365 | + $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
| 366 | + } |
|
| 367 | + |
|
| 368 | + |
|
| 369 | + /** |
|
| 370 | + * update successful reShare with the correct token |
|
| 371 | + * |
|
| 372 | + * @param int $shareId |
|
| 373 | + * @param string $token |
|
| 374 | + */ |
|
| 375 | + protected function updateSuccessfulReShare($shareId, $token) { |
|
| 376 | + $query = $this->dbConnection->getQueryBuilder(); |
|
| 377 | + $query->update('share') |
|
| 378 | + ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
| 379 | + ->set('token', $query->createNamedParameter($token)) |
|
| 380 | + ->executeStatement(); |
|
| 381 | + } |
|
| 382 | + |
|
| 383 | + /** |
|
| 384 | + * store remote ID in federated reShare table |
|
| 385 | + * |
|
| 386 | + * @param $shareId |
|
| 387 | + * @param $remoteId |
|
| 388 | + */ |
|
| 389 | + public function storeRemoteId(int $shareId, string $remoteId): void { |
|
| 390 | + $query = $this->dbConnection->getQueryBuilder(); |
|
| 391 | + $query->insert('federated_reshares') |
|
| 392 | + ->values( |
|
| 393 | + [ |
|
| 394 | + 'share_id' => $query->createNamedParameter($shareId), |
|
| 395 | + 'remote_id' => $query->createNamedParameter($remoteId), |
|
| 396 | + ] |
|
| 397 | + ); |
|
| 398 | + $query->executeStatement(); |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + /** |
|
| 402 | + * get share ID on remote server for federated re-shares |
|
| 403 | + * |
|
| 404 | + * @param IShare $share |
|
| 405 | + * @return string |
|
| 406 | + * @throws ShareNotFound |
|
| 407 | + */ |
|
| 408 | + public function getRemoteId(IShare $share): string { |
|
| 409 | + $query = $this->dbConnection->getQueryBuilder(); |
|
| 410 | + $query->select('remote_id')->from('federated_reshares') |
|
| 411 | + ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
| 412 | + $result = $query->executeQuery(); |
|
| 413 | + $data = $result->fetchAssociative(); |
|
| 414 | + $result->closeCursor(); |
|
| 415 | + |
|
| 416 | + if (!is_array($data) || !isset($data['remote_id'])) { |
|
| 417 | + throw new ShareNotFound(); |
|
| 418 | + } |
|
| 419 | + |
|
| 420 | + return (string)$data['remote_id']; |
|
| 421 | + } |
|
| 422 | + |
|
| 423 | + /** |
|
| 424 | + * @inheritdoc |
|
| 425 | + */ |
|
| 426 | + public function move(IShare $share, $recipient) { |
|
| 427 | + /* |
|
| 428 | 428 | * This function does nothing yet as it is just for outgoing |
| 429 | 429 | * federated shares. |
| 430 | 430 | */ |
| 431 | - return $share; |
|
| 432 | - } |
|
| 433 | - |
|
| 434 | - public function getChildren(IShare $parent): array { |
|
| 435 | - $children = []; |
|
| 436 | - |
|
| 437 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 438 | - $qb->select('*') |
|
| 439 | - ->from('share') |
|
| 440 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 441 | - ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 442 | - ->orderBy('id'); |
|
| 443 | - |
|
| 444 | - $cursor = $qb->executeQuery(); |
|
| 445 | - while ($data = $cursor->fetchAssociative()) { |
|
| 446 | - $children[] = $this->createShareObject($data); |
|
| 447 | - } |
|
| 448 | - $cursor->closeCursor(); |
|
| 449 | - |
|
| 450 | - return $children; |
|
| 451 | - } |
|
| 452 | - |
|
| 453 | - /** |
|
| 454 | - * Delete a share (owner unShares the file) |
|
| 455 | - * |
|
| 456 | - * @param IShare $share |
|
| 457 | - * @throws ShareNotFound |
|
| 458 | - * @throws HintException |
|
| 459 | - */ |
|
| 460 | - public function delete(IShare $share) { |
|
| 461 | - [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
| 462 | - |
|
| 463 | - // if the local user is the owner we can send the unShare request directly... |
|
| 464 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 465 | - $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
| 466 | - $this->revokeShare($share, true); |
|
| 467 | - } else { // ... if not we need to correct ID for the unShare request |
|
| 468 | - $remoteId = $this->getRemoteId($share); |
|
| 469 | - $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
| 470 | - $this->revokeShare($share, false); |
|
| 471 | - } |
|
| 472 | - |
|
| 473 | - // only remove the share when all messages are send to not lose information |
|
| 474 | - // about the share to early |
|
| 475 | - $this->removeShareFromTable($share); |
|
| 476 | - } |
|
| 477 | - |
|
| 478 | - /** |
|
| 479 | - * in case of a re-share we need to send the other use (initiator or owner) |
|
| 480 | - * a message that the file was unshared |
|
| 481 | - * |
|
| 482 | - * @param IShare $share |
|
| 483 | - * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
| 484 | - * @throws ShareNotFound |
|
| 485 | - * @throws HintException |
|
| 486 | - */ |
|
| 487 | - protected function revokeShare($share, $isOwner) { |
|
| 488 | - if ($this->userManager->userExists($share->getShareOwner()) && $this->userManager->userExists($share->getSharedBy())) { |
|
| 489 | - // If both the owner and the initiator of the share are local users we don't have to notify anybody else |
|
| 490 | - return; |
|
| 491 | - } |
|
| 492 | - |
|
| 493 | - // also send a unShare request to the initiator, if this is a different user than the owner |
|
| 494 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 495 | - if ($isOwner) { |
|
| 496 | - [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
| 497 | - } else { |
|
| 498 | - [, $remote] = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
| 499 | - } |
|
| 500 | - $remoteId = $this->getRemoteId($share); |
|
| 501 | - $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
| 502 | - } |
|
| 503 | - } |
|
| 504 | - |
|
| 505 | - /** |
|
| 506 | - * remove share from table |
|
| 507 | - * |
|
| 508 | - * @param IShare $share |
|
| 509 | - */ |
|
| 510 | - public function removeShareFromTable(IShare $share) { |
|
| 511 | - $this->removeShareFromTableById($share->getId()); |
|
| 512 | - } |
|
| 513 | - |
|
| 514 | - /** |
|
| 515 | - * remove share from table |
|
| 516 | - * |
|
| 517 | - * @param string $shareId |
|
| 518 | - */ |
|
| 519 | - private function removeShareFromTableById($shareId) { |
|
| 520 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 521 | - $qb->delete('share') |
|
| 522 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))) |
|
| 523 | - ->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))); |
|
| 524 | - $qb->executeStatement(); |
|
| 525 | - |
|
| 526 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 527 | - $qb->delete('federated_reshares') |
|
| 528 | - ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
| 529 | - $qb->executeStatement(); |
|
| 530 | - } |
|
| 531 | - |
|
| 532 | - /** |
|
| 533 | - * @inheritdoc |
|
| 534 | - */ |
|
| 535 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
| 536 | - // nothing to do here. Technically deleteFromSelf in the context of federated |
|
| 537 | - // shares is a umount of an external storage. This is handled here |
|
| 538 | - // apps/files_sharing/lib/external/manager.php |
|
| 539 | - // TODO move this code over to this app |
|
| 540 | - } |
|
| 541 | - |
|
| 542 | - public function restore(IShare $share, string $recipient): IShare { |
|
| 543 | - throw new GenericShareException('not implemented'); |
|
| 544 | - } |
|
| 545 | - |
|
| 546 | - |
|
| 547 | - public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) { |
|
| 548 | - if (!$shallow) { |
|
| 549 | - throw new \Exception('non-shallow getSharesInFolder is no longer supported'); |
|
| 550 | - } |
|
| 551 | - return $this->getSharesInFolderInternal($userId, $node, $reshares); |
|
| 552 | - } |
|
| 553 | - |
|
| 554 | - public function getAllSharesInFolder(Folder $node): array { |
|
| 555 | - return $this->getSharesInFolderInternal(null, $node, null); |
|
| 556 | - } |
|
| 557 | - |
|
| 558 | - /** |
|
| 559 | - * @return array<int, list<IShare>> |
|
| 560 | - */ |
|
| 561 | - private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { |
|
| 562 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 563 | - $qb->select('*') |
|
| 564 | - ->from('share', 's') |
|
| 565 | - ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) |
|
| 566 | - ->andWhere( |
|
| 567 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE)) |
|
| 568 | - ); |
|
| 569 | - |
|
| 570 | - if ($userId !== null) { |
|
| 571 | - /** |
|
| 572 | - * Reshares for this user are shares where they are the owner. |
|
| 573 | - */ |
|
| 574 | - if ($reshares !== true) { |
|
| 575 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 576 | - } else { |
|
| 577 | - $qb->andWhere( |
|
| 578 | - $qb->expr()->orX( |
|
| 579 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 580 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 581 | - ) |
|
| 582 | - ); |
|
| 583 | - } |
|
| 584 | - } |
|
| 585 | - |
|
| 586 | - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 587 | - |
|
| 588 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 589 | - |
|
| 590 | - $qb->orderBy('id'); |
|
| 591 | - |
|
| 592 | - $cursor = $qb->executeQuery(); |
|
| 593 | - $shares = []; |
|
| 594 | - while ($data = $cursor->fetchAssociative()) { |
|
| 595 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 596 | - } |
|
| 597 | - $cursor->closeCursor(); |
|
| 598 | - |
|
| 599 | - return $shares; |
|
| 600 | - } |
|
| 601 | - |
|
| 602 | - /** |
|
| 603 | - * @inheritdoc |
|
| 604 | - */ |
|
| 605 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
| 606 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 607 | - $qb->select('*') |
|
| 608 | - ->from('share'); |
|
| 609 | - |
|
| 610 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); |
|
| 611 | - |
|
| 612 | - /** |
|
| 613 | - * Reshares for this user are shares where they are the owner. |
|
| 614 | - */ |
|
| 615 | - if ($reshares === false) { |
|
| 616 | - //Special case for old shares created via the web UI |
|
| 617 | - $or1 = $qb->expr()->andX( |
|
| 618 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 619 | - $qb->expr()->isNull('uid_initiator') |
|
| 620 | - ); |
|
| 621 | - |
|
| 622 | - $qb->andWhere( |
|
| 623 | - $qb->expr()->orX( |
|
| 624 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 625 | - $or1 |
|
| 626 | - ) |
|
| 627 | - ); |
|
| 628 | - } else { |
|
| 629 | - $qb->andWhere( |
|
| 630 | - $qb->expr()->orX( |
|
| 631 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 632 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 633 | - ) |
|
| 634 | - ); |
|
| 635 | - } |
|
| 636 | - |
|
| 637 | - if ($node !== null) { |
|
| 638 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 639 | - } |
|
| 640 | - |
|
| 641 | - if ($limit !== -1) { |
|
| 642 | - $qb->setMaxResults($limit); |
|
| 643 | - } |
|
| 644 | - |
|
| 645 | - $qb->setFirstResult($offset); |
|
| 646 | - $qb->orderBy('id'); |
|
| 647 | - |
|
| 648 | - $cursor = $qb->executeQuery(); |
|
| 649 | - $shares = []; |
|
| 650 | - while ($data = $cursor->fetchAssociative()) { |
|
| 651 | - $shares[] = $this->createShareObject($data); |
|
| 652 | - } |
|
| 653 | - $cursor->closeCursor(); |
|
| 654 | - |
|
| 655 | - return $shares; |
|
| 656 | - } |
|
| 657 | - |
|
| 658 | - /** |
|
| 659 | - * @inheritdoc |
|
| 660 | - */ |
|
| 661 | - public function getShareById($id, $recipientId = null) { |
|
| 662 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 663 | - |
|
| 664 | - $qb->select('*') |
|
| 665 | - ->from('share') |
|
| 666 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 667 | - ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 668 | - |
|
| 669 | - $cursor = $qb->executeQuery(); |
|
| 670 | - $data = $cursor->fetchAssociative(); |
|
| 671 | - $cursor->closeCursor(); |
|
| 672 | - |
|
| 673 | - if ($data === false) { |
|
| 674 | - throw new ShareNotFound('Can not find share with ID: ' . $id); |
|
| 675 | - } |
|
| 676 | - |
|
| 677 | - try { |
|
| 678 | - $share = $this->createShareObject($data); |
|
| 679 | - } catch (InvalidShare $e) { |
|
| 680 | - throw new ShareNotFound(); |
|
| 681 | - } |
|
| 682 | - |
|
| 683 | - return $share; |
|
| 684 | - } |
|
| 685 | - |
|
| 686 | - /** |
|
| 687 | - * Get shares for a given path |
|
| 688 | - * |
|
| 689 | - * @param Node $path |
|
| 690 | - * @return IShare[] |
|
| 691 | - */ |
|
| 692 | - public function getSharesByPath(Node $path) { |
|
| 693 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 694 | - |
|
| 695 | - // get federated user shares |
|
| 696 | - $cursor = $qb->select('*') |
|
| 697 | - ->from('share') |
|
| 698 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 699 | - ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 700 | - ->executeQuery(); |
|
| 701 | - |
|
| 702 | - $shares = []; |
|
| 703 | - while ($data = $cursor->fetchAssociative()) { |
|
| 704 | - $shares[] = $this->createShareObject($data); |
|
| 705 | - } |
|
| 706 | - $cursor->closeCursor(); |
|
| 707 | - |
|
| 708 | - return $shares; |
|
| 709 | - } |
|
| 710 | - |
|
| 711 | - /** |
|
| 712 | - * @inheritdoc |
|
| 713 | - */ |
|
| 714 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
| 715 | - /** @var IShare[] $shares */ |
|
| 716 | - $shares = []; |
|
| 717 | - |
|
| 718 | - //Get shares directly with this user |
|
| 719 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 720 | - $qb->select('*') |
|
| 721 | - ->from('share'); |
|
| 722 | - |
|
| 723 | - // Order by id |
|
| 724 | - $qb->orderBy('id'); |
|
| 725 | - |
|
| 726 | - // Set limit and offset |
|
| 727 | - if ($limit !== -1) { |
|
| 728 | - $qb->setMaxResults($limit); |
|
| 729 | - } |
|
| 730 | - $qb->setFirstResult($offset); |
|
| 731 | - |
|
| 732 | - $qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 733 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 734 | - |
|
| 735 | - // Filter by node if provided |
|
| 736 | - if ($node !== null) { |
|
| 737 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 738 | - } |
|
| 739 | - |
|
| 740 | - $cursor = $qb->executeQuery(); |
|
| 741 | - |
|
| 742 | - while ($data = $cursor->fetchAssociative()) { |
|
| 743 | - $shares[] = $this->createShareObject($data); |
|
| 744 | - } |
|
| 745 | - $cursor->closeCursor(); |
|
| 746 | - |
|
| 747 | - |
|
| 748 | - return $shares; |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - /** |
|
| 752 | - * Get a share by token |
|
| 753 | - * |
|
| 754 | - * @param string $token |
|
| 755 | - * @return IShare |
|
| 756 | - * @throws ShareNotFound |
|
| 757 | - */ |
|
| 758 | - public function getShareByToken($token) { |
|
| 759 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 760 | - |
|
| 761 | - $cursor = $qb->select('*') |
|
| 762 | - ->from('share') |
|
| 763 | - ->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 764 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 765 | - ->executeQuery(); |
|
| 766 | - |
|
| 767 | - $data = $cursor->fetchAssociative(); |
|
| 768 | - |
|
| 769 | - if ($data === false) { |
|
| 770 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 771 | - } |
|
| 772 | - |
|
| 773 | - try { |
|
| 774 | - $share = $this->createShareObject($data); |
|
| 775 | - } catch (InvalidShare $e) { |
|
| 776 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 777 | - } |
|
| 778 | - |
|
| 779 | - return $share; |
|
| 780 | - } |
|
| 781 | - |
|
| 782 | - /** |
|
| 783 | - * get database row of a give share |
|
| 784 | - * |
|
| 785 | - * @param $id |
|
| 786 | - * @return array |
|
| 787 | - * @throws ShareNotFound |
|
| 788 | - */ |
|
| 789 | - private function getRawShare($id) { |
|
| 790 | - // Now fetch the inserted share and create a complete share object |
|
| 791 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 792 | - $qb->select('*') |
|
| 793 | - ->from('share') |
|
| 794 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 795 | - |
|
| 796 | - $cursor = $qb->executeQuery(); |
|
| 797 | - $data = $cursor->fetchAssociative(); |
|
| 798 | - $cursor->closeCursor(); |
|
| 799 | - |
|
| 800 | - if ($data === false) { |
|
| 801 | - throw new ShareNotFound; |
|
| 802 | - } |
|
| 803 | - |
|
| 804 | - return $data; |
|
| 805 | - } |
|
| 806 | - |
|
| 807 | - /** |
|
| 808 | - * Create a share object from an database row |
|
| 809 | - * |
|
| 810 | - * @param array $data |
|
| 811 | - * @return IShare |
|
| 812 | - * @throws InvalidShare |
|
| 813 | - * @throws ShareNotFound |
|
| 814 | - */ |
|
| 815 | - private function createShareObject($data) { |
|
| 816 | - $share = new Share($this->rootFolder, $this->userManager); |
|
| 817 | - $share->setId((int)$data['id']) |
|
| 818 | - ->setShareType((int)$data['share_type']) |
|
| 819 | - ->setPermissions((int)$data['permissions']) |
|
| 820 | - ->setTarget($data['file_target']) |
|
| 821 | - ->setMailSend((bool)$data['mail_send']) |
|
| 822 | - ->setStatus((int)$data['accepted']) |
|
| 823 | - ->setToken($data['token']); |
|
| 824 | - |
|
| 825 | - $shareTime = new \DateTime(); |
|
| 826 | - $shareTime->setTimestamp((int)$data['stime']); |
|
| 827 | - $share->setShareTime($shareTime); |
|
| 828 | - $share->setSharedWith($data['share_with']); |
|
| 829 | - |
|
| 830 | - if ($data['uid_initiator'] !== null) { |
|
| 831 | - $share->setShareOwner($data['uid_owner']); |
|
| 832 | - $share->setSharedBy($data['uid_initiator']); |
|
| 833 | - } else { |
|
| 834 | - //OLD SHARE |
|
| 835 | - $share->setSharedBy($data['uid_owner']); |
|
| 836 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 837 | - |
|
| 838 | - $owner = $path->getOwner(); |
|
| 839 | - $share->setShareOwner($owner->getUID()); |
|
| 840 | - } |
|
| 841 | - |
|
| 842 | - $share->setNodeId((int)$data['file_source']); |
|
| 843 | - $share->setNodeType($data['item_type']); |
|
| 844 | - |
|
| 845 | - $share->setProviderId($this->identifier()); |
|
| 846 | - |
|
| 847 | - if ($data['expiration'] !== null) { |
|
| 848 | - $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 849 | - $share->setExpirationDate($expiration); |
|
| 850 | - } |
|
| 851 | - |
|
| 852 | - return $share; |
|
| 853 | - } |
|
| 854 | - |
|
| 855 | - /** |
|
| 856 | - * Get the node with file $id for $user |
|
| 857 | - * |
|
| 858 | - * @param string $userId |
|
| 859 | - * @param int $id |
|
| 860 | - * @return Node |
|
| 861 | - * @throws InvalidShare |
|
| 862 | - */ |
|
| 863 | - private function getNode($userId, $id) { |
|
| 864 | - try { |
|
| 865 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 866 | - } catch (NotFoundException $e) { |
|
| 867 | - throw new InvalidShare(); |
|
| 868 | - } |
|
| 869 | - |
|
| 870 | - $node = $userFolder->getFirstNodeById($id); |
|
| 871 | - |
|
| 872 | - if (!$node) { |
|
| 873 | - throw new InvalidShare(); |
|
| 874 | - } |
|
| 875 | - |
|
| 876 | - return $node; |
|
| 877 | - } |
|
| 878 | - |
|
| 879 | - /** |
|
| 880 | - * A user is deleted from the system |
|
| 881 | - * So clean up the relevant shares. |
|
| 882 | - * |
|
| 883 | - * @param string $uid |
|
| 884 | - * @param int $shareType |
|
| 885 | - */ |
|
| 886 | - public function userDeleted($uid, $shareType) { |
|
| 887 | - //TODO: probably a good idea to send unshare info to remote servers |
|
| 888 | - |
|
| 889 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 890 | - $qb->delete('share') |
|
| 891 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) |
|
| 892 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 893 | - ->executeStatement(); |
|
| 894 | - |
|
| 895 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 896 | - $qb->delete('share_external') |
|
| 897 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 898 | - ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) |
|
| 899 | - ->executeStatement(); |
|
| 900 | - } |
|
| 901 | - |
|
| 902 | - public function groupDeleted($gid) { |
|
| 903 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 904 | - $qb->select('id') |
|
| 905 | - ->from('share_external') |
|
| 906 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 907 | - // This is not a typo, the group ID is really stored in the 'user' column |
|
| 908 | - ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); |
|
| 909 | - $cursor = $qb->executeQuery(); |
|
| 910 | - $parentShareIds = $cursor->fetchFirstColumn(); |
|
| 911 | - $cursor->closeCursor(); |
|
| 912 | - if ($parentShareIds === []) { |
|
| 913 | - return; |
|
| 914 | - } |
|
| 915 | - |
|
| 916 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 917 | - $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 918 | - $qb->delete('share_external') |
|
| 919 | - ->where($qb->expr()->in('id', $parentShareIdsParam)) |
|
| 920 | - ->orWhere($qb->expr()->in('parent', $parentShareIdsParam)) |
|
| 921 | - ->executeStatement(); |
|
| 922 | - } |
|
| 923 | - |
|
| 924 | - public function userDeletedFromGroup($uid, $gid) { |
|
| 925 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 926 | - $qb->select('id') |
|
| 927 | - ->from('share_external') |
|
| 928 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 929 | - // This is not a typo, the group ID is really stored in the 'user' column |
|
| 930 | - ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); |
|
| 931 | - $cursor = $qb->executeQuery(); |
|
| 932 | - $parentShareIds = $cursor->fetchFirstColumn(); |
|
| 933 | - $cursor->closeCursor(); |
|
| 934 | - if ($parentShareIds === []) { |
|
| 935 | - return; |
|
| 936 | - } |
|
| 937 | - |
|
| 938 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 939 | - $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 940 | - $qb->delete('share_external') |
|
| 941 | - ->where($qb->expr()->in('parent', $parentShareIdsParam)) |
|
| 942 | - ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) |
|
| 943 | - ->executeStatement(); |
|
| 944 | - } |
|
| 945 | - |
|
| 946 | - /** |
|
| 947 | - * Check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
| 948 | - */ |
|
| 949 | - public function isOutgoingServer2serverShareEnabled(): bool { |
|
| 950 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
| 951 | - return false; |
|
| 952 | - } |
|
| 953 | - $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
| 954 | - return $result === 'yes'; |
|
| 955 | - } |
|
| 956 | - |
|
| 957 | - /** |
|
| 958 | - * Check if users are allowed to mount public links from other Nextclouds |
|
| 959 | - */ |
|
| 960 | - public function isIncomingServer2serverShareEnabled(): bool { |
|
| 961 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
| 962 | - return false; |
|
| 963 | - } |
|
| 964 | - $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
| 965 | - return $result === 'yes'; |
|
| 966 | - } |
|
| 967 | - |
|
| 968 | - |
|
| 969 | - /** |
|
| 970 | - * Check if users from other Nextcloud instances are allowed to send federated group shares |
|
| 971 | - */ |
|
| 972 | - public function isOutgoingServer2serverGroupShareEnabled(): bool { |
|
| 973 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
| 974 | - return false; |
|
| 975 | - } |
|
| 976 | - $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no'); |
|
| 977 | - return $result === 'yes'; |
|
| 978 | - } |
|
| 979 | - |
|
| 980 | - /** |
|
| 981 | - * Check if users are allowed to receive federated group shares |
|
| 982 | - */ |
|
| 983 | - public function isIncomingServer2serverGroupShareEnabled(): bool { |
|
| 984 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
| 985 | - return false; |
|
| 986 | - } |
|
| 987 | - $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no'); |
|
| 988 | - return $result === 'yes'; |
|
| 989 | - } |
|
| 990 | - |
|
| 991 | - /** |
|
| 992 | - * Check if federated group sharing is supported, therefore the OCM API need to be enabled |
|
| 993 | - */ |
|
| 994 | - public function isFederatedGroupSharingSupported(): bool { |
|
| 995 | - return $this->cloudFederationProviderManager->isReady(); |
|
| 996 | - } |
|
| 997 | - |
|
| 998 | - /** |
|
| 999 | - * Check if querying sharees on the lookup server is enabled |
|
| 1000 | - */ |
|
| 1001 | - public function isLookupServerQueriesEnabled(): bool { |
|
| 1002 | - // in a global scale setup we should always query the lookup server |
|
| 1003 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
| 1004 | - return true; |
|
| 1005 | - } |
|
| 1006 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no') === 'yes'; |
|
| 1007 | - // TODO: Reenable if lookup server is used again |
|
| 1008 | - // return $result; |
|
| 1009 | - return false; |
|
| 1010 | - } |
|
| 1011 | - |
|
| 1012 | - |
|
| 1013 | - /** |
|
| 1014 | - * Check if it is allowed to publish user specific data to the lookup server |
|
| 1015 | - */ |
|
| 1016 | - public function isLookupServerUploadEnabled(): bool { |
|
| 1017 | - // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
| 1018 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
| 1019 | - return false; |
|
| 1020 | - } |
|
| 1021 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'no') === 'yes'; |
|
| 1022 | - // TODO: Reenable if lookup server is used again |
|
| 1023 | - // return $result; |
|
| 1024 | - return false; |
|
| 1025 | - } |
|
| 1026 | - |
|
| 1027 | - /** |
|
| 1028 | - * Check if auto accepting incoming shares from trusted servers is enabled |
|
| 1029 | - */ |
|
| 1030 | - public function isFederatedTrustedShareAutoAccept(): bool { |
|
| 1031 | - $result = $this->config->getAppValue('files_sharing', 'federatedTrustedShareAutoAccept', 'yes'); |
|
| 1032 | - return $result === 'yes'; |
|
| 1033 | - } |
|
| 1034 | - |
|
| 1035 | - public function getAccessList($nodes, $currentAccess) { |
|
| 1036 | - $ids = []; |
|
| 1037 | - foreach ($nodes as $node) { |
|
| 1038 | - $ids[] = $node->getId(); |
|
| 1039 | - } |
|
| 1040 | - |
|
| 1041 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1042 | - $qb->select('share_with', 'token', 'file_source') |
|
| 1043 | - ->from('share') |
|
| 1044 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) |
|
| 1045 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1046 | - ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 1047 | - $cursor = $qb->executeQuery(); |
|
| 1048 | - |
|
| 1049 | - if ($currentAccess === false) { |
|
| 1050 | - $remote = $cursor->fetchAssociative() !== false; |
|
| 1051 | - $cursor->closeCursor(); |
|
| 1052 | - |
|
| 1053 | - return ['remote' => $remote]; |
|
| 1054 | - } |
|
| 1055 | - |
|
| 1056 | - $remote = []; |
|
| 1057 | - while ($row = $cursor->fetchAssociative()) { |
|
| 1058 | - $remote[$row['share_with']] = [ |
|
| 1059 | - 'node_id' => $row['file_source'], |
|
| 1060 | - 'token' => $row['token'], |
|
| 1061 | - ]; |
|
| 1062 | - } |
|
| 1063 | - $cursor->closeCursor(); |
|
| 1064 | - |
|
| 1065 | - return ['remote' => $remote]; |
|
| 1066 | - } |
|
| 1067 | - |
|
| 1068 | - public function getAllShares(): iterable { |
|
| 1069 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1070 | - |
|
| 1071 | - $qb->select('*') |
|
| 1072 | - ->from('share') |
|
| 1073 | - ->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_REMOTE_GROUP, IShare::TYPE_REMOTE], IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 1074 | - |
|
| 1075 | - $cursor = $qb->executeQuery(); |
|
| 1076 | - while ($data = $cursor->fetchAssociative()) { |
|
| 1077 | - try { |
|
| 1078 | - $share = $this->createShareObject($data); |
|
| 1079 | - } catch (InvalidShare $e) { |
|
| 1080 | - continue; |
|
| 1081 | - } catch (ShareNotFound $e) { |
|
| 1082 | - continue; |
|
| 1083 | - } |
|
| 1084 | - |
|
| 1085 | - yield $share; |
|
| 1086 | - } |
|
| 1087 | - $cursor->closeCursor(); |
|
| 1088 | - } |
|
| 431 | + return $share; |
|
| 432 | + } |
|
| 433 | + |
|
| 434 | + public function getChildren(IShare $parent): array { |
|
| 435 | + $children = []; |
|
| 436 | + |
|
| 437 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 438 | + $qb->select('*') |
|
| 439 | + ->from('share') |
|
| 440 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 441 | + ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 442 | + ->orderBy('id'); |
|
| 443 | + |
|
| 444 | + $cursor = $qb->executeQuery(); |
|
| 445 | + while ($data = $cursor->fetchAssociative()) { |
|
| 446 | + $children[] = $this->createShareObject($data); |
|
| 447 | + } |
|
| 448 | + $cursor->closeCursor(); |
|
| 449 | + |
|
| 450 | + return $children; |
|
| 451 | + } |
|
| 452 | + |
|
| 453 | + /** |
|
| 454 | + * Delete a share (owner unShares the file) |
|
| 455 | + * |
|
| 456 | + * @param IShare $share |
|
| 457 | + * @throws ShareNotFound |
|
| 458 | + * @throws HintException |
|
| 459 | + */ |
|
| 460 | + public function delete(IShare $share) { |
|
| 461 | + [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
| 462 | + |
|
| 463 | + // if the local user is the owner we can send the unShare request directly... |
|
| 464 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 465 | + $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
| 466 | + $this->revokeShare($share, true); |
|
| 467 | + } else { // ... if not we need to correct ID for the unShare request |
|
| 468 | + $remoteId = $this->getRemoteId($share); |
|
| 469 | + $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
| 470 | + $this->revokeShare($share, false); |
|
| 471 | + } |
|
| 472 | + |
|
| 473 | + // only remove the share when all messages are send to not lose information |
|
| 474 | + // about the share to early |
|
| 475 | + $this->removeShareFromTable($share); |
|
| 476 | + } |
|
| 477 | + |
|
| 478 | + /** |
|
| 479 | + * in case of a re-share we need to send the other use (initiator or owner) |
|
| 480 | + * a message that the file was unshared |
|
| 481 | + * |
|
| 482 | + * @param IShare $share |
|
| 483 | + * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
| 484 | + * @throws ShareNotFound |
|
| 485 | + * @throws HintException |
|
| 486 | + */ |
|
| 487 | + protected function revokeShare($share, $isOwner) { |
|
| 488 | + if ($this->userManager->userExists($share->getShareOwner()) && $this->userManager->userExists($share->getSharedBy())) { |
|
| 489 | + // If both the owner and the initiator of the share are local users we don't have to notify anybody else |
|
| 490 | + return; |
|
| 491 | + } |
|
| 492 | + |
|
| 493 | + // also send a unShare request to the initiator, if this is a different user than the owner |
|
| 494 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 495 | + if ($isOwner) { |
|
| 496 | + [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
| 497 | + } else { |
|
| 498 | + [, $remote] = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
| 499 | + } |
|
| 500 | + $remoteId = $this->getRemoteId($share); |
|
| 501 | + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
| 502 | + } |
|
| 503 | + } |
|
| 504 | + |
|
| 505 | + /** |
|
| 506 | + * remove share from table |
|
| 507 | + * |
|
| 508 | + * @param IShare $share |
|
| 509 | + */ |
|
| 510 | + public function removeShareFromTable(IShare $share) { |
|
| 511 | + $this->removeShareFromTableById($share->getId()); |
|
| 512 | + } |
|
| 513 | + |
|
| 514 | + /** |
|
| 515 | + * remove share from table |
|
| 516 | + * |
|
| 517 | + * @param string $shareId |
|
| 518 | + */ |
|
| 519 | + private function removeShareFromTableById($shareId) { |
|
| 520 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 521 | + $qb->delete('share') |
|
| 522 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))) |
|
| 523 | + ->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))); |
|
| 524 | + $qb->executeStatement(); |
|
| 525 | + |
|
| 526 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 527 | + $qb->delete('federated_reshares') |
|
| 528 | + ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
| 529 | + $qb->executeStatement(); |
|
| 530 | + } |
|
| 531 | + |
|
| 532 | + /** |
|
| 533 | + * @inheritdoc |
|
| 534 | + */ |
|
| 535 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
| 536 | + // nothing to do here. Technically deleteFromSelf in the context of federated |
|
| 537 | + // shares is a umount of an external storage. This is handled here |
|
| 538 | + // apps/files_sharing/lib/external/manager.php |
|
| 539 | + // TODO move this code over to this app |
|
| 540 | + } |
|
| 541 | + |
|
| 542 | + public function restore(IShare $share, string $recipient): IShare { |
|
| 543 | + throw new GenericShareException('not implemented'); |
|
| 544 | + } |
|
| 545 | + |
|
| 546 | + |
|
| 547 | + public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) { |
|
| 548 | + if (!$shallow) { |
|
| 549 | + throw new \Exception('non-shallow getSharesInFolder is no longer supported'); |
|
| 550 | + } |
|
| 551 | + return $this->getSharesInFolderInternal($userId, $node, $reshares); |
|
| 552 | + } |
|
| 553 | + |
|
| 554 | + public function getAllSharesInFolder(Folder $node): array { |
|
| 555 | + return $this->getSharesInFolderInternal(null, $node, null); |
|
| 556 | + } |
|
| 557 | + |
|
| 558 | + /** |
|
| 559 | + * @return array<int, list<IShare>> |
|
| 560 | + */ |
|
| 561 | + private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { |
|
| 562 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 563 | + $qb->select('*') |
|
| 564 | + ->from('share', 's') |
|
| 565 | + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) |
|
| 566 | + ->andWhere( |
|
| 567 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE)) |
|
| 568 | + ); |
|
| 569 | + |
|
| 570 | + if ($userId !== null) { |
|
| 571 | + /** |
|
| 572 | + * Reshares for this user are shares where they are the owner. |
|
| 573 | + */ |
|
| 574 | + if ($reshares !== true) { |
|
| 575 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 576 | + } else { |
|
| 577 | + $qb->andWhere( |
|
| 578 | + $qb->expr()->orX( |
|
| 579 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 580 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 581 | + ) |
|
| 582 | + ); |
|
| 583 | + } |
|
| 584 | + } |
|
| 585 | + |
|
| 586 | + $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 587 | + |
|
| 588 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 589 | + |
|
| 590 | + $qb->orderBy('id'); |
|
| 591 | + |
|
| 592 | + $cursor = $qb->executeQuery(); |
|
| 593 | + $shares = []; |
|
| 594 | + while ($data = $cursor->fetchAssociative()) { |
|
| 595 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 596 | + } |
|
| 597 | + $cursor->closeCursor(); |
|
| 598 | + |
|
| 599 | + return $shares; |
|
| 600 | + } |
|
| 601 | + |
|
| 602 | + /** |
|
| 603 | + * @inheritdoc |
|
| 604 | + */ |
|
| 605 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
| 606 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 607 | + $qb->select('*') |
|
| 608 | + ->from('share'); |
|
| 609 | + |
|
| 610 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); |
|
| 611 | + |
|
| 612 | + /** |
|
| 613 | + * Reshares for this user are shares where they are the owner. |
|
| 614 | + */ |
|
| 615 | + if ($reshares === false) { |
|
| 616 | + //Special case for old shares created via the web UI |
|
| 617 | + $or1 = $qb->expr()->andX( |
|
| 618 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 619 | + $qb->expr()->isNull('uid_initiator') |
|
| 620 | + ); |
|
| 621 | + |
|
| 622 | + $qb->andWhere( |
|
| 623 | + $qb->expr()->orX( |
|
| 624 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 625 | + $or1 |
|
| 626 | + ) |
|
| 627 | + ); |
|
| 628 | + } else { |
|
| 629 | + $qb->andWhere( |
|
| 630 | + $qb->expr()->orX( |
|
| 631 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 632 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 633 | + ) |
|
| 634 | + ); |
|
| 635 | + } |
|
| 636 | + |
|
| 637 | + if ($node !== null) { |
|
| 638 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 639 | + } |
|
| 640 | + |
|
| 641 | + if ($limit !== -1) { |
|
| 642 | + $qb->setMaxResults($limit); |
|
| 643 | + } |
|
| 644 | + |
|
| 645 | + $qb->setFirstResult($offset); |
|
| 646 | + $qb->orderBy('id'); |
|
| 647 | + |
|
| 648 | + $cursor = $qb->executeQuery(); |
|
| 649 | + $shares = []; |
|
| 650 | + while ($data = $cursor->fetchAssociative()) { |
|
| 651 | + $shares[] = $this->createShareObject($data); |
|
| 652 | + } |
|
| 653 | + $cursor->closeCursor(); |
|
| 654 | + |
|
| 655 | + return $shares; |
|
| 656 | + } |
|
| 657 | + |
|
| 658 | + /** |
|
| 659 | + * @inheritdoc |
|
| 660 | + */ |
|
| 661 | + public function getShareById($id, $recipientId = null) { |
|
| 662 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 663 | + |
|
| 664 | + $qb->select('*') |
|
| 665 | + ->from('share') |
|
| 666 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 667 | + ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 668 | + |
|
| 669 | + $cursor = $qb->executeQuery(); |
|
| 670 | + $data = $cursor->fetchAssociative(); |
|
| 671 | + $cursor->closeCursor(); |
|
| 672 | + |
|
| 673 | + if ($data === false) { |
|
| 674 | + throw new ShareNotFound('Can not find share with ID: ' . $id); |
|
| 675 | + } |
|
| 676 | + |
|
| 677 | + try { |
|
| 678 | + $share = $this->createShareObject($data); |
|
| 679 | + } catch (InvalidShare $e) { |
|
| 680 | + throw new ShareNotFound(); |
|
| 681 | + } |
|
| 682 | + |
|
| 683 | + return $share; |
|
| 684 | + } |
|
| 685 | + |
|
| 686 | + /** |
|
| 687 | + * Get shares for a given path |
|
| 688 | + * |
|
| 689 | + * @param Node $path |
|
| 690 | + * @return IShare[] |
|
| 691 | + */ |
|
| 692 | + public function getSharesByPath(Node $path) { |
|
| 693 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 694 | + |
|
| 695 | + // get federated user shares |
|
| 696 | + $cursor = $qb->select('*') |
|
| 697 | + ->from('share') |
|
| 698 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 699 | + ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 700 | + ->executeQuery(); |
|
| 701 | + |
|
| 702 | + $shares = []; |
|
| 703 | + while ($data = $cursor->fetchAssociative()) { |
|
| 704 | + $shares[] = $this->createShareObject($data); |
|
| 705 | + } |
|
| 706 | + $cursor->closeCursor(); |
|
| 707 | + |
|
| 708 | + return $shares; |
|
| 709 | + } |
|
| 710 | + |
|
| 711 | + /** |
|
| 712 | + * @inheritdoc |
|
| 713 | + */ |
|
| 714 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
| 715 | + /** @var IShare[] $shares */ |
|
| 716 | + $shares = []; |
|
| 717 | + |
|
| 718 | + //Get shares directly with this user |
|
| 719 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 720 | + $qb->select('*') |
|
| 721 | + ->from('share'); |
|
| 722 | + |
|
| 723 | + // Order by id |
|
| 724 | + $qb->orderBy('id'); |
|
| 725 | + |
|
| 726 | + // Set limit and offset |
|
| 727 | + if ($limit !== -1) { |
|
| 728 | + $qb->setMaxResults($limit); |
|
| 729 | + } |
|
| 730 | + $qb->setFirstResult($offset); |
|
| 731 | + |
|
| 732 | + $qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 733 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 734 | + |
|
| 735 | + // Filter by node if provided |
|
| 736 | + if ($node !== null) { |
|
| 737 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 738 | + } |
|
| 739 | + |
|
| 740 | + $cursor = $qb->executeQuery(); |
|
| 741 | + |
|
| 742 | + while ($data = $cursor->fetchAssociative()) { |
|
| 743 | + $shares[] = $this->createShareObject($data); |
|
| 744 | + } |
|
| 745 | + $cursor->closeCursor(); |
|
| 746 | + |
|
| 747 | + |
|
| 748 | + return $shares; |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + /** |
|
| 752 | + * Get a share by token |
|
| 753 | + * |
|
| 754 | + * @param string $token |
|
| 755 | + * @return IShare |
|
| 756 | + * @throws ShareNotFound |
|
| 757 | + */ |
|
| 758 | + public function getShareByToken($token) { |
|
| 759 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 760 | + |
|
| 761 | + $cursor = $qb->select('*') |
|
| 762 | + ->from('share') |
|
| 763 | + ->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 764 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 765 | + ->executeQuery(); |
|
| 766 | + |
|
| 767 | + $data = $cursor->fetchAssociative(); |
|
| 768 | + |
|
| 769 | + if ($data === false) { |
|
| 770 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 771 | + } |
|
| 772 | + |
|
| 773 | + try { |
|
| 774 | + $share = $this->createShareObject($data); |
|
| 775 | + } catch (InvalidShare $e) { |
|
| 776 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 777 | + } |
|
| 778 | + |
|
| 779 | + return $share; |
|
| 780 | + } |
|
| 781 | + |
|
| 782 | + /** |
|
| 783 | + * get database row of a give share |
|
| 784 | + * |
|
| 785 | + * @param $id |
|
| 786 | + * @return array |
|
| 787 | + * @throws ShareNotFound |
|
| 788 | + */ |
|
| 789 | + private function getRawShare($id) { |
|
| 790 | + // Now fetch the inserted share and create a complete share object |
|
| 791 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 792 | + $qb->select('*') |
|
| 793 | + ->from('share') |
|
| 794 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 795 | + |
|
| 796 | + $cursor = $qb->executeQuery(); |
|
| 797 | + $data = $cursor->fetchAssociative(); |
|
| 798 | + $cursor->closeCursor(); |
|
| 799 | + |
|
| 800 | + if ($data === false) { |
|
| 801 | + throw new ShareNotFound; |
|
| 802 | + } |
|
| 803 | + |
|
| 804 | + return $data; |
|
| 805 | + } |
|
| 806 | + |
|
| 807 | + /** |
|
| 808 | + * Create a share object from an database row |
|
| 809 | + * |
|
| 810 | + * @param array $data |
|
| 811 | + * @return IShare |
|
| 812 | + * @throws InvalidShare |
|
| 813 | + * @throws ShareNotFound |
|
| 814 | + */ |
|
| 815 | + private function createShareObject($data) { |
|
| 816 | + $share = new Share($this->rootFolder, $this->userManager); |
|
| 817 | + $share->setId((int)$data['id']) |
|
| 818 | + ->setShareType((int)$data['share_type']) |
|
| 819 | + ->setPermissions((int)$data['permissions']) |
|
| 820 | + ->setTarget($data['file_target']) |
|
| 821 | + ->setMailSend((bool)$data['mail_send']) |
|
| 822 | + ->setStatus((int)$data['accepted']) |
|
| 823 | + ->setToken($data['token']); |
|
| 824 | + |
|
| 825 | + $shareTime = new \DateTime(); |
|
| 826 | + $shareTime->setTimestamp((int)$data['stime']); |
|
| 827 | + $share->setShareTime($shareTime); |
|
| 828 | + $share->setSharedWith($data['share_with']); |
|
| 829 | + |
|
| 830 | + if ($data['uid_initiator'] !== null) { |
|
| 831 | + $share->setShareOwner($data['uid_owner']); |
|
| 832 | + $share->setSharedBy($data['uid_initiator']); |
|
| 833 | + } else { |
|
| 834 | + //OLD SHARE |
|
| 835 | + $share->setSharedBy($data['uid_owner']); |
|
| 836 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 837 | + |
|
| 838 | + $owner = $path->getOwner(); |
|
| 839 | + $share->setShareOwner($owner->getUID()); |
|
| 840 | + } |
|
| 841 | + |
|
| 842 | + $share->setNodeId((int)$data['file_source']); |
|
| 843 | + $share->setNodeType($data['item_type']); |
|
| 844 | + |
|
| 845 | + $share->setProviderId($this->identifier()); |
|
| 846 | + |
|
| 847 | + if ($data['expiration'] !== null) { |
|
| 848 | + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 849 | + $share->setExpirationDate($expiration); |
|
| 850 | + } |
|
| 851 | + |
|
| 852 | + return $share; |
|
| 853 | + } |
|
| 854 | + |
|
| 855 | + /** |
|
| 856 | + * Get the node with file $id for $user |
|
| 857 | + * |
|
| 858 | + * @param string $userId |
|
| 859 | + * @param int $id |
|
| 860 | + * @return Node |
|
| 861 | + * @throws InvalidShare |
|
| 862 | + */ |
|
| 863 | + private function getNode($userId, $id) { |
|
| 864 | + try { |
|
| 865 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 866 | + } catch (NotFoundException $e) { |
|
| 867 | + throw new InvalidShare(); |
|
| 868 | + } |
|
| 869 | + |
|
| 870 | + $node = $userFolder->getFirstNodeById($id); |
|
| 871 | + |
|
| 872 | + if (!$node) { |
|
| 873 | + throw new InvalidShare(); |
|
| 874 | + } |
|
| 875 | + |
|
| 876 | + return $node; |
|
| 877 | + } |
|
| 878 | + |
|
| 879 | + /** |
|
| 880 | + * A user is deleted from the system |
|
| 881 | + * So clean up the relevant shares. |
|
| 882 | + * |
|
| 883 | + * @param string $uid |
|
| 884 | + * @param int $shareType |
|
| 885 | + */ |
|
| 886 | + public function userDeleted($uid, $shareType) { |
|
| 887 | + //TODO: probably a good idea to send unshare info to remote servers |
|
| 888 | + |
|
| 889 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 890 | + $qb->delete('share') |
|
| 891 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) |
|
| 892 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 893 | + ->executeStatement(); |
|
| 894 | + |
|
| 895 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 896 | + $qb->delete('share_external') |
|
| 897 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 898 | + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) |
|
| 899 | + ->executeStatement(); |
|
| 900 | + } |
|
| 901 | + |
|
| 902 | + public function groupDeleted($gid) { |
|
| 903 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 904 | + $qb->select('id') |
|
| 905 | + ->from('share_external') |
|
| 906 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 907 | + // This is not a typo, the group ID is really stored in the 'user' column |
|
| 908 | + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); |
|
| 909 | + $cursor = $qb->executeQuery(); |
|
| 910 | + $parentShareIds = $cursor->fetchFirstColumn(); |
|
| 911 | + $cursor->closeCursor(); |
|
| 912 | + if ($parentShareIds === []) { |
|
| 913 | + return; |
|
| 914 | + } |
|
| 915 | + |
|
| 916 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 917 | + $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 918 | + $qb->delete('share_external') |
|
| 919 | + ->where($qb->expr()->in('id', $parentShareIdsParam)) |
|
| 920 | + ->orWhere($qb->expr()->in('parent', $parentShareIdsParam)) |
|
| 921 | + ->executeStatement(); |
|
| 922 | + } |
|
| 923 | + |
|
| 924 | + public function userDeletedFromGroup($uid, $gid) { |
|
| 925 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 926 | + $qb->select('id') |
|
| 927 | + ->from('share_external') |
|
| 928 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) |
|
| 929 | + // This is not a typo, the group ID is really stored in the 'user' column |
|
| 930 | + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); |
|
| 931 | + $cursor = $qb->executeQuery(); |
|
| 932 | + $parentShareIds = $cursor->fetchFirstColumn(); |
|
| 933 | + $cursor->closeCursor(); |
|
| 934 | + if ($parentShareIds === []) { |
|
| 935 | + return; |
|
| 936 | + } |
|
| 937 | + |
|
| 938 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 939 | + $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 940 | + $qb->delete('share_external') |
|
| 941 | + ->where($qb->expr()->in('parent', $parentShareIdsParam)) |
|
| 942 | + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) |
|
| 943 | + ->executeStatement(); |
|
| 944 | + } |
|
| 945 | + |
|
| 946 | + /** |
|
| 947 | + * Check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
| 948 | + */ |
|
| 949 | + public function isOutgoingServer2serverShareEnabled(): bool { |
|
| 950 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
| 951 | + return false; |
|
| 952 | + } |
|
| 953 | + $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
| 954 | + return $result === 'yes'; |
|
| 955 | + } |
|
| 956 | + |
|
| 957 | + /** |
|
| 958 | + * Check if users are allowed to mount public links from other Nextclouds |
|
| 959 | + */ |
|
| 960 | + public function isIncomingServer2serverShareEnabled(): bool { |
|
| 961 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
| 962 | + return false; |
|
| 963 | + } |
|
| 964 | + $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
| 965 | + return $result === 'yes'; |
|
| 966 | + } |
|
| 967 | + |
|
| 968 | + |
|
| 969 | + /** |
|
| 970 | + * Check if users from other Nextcloud instances are allowed to send federated group shares |
|
| 971 | + */ |
|
| 972 | + public function isOutgoingServer2serverGroupShareEnabled(): bool { |
|
| 973 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
| 974 | + return false; |
|
| 975 | + } |
|
| 976 | + $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no'); |
|
| 977 | + return $result === 'yes'; |
|
| 978 | + } |
|
| 979 | + |
|
| 980 | + /** |
|
| 981 | + * Check if users are allowed to receive federated group shares |
|
| 982 | + */ |
|
| 983 | + public function isIncomingServer2serverGroupShareEnabled(): bool { |
|
| 984 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
| 985 | + return false; |
|
| 986 | + } |
|
| 987 | + $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no'); |
|
| 988 | + return $result === 'yes'; |
|
| 989 | + } |
|
| 990 | + |
|
| 991 | + /** |
|
| 992 | + * Check if federated group sharing is supported, therefore the OCM API need to be enabled |
|
| 993 | + */ |
|
| 994 | + public function isFederatedGroupSharingSupported(): bool { |
|
| 995 | + return $this->cloudFederationProviderManager->isReady(); |
|
| 996 | + } |
|
| 997 | + |
|
| 998 | + /** |
|
| 999 | + * Check if querying sharees on the lookup server is enabled |
|
| 1000 | + */ |
|
| 1001 | + public function isLookupServerQueriesEnabled(): bool { |
|
| 1002 | + // in a global scale setup we should always query the lookup server |
|
| 1003 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
| 1004 | + return true; |
|
| 1005 | + } |
|
| 1006 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no') === 'yes'; |
|
| 1007 | + // TODO: Reenable if lookup server is used again |
|
| 1008 | + // return $result; |
|
| 1009 | + return false; |
|
| 1010 | + } |
|
| 1011 | + |
|
| 1012 | + |
|
| 1013 | + /** |
|
| 1014 | + * Check if it is allowed to publish user specific data to the lookup server |
|
| 1015 | + */ |
|
| 1016 | + public function isLookupServerUploadEnabled(): bool { |
|
| 1017 | + // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
| 1018 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
| 1019 | + return false; |
|
| 1020 | + } |
|
| 1021 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'no') === 'yes'; |
|
| 1022 | + // TODO: Reenable if lookup server is used again |
|
| 1023 | + // return $result; |
|
| 1024 | + return false; |
|
| 1025 | + } |
|
| 1026 | + |
|
| 1027 | + /** |
|
| 1028 | + * Check if auto accepting incoming shares from trusted servers is enabled |
|
| 1029 | + */ |
|
| 1030 | + public function isFederatedTrustedShareAutoAccept(): bool { |
|
| 1031 | + $result = $this->config->getAppValue('files_sharing', 'federatedTrustedShareAutoAccept', 'yes'); |
|
| 1032 | + return $result === 'yes'; |
|
| 1033 | + } |
|
| 1034 | + |
|
| 1035 | + public function getAccessList($nodes, $currentAccess) { |
|
| 1036 | + $ids = []; |
|
| 1037 | + foreach ($nodes as $node) { |
|
| 1038 | + $ids[] = $node->getId(); |
|
| 1039 | + } |
|
| 1040 | + |
|
| 1041 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1042 | + $qb->select('share_with', 'token', 'file_source') |
|
| 1043 | + ->from('share') |
|
| 1044 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) |
|
| 1045 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1046 | + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 1047 | + $cursor = $qb->executeQuery(); |
|
| 1048 | + |
|
| 1049 | + if ($currentAccess === false) { |
|
| 1050 | + $remote = $cursor->fetchAssociative() !== false; |
|
| 1051 | + $cursor->closeCursor(); |
|
| 1052 | + |
|
| 1053 | + return ['remote' => $remote]; |
|
| 1054 | + } |
|
| 1055 | + |
|
| 1056 | + $remote = []; |
|
| 1057 | + while ($row = $cursor->fetchAssociative()) { |
|
| 1058 | + $remote[$row['share_with']] = [ |
|
| 1059 | + 'node_id' => $row['file_source'], |
|
| 1060 | + 'token' => $row['token'], |
|
| 1061 | + ]; |
|
| 1062 | + } |
|
| 1063 | + $cursor->closeCursor(); |
|
| 1064 | + |
|
| 1065 | + return ['remote' => $remote]; |
|
| 1066 | + } |
|
| 1067 | + |
|
| 1068 | + public function getAllShares(): iterable { |
|
| 1069 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1070 | + |
|
| 1071 | + $qb->select('*') |
|
| 1072 | + ->from('share') |
|
| 1073 | + ->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_REMOTE_GROUP, IShare::TYPE_REMOTE], IQueryBuilder::PARAM_INT_ARRAY))); |
|
| 1074 | + |
|
| 1075 | + $cursor = $qb->executeQuery(); |
|
| 1076 | + while ($data = $cursor->fetchAssociative()) { |
|
| 1077 | + try { |
|
| 1078 | + $share = $this->createShareObject($data); |
|
| 1079 | + } catch (InvalidShare $e) { |
|
| 1080 | + continue; |
|
| 1081 | + } catch (ShareNotFound $e) { |
|
| 1082 | + continue; |
|
| 1083 | + } |
|
| 1084 | + |
|
| 1085 | + yield $share; |
|
| 1086 | + } |
|
| 1087 | + $cursor->closeCursor(); |
|
| 1088 | + } |
|
| 1089 | 1089 | } |
@@ -140,7 +140,7 @@ discard block |
||
| 140 | 140 | if ($remoteShare) { |
| 141 | 141 | try { |
| 142 | 142 | $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
| 143 | - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType, $expirationDate); |
|
| 143 | + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_'.time(), $shareType, $expirationDate); |
|
| 144 | 144 | $share->setId($shareId); |
| 145 | 145 | [$token, $remoteId] = $this->askOwnerToReShare($shareWith, $share, $shareId); |
| 146 | 146 | // remote share was create successfully if we get a valid token as return |
@@ -274,7 +274,7 @@ discard block |
||
| 274 | 274 | $result = $qResult->fetchAllAssociative(); |
| 275 | 275 | $qResult->closeCursor(); |
| 276 | 276 | |
| 277 | - if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
| 277 | + if (isset($result[0]) && (int) $result[0]['remote_id'] > 0) { |
|
| 278 | 278 | return $result[0]; |
| 279 | 279 | } |
| 280 | 280 | |
@@ -408,7 +408,7 @@ discard block |
||
| 408 | 408 | public function getRemoteId(IShare $share): string { |
| 409 | 409 | $query = $this->dbConnection->getQueryBuilder(); |
| 410 | 410 | $query->select('remote_id')->from('federated_reshares') |
| 411 | - ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
| 411 | + ->where($query->expr()->eq('share_id', $query->createNamedParameter((int) $share->getId()))); |
|
| 412 | 412 | $result = $query->executeQuery(); |
| 413 | 413 | $data = $result->fetchAssociative(); |
| 414 | 414 | $result->closeCursor(); |
@@ -417,7 +417,7 @@ discard block |
||
| 417 | 417 | throw new ShareNotFound(); |
| 418 | 418 | } |
| 419 | 419 | |
| 420 | - return (string)$data['remote_id']; |
|
| 420 | + return (string) $data['remote_id']; |
|
| 421 | 421 | } |
| 422 | 422 | |
| 423 | 423 | /** |
@@ -671,7 +671,7 @@ discard block |
||
| 671 | 671 | $cursor->closeCursor(); |
| 672 | 672 | |
| 673 | 673 | if ($data === false) { |
| 674 | - throw new ShareNotFound('Can not find share with ID: ' . $id); |
|
| 674 | + throw new ShareNotFound('Can not find share with ID: '.$id); |
|
| 675 | 675 | } |
| 676 | 676 | |
| 677 | 677 | try { |
@@ -814,16 +814,16 @@ discard block |
||
| 814 | 814 | */ |
| 815 | 815 | private function createShareObject($data) { |
| 816 | 816 | $share = new Share($this->rootFolder, $this->userManager); |
| 817 | - $share->setId((int)$data['id']) |
|
| 818 | - ->setShareType((int)$data['share_type']) |
|
| 819 | - ->setPermissions((int)$data['permissions']) |
|
| 817 | + $share->setId((int) $data['id']) |
|
| 818 | + ->setShareType((int) $data['share_type']) |
|
| 819 | + ->setPermissions((int) $data['permissions']) |
|
| 820 | 820 | ->setTarget($data['file_target']) |
| 821 | - ->setMailSend((bool)$data['mail_send']) |
|
| 822 | - ->setStatus((int)$data['accepted']) |
|
| 821 | + ->setMailSend((bool) $data['mail_send']) |
|
| 822 | + ->setStatus((int) $data['accepted']) |
|
| 823 | 823 | ->setToken($data['token']); |
| 824 | 824 | |
| 825 | 825 | $shareTime = new \DateTime(); |
| 826 | - $shareTime->setTimestamp((int)$data['stime']); |
|
| 826 | + $shareTime->setTimestamp((int) $data['stime']); |
|
| 827 | 827 | $share->setShareTime($shareTime); |
| 828 | 828 | $share->setSharedWith($data['share_with']); |
| 829 | 829 | |
@@ -833,13 +833,13 @@ discard block |
||
| 833 | 833 | } else { |
| 834 | 834 | //OLD SHARE |
| 835 | 835 | $share->setSharedBy($data['uid_owner']); |
| 836 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 836 | + $path = $this->getNode($share->getSharedBy(), (int) $data['file_source']); |
|
| 837 | 837 | |
| 838 | 838 | $owner = $path->getOwner(); |
| 839 | 839 | $share->setShareOwner($owner->getUID()); |
| 840 | 840 | } |
| 841 | 841 | |
| 842 | - $share->setNodeId((int)$data['file_source']); |
|
| 842 | + $share->setNodeId((int) $data['file_source']); |
|
| 843 | 843 | $share->setNodeType($data['item_type']); |
| 844 | 844 | |
| 845 | 845 | $share->setProviderId($this->identifier()); |