@@ -17,142 +17,142 @@ |
||
| 17 | 17 | use OCP\IRequest; |
| 18 | 18 | |
| 19 | 19 | class RecoveryController extends Controller { |
| 20 | - /** |
|
| 21 | - * @param string $AppName |
|
| 22 | - * @param IRequest $request |
|
| 23 | - * @param IConfig $config |
|
| 24 | - * @param IL10N $l |
|
| 25 | - * @param Recovery $recovery |
|
| 26 | - */ |
|
| 27 | - public function __construct( |
|
| 28 | - $appName, |
|
| 29 | - IRequest $request, |
|
| 30 | - private IConfig $config, |
|
| 31 | - private IL10N $l, |
|
| 32 | - private Recovery $recovery, |
|
| 33 | - ) { |
|
| 34 | - parent::__construct($appName, $request); |
|
| 35 | - } |
|
| 20 | + /** |
|
| 21 | + * @param string $AppName |
|
| 22 | + * @param IRequest $request |
|
| 23 | + * @param IConfig $config |
|
| 24 | + * @param IL10N $l |
|
| 25 | + * @param Recovery $recovery |
|
| 26 | + */ |
|
| 27 | + public function __construct( |
|
| 28 | + $appName, |
|
| 29 | + IRequest $request, |
|
| 30 | + private IConfig $config, |
|
| 31 | + private IL10N $l, |
|
| 32 | + private Recovery $recovery, |
|
| 33 | + ) { |
|
| 34 | + parent::__construct($appName, $request); |
|
| 35 | + } |
|
| 36 | 36 | |
| 37 | - /** |
|
| 38 | - * @param string $recoveryPassword |
|
| 39 | - * @param string $confirmPassword |
|
| 40 | - * @param string $adminEnableRecovery |
|
| 41 | - * @return DataResponse |
|
| 42 | - */ |
|
| 43 | - public function adminRecovery($recoveryPassword, $confirmPassword, $adminEnableRecovery) { |
|
| 44 | - // Check if both passwords are the same |
|
| 45 | - if (empty($recoveryPassword)) { |
|
| 46 | - $errorMessage = $this->l->t('Missing recovery key password'); |
|
| 47 | - return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 48 | - Http::STATUS_BAD_REQUEST); |
|
| 49 | - } |
|
| 37 | + /** |
|
| 38 | + * @param string $recoveryPassword |
|
| 39 | + * @param string $confirmPassword |
|
| 40 | + * @param string $adminEnableRecovery |
|
| 41 | + * @return DataResponse |
|
| 42 | + */ |
|
| 43 | + public function adminRecovery($recoveryPassword, $confirmPassword, $adminEnableRecovery) { |
|
| 44 | + // Check if both passwords are the same |
|
| 45 | + if (empty($recoveryPassword)) { |
|
| 46 | + $errorMessage = $this->l->t('Missing recovery key password'); |
|
| 47 | + return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 48 | + Http::STATUS_BAD_REQUEST); |
|
| 49 | + } |
|
| 50 | 50 | |
| 51 | - if (empty($confirmPassword)) { |
|
| 52 | - $errorMessage = $this->l->t('Please repeat the recovery key password'); |
|
| 53 | - return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 54 | - Http::STATUS_BAD_REQUEST); |
|
| 55 | - } |
|
| 51 | + if (empty($confirmPassword)) { |
|
| 52 | + $errorMessage = $this->l->t('Please repeat the recovery key password'); |
|
| 53 | + return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 54 | + Http::STATUS_BAD_REQUEST); |
|
| 55 | + } |
|
| 56 | 56 | |
| 57 | - if ($recoveryPassword !== $confirmPassword) { |
|
| 58 | - $errorMessage = $this->l->t('Repeated recovery key password does not match the provided recovery key password'); |
|
| 59 | - return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 60 | - Http::STATUS_BAD_REQUEST); |
|
| 61 | - } |
|
| 57 | + if ($recoveryPassword !== $confirmPassword) { |
|
| 58 | + $errorMessage = $this->l->t('Repeated recovery key password does not match the provided recovery key password'); |
|
| 59 | + return new DataResponse(['data' => ['message' => $errorMessage]], |
|
| 60 | + Http::STATUS_BAD_REQUEST); |
|
| 61 | + } |
|
| 62 | 62 | |
| 63 | - if (isset($adminEnableRecovery) && $adminEnableRecovery === '1') { |
|
| 64 | - if ($this->recovery->enableAdminRecovery($recoveryPassword)) { |
|
| 65 | - return new DataResponse(['data' => ['message' => $this->l->t('Recovery key successfully enabled')]]); |
|
| 66 | - } |
|
| 67 | - return new DataResponse(['data' => ['message' => $this->l->t('Could not enable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); |
|
| 68 | - } elseif (isset($adminEnableRecovery) && $adminEnableRecovery === '0') { |
|
| 69 | - if ($this->recovery->disableAdminRecovery($recoveryPassword)) { |
|
| 70 | - return new DataResponse(['data' => ['message' => $this->l->t('Recovery key successfully disabled')]]); |
|
| 71 | - } |
|
| 72 | - return new DataResponse(['data' => ['message' => $this->l->t('Could not disable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); |
|
| 73 | - } |
|
| 74 | - // this response should never be sent but just in case. |
|
| 75 | - return new DataResponse(['data' => ['message' => $this->l->t('Missing parameters')]], Http::STATUS_BAD_REQUEST); |
|
| 76 | - } |
|
| 63 | + if (isset($adminEnableRecovery) && $adminEnableRecovery === '1') { |
|
| 64 | + if ($this->recovery->enableAdminRecovery($recoveryPassword)) { |
|
| 65 | + return new DataResponse(['data' => ['message' => $this->l->t('Recovery key successfully enabled')]]); |
|
| 66 | + } |
|
| 67 | + return new DataResponse(['data' => ['message' => $this->l->t('Could not enable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); |
|
| 68 | + } elseif (isset($adminEnableRecovery) && $adminEnableRecovery === '0') { |
|
| 69 | + if ($this->recovery->disableAdminRecovery($recoveryPassword)) { |
|
| 70 | + return new DataResponse(['data' => ['message' => $this->l->t('Recovery key successfully disabled')]]); |
|
| 71 | + } |
|
| 72 | + return new DataResponse(['data' => ['message' => $this->l->t('Could not disable recovery key. Please check your recovery key password!')]], Http::STATUS_BAD_REQUEST); |
|
| 73 | + } |
|
| 74 | + // this response should never be sent but just in case. |
|
| 75 | + return new DataResponse(['data' => ['message' => $this->l->t('Missing parameters')]], Http::STATUS_BAD_REQUEST); |
|
| 76 | + } |
|
| 77 | 77 | |
| 78 | - /** |
|
| 79 | - * @param string $newPassword |
|
| 80 | - * @param string $oldPassword |
|
| 81 | - * @param string $confirmPassword |
|
| 82 | - * @return DataResponse |
|
| 83 | - */ |
|
| 84 | - public function changeRecoveryPassword($newPassword, $oldPassword, $confirmPassword) { |
|
| 85 | - //check if both passwords are the same |
|
| 86 | - if (empty($oldPassword)) { |
|
| 87 | - $errorMessage = $this->l->t('Please provide the old recovery password'); |
|
| 88 | - return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 89 | - } |
|
| 78 | + /** |
|
| 79 | + * @param string $newPassword |
|
| 80 | + * @param string $oldPassword |
|
| 81 | + * @param string $confirmPassword |
|
| 82 | + * @return DataResponse |
|
| 83 | + */ |
|
| 84 | + public function changeRecoveryPassword($newPassword, $oldPassword, $confirmPassword) { |
|
| 85 | + //check if both passwords are the same |
|
| 86 | + if (empty($oldPassword)) { |
|
| 87 | + $errorMessage = $this->l->t('Please provide the old recovery password'); |
|
| 88 | + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 89 | + } |
|
| 90 | 90 | |
| 91 | - if (empty($newPassword)) { |
|
| 92 | - $errorMessage = $this->l->t('Please provide a new recovery password'); |
|
| 93 | - return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 94 | - } |
|
| 91 | + if (empty($newPassword)) { |
|
| 92 | + $errorMessage = $this->l->t('Please provide a new recovery password'); |
|
| 93 | + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 94 | + } |
|
| 95 | 95 | |
| 96 | - if (empty($confirmPassword)) { |
|
| 97 | - $errorMessage = $this->l->t('Please repeat the new recovery password'); |
|
| 98 | - return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 99 | - } |
|
| 96 | + if (empty($confirmPassword)) { |
|
| 97 | + $errorMessage = $this->l->t('Please repeat the new recovery password'); |
|
| 98 | + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 99 | + } |
|
| 100 | 100 | |
| 101 | - if ($newPassword !== $confirmPassword) { |
|
| 102 | - $errorMessage = $this->l->t('Repeated recovery key password does not match the provided recovery key password'); |
|
| 103 | - return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 104 | - } |
|
| 101 | + if ($newPassword !== $confirmPassword) { |
|
| 102 | + $errorMessage = $this->l->t('Repeated recovery key password does not match the provided recovery key password'); |
|
| 103 | + return new DataResponse(['data' => ['message' => $errorMessage]], Http::STATUS_BAD_REQUEST); |
|
| 104 | + } |
|
| 105 | 105 | |
| 106 | - $result = $this->recovery->changeRecoveryKeyPassword($newPassword, |
|
| 107 | - $oldPassword); |
|
| 106 | + $result = $this->recovery->changeRecoveryKeyPassword($newPassword, |
|
| 107 | + $oldPassword); |
|
| 108 | 108 | |
| 109 | - if ($result) { |
|
| 110 | - return new DataResponse( |
|
| 111 | - [ |
|
| 112 | - 'data' => [ |
|
| 113 | - 'message' => $this->l->t('Password successfully changed.')] |
|
| 114 | - ] |
|
| 115 | - ); |
|
| 116 | - } |
|
| 117 | - return new DataResponse( |
|
| 118 | - [ |
|
| 119 | - 'data' => [ |
|
| 120 | - 'message' => $this->l->t('Could not change the password. Maybe the old password was not correct.') |
|
| 121 | - ] |
|
| 122 | - ], Http::STATUS_BAD_REQUEST); |
|
| 123 | - } |
|
| 109 | + if ($result) { |
|
| 110 | + return new DataResponse( |
|
| 111 | + [ |
|
| 112 | + 'data' => [ |
|
| 113 | + 'message' => $this->l->t('Password successfully changed.')] |
|
| 114 | + ] |
|
| 115 | + ); |
|
| 116 | + } |
|
| 117 | + return new DataResponse( |
|
| 118 | + [ |
|
| 119 | + 'data' => [ |
|
| 120 | + 'message' => $this->l->t('Could not change the password. Maybe the old password was not correct.') |
|
| 121 | + ] |
|
| 122 | + ], Http::STATUS_BAD_REQUEST); |
|
| 123 | + } |
|
| 124 | 124 | |
| 125 | - /** |
|
| 126 | - * @param string $userEnableRecovery |
|
| 127 | - * @return DataResponse |
|
| 128 | - */ |
|
| 129 | - #[NoAdminRequired] |
|
| 130 | - public function userSetRecovery($userEnableRecovery) { |
|
| 131 | - if ($userEnableRecovery === '0' || $userEnableRecovery === '1') { |
|
| 132 | - $result = $this->recovery->setRecoveryForUser($userEnableRecovery); |
|
| 125 | + /** |
|
| 126 | + * @param string $userEnableRecovery |
|
| 127 | + * @return DataResponse |
|
| 128 | + */ |
|
| 129 | + #[NoAdminRequired] |
|
| 130 | + public function userSetRecovery($userEnableRecovery) { |
|
| 131 | + if ($userEnableRecovery === '0' || $userEnableRecovery === '1') { |
|
| 132 | + $result = $this->recovery->setRecoveryForUser($userEnableRecovery); |
|
| 133 | 133 | |
| 134 | - if ($result) { |
|
| 135 | - if ($userEnableRecovery === '0') { |
|
| 136 | - return new DataResponse( |
|
| 137 | - [ |
|
| 138 | - 'data' => [ |
|
| 139 | - 'message' => $this->l->t('Recovery Key disabled')] |
|
| 140 | - ] |
|
| 141 | - ); |
|
| 142 | - } |
|
| 143 | - return new DataResponse( |
|
| 144 | - [ |
|
| 145 | - 'data' => [ |
|
| 146 | - 'message' => $this->l->t('Recovery Key enabled')] |
|
| 147 | - ] |
|
| 148 | - ); |
|
| 149 | - } |
|
| 150 | - } |
|
| 151 | - return new DataResponse( |
|
| 152 | - [ |
|
| 153 | - 'data' => [ |
|
| 154 | - 'message' => $this->l->t('Could not enable the recovery key, please try again or contact your administrator') |
|
| 155 | - ] |
|
| 156 | - ], Http::STATUS_BAD_REQUEST); |
|
| 157 | - } |
|
| 134 | + if ($result) { |
|
| 135 | + if ($userEnableRecovery === '0') { |
|
| 136 | + return new DataResponse( |
|
| 137 | + [ |
|
| 138 | + 'data' => [ |
|
| 139 | + 'message' => $this->l->t('Recovery Key disabled')] |
|
| 140 | + ] |
|
| 141 | + ); |
|
| 142 | + } |
|
| 143 | + return new DataResponse( |
|
| 144 | + [ |
|
| 145 | + 'data' => [ |
|
| 146 | + 'message' => $this->l->t('Recovery Key enabled')] |
|
| 147 | + ] |
|
| 148 | + ); |
|
| 149 | + } |
|
| 150 | + } |
|
| 151 | + return new DataResponse( |
|
| 152 | + [ |
|
| 153 | + 'data' => [ |
|
| 154 | + 'message' => $this->l->t('Could not enable the recovery key, please try again or contact your administrator') |
|
| 155 | + ] |
|
| 156 | + ], Http::STATUS_BAD_REQUEST); |
|
| 157 | + } |
|
| 158 | 158 | } |
@@ -17,60 +17,60 @@ |
||
| 17 | 17 | |
| 18 | 18 | class StatusController extends Controller { |
| 19 | 19 | |
| 20 | - /** |
|
| 21 | - * @param string $AppName |
|
| 22 | - * @param IRequest $request |
|
| 23 | - * @param IL10N $l |
|
| 24 | - * @param Session $session |
|
| 25 | - * @param IManager $encryptionManager |
|
| 26 | - */ |
|
| 27 | - public function __construct( |
|
| 28 | - $appName, |
|
| 29 | - IRequest $request, |
|
| 30 | - private IL10N $l, |
|
| 31 | - private Session $session, |
|
| 32 | - private IManager $encryptionManager, |
|
| 33 | - ) { |
|
| 34 | - parent::__construct($appName, $request); |
|
| 35 | - } |
|
| 20 | + /** |
|
| 21 | + * @param string $AppName |
|
| 22 | + * @param IRequest $request |
|
| 23 | + * @param IL10N $l |
|
| 24 | + * @param Session $session |
|
| 25 | + * @param IManager $encryptionManager |
|
| 26 | + */ |
|
| 27 | + public function __construct( |
|
| 28 | + $appName, |
|
| 29 | + IRequest $request, |
|
| 30 | + private IL10N $l, |
|
| 31 | + private Session $session, |
|
| 32 | + private IManager $encryptionManager, |
|
| 33 | + ) { |
|
| 34 | + parent::__construct($appName, $request); |
|
| 35 | + } |
|
| 36 | 36 | |
| 37 | - /** |
|
| 38 | - * @return DataResponse |
|
| 39 | - */ |
|
| 40 | - #[NoAdminRequired] |
|
| 41 | - public function getStatus() { |
|
| 42 | - $status = 'error'; |
|
| 43 | - $message = 'no valid init status'; |
|
| 44 | - switch ($this->session->getStatus()) { |
|
| 45 | - case Session::INIT_EXECUTED: |
|
| 46 | - $status = 'interactionNeeded'; |
|
| 47 | - $message = $this->l->t( |
|
| 48 | - 'Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files.' |
|
| 49 | - ); |
|
| 50 | - break; |
|
| 51 | - case Session::NOT_INITIALIZED: |
|
| 52 | - $status = 'interactionNeeded'; |
|
| 53 | - if ($this->encryptionManager->isEnabled()) { |
|
| 54 | - $message = $this->l->t( |
|
| 55 | - 'Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again.' |
|
| 56 | - ); |
|
| 57 | - } else { |
|
| 58 | - $message = $this->l->t( |
|
| 59 | - 'Please enable server side encryption in the admin settings in order to use the encryption module.' |
|
| 60 | - ); |
|
| 61 | - } |
|
| 62 | - break; |
|
| 63 | - case Session::INIT_SUCCESSFUL: |
|
| 64 | - $status = 'success'; |
|
| 65 | - $message = $this->l->t('Encryption app is enabled and ready'); |
|
| 66 | - } |
|
| 37 | + /** |
|
| 38 | + * @return DataResponse |
|
| 39 | + */ |
|
| 40 | + #[NoAdminRequired] |
|
| 41 | + public function getStatus() { |
|
| 42 | + $status = 'error'; |
|
| 43 | + $message = 'no valid init status'; |
|
| 44 | + switch ($this->session->getStatus()) { |
|
| 45 | + case Session::INIT_EXECUTED: |
|
| 46 | + $status = 'interactionNeeded'; |
|
| 47 | + $message = $this->l->t( |
|
| 48 | + 'Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files.' |
|
| 49 | + ); |
|
| 50 | + break; |
|
| 51 | + case Session::NOT_INITIALIZED: |
|
| 52 | + $status = 'interactionNeeded'; |
|
| 53 | + if ($this->encryptionManager->isEnabled()) { |
|
| 54 | + $message = $this->l->t( |
|
| 55 | + 'Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again.' |
|
| 56 | + ); |
|
| 57 | + } else { |
|
| 58 | + $message = $this->l->t( |
|
| 59 | + 'Please enable server side encryption in the admin settings in order to use the encryption module.' |
|
| 60 | + ); |
|
| 61 | + } |
|
| 62 | + break; |
|
| 63 | + case Session::INIT_SUCCESSFUL: |
|
| 64 | + $status = 'success'; |
|
| 65 | + $message = $this->l->t('Encryption app is enabled and ready'); |
|
| 66 | + } |
|
| 67 | 67 | |
| 68 | - return new DataResponse( |
|
| 69 | - [ |
|
| 70 | - 'status' => $status, |
|
| 71 | - 'data' => [ |
|
| 72 | - 'message' => $message] |
|
| 73 | - ] |
|
| 74 | - ); |
|
| 75 | - } |
|
| 68 | + return new DataResponse( |
|
| 69 | + [ |
|
| 70 | + 'status' => $status, |
|
| 71 | + 'data' => [ |
|
| 72 | + 'message' => $message] |
|
| 73 | + ] |
|
| 74 | + ); |
|
| 75 | + } |
|
| 76 | 76 | } |
@@ -15,116 +15,116 @@ |
||
| 15 | 15 | use Sabre\DAVACL\PrincipalBackend\BackendInterface; |
| 16 | 16 | |
| 17 | 17 | class RemoteUserPrincipalBackend implements BackendInterface { |
| 18 | - public const PRINCIPAL_PREFIX = 'principals/remote-users'; |
|
| 19 | - |
|
| 20 | - private bool $hasCachedAllChildren = false; |
|
| 21 | - |
|
| 22 | - /** @var array<string, mixed>[] */ |
|
| 23 | - private array $principals = []; |
|
| 24 | - |
|
| 25 | - /** @var array<string, array<string, mixed>|null> */ |
|
| 26 | - private array $principalsByPath = []; |
|
| 27 | - |
|
| 28 | - public function __construct( |
|
| 29 | - private readonly ICloudIdManager $cloudIdManager, |
|
| 30 | - private readonly SharingMapper $sharingMapper, |
|
| 31 | - ) { |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - public function getPrincipalsByPrefix($prefixPath) { |
|
| 35 | - if ($prefixPath !== self::PRINCIPAL_PREFIX) { |
|
| 36 | - return []; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - if (!$this->hasCachedAllChildren) { |
|
| 40 | - $this->loadChildren(); |
|
| 41 | - $this->hasCachedAllChildren = true; |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - return $this->principals; |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - public function getPrincipalByPath($path) { |
|
| 48 | - [$prefix] = \Sabre\Uri\split($path); |
|
| 49 | - if ($prefix !== self::PRINCIPAL_PREFIX) { |
|
| 50 | - return null; |
|
| 51 | - } |
|
| 52 | - |
|
| 53 | - if (isset($this->principalsByPath[$path])) { |
|
| 54 | - return $this->principalsByPath[$path]; |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - try { |
|
| 58 | - $principal = $this->principalUriToPrincipal($path); |
|
| 59 | - } catch (\Exception $e) { |
|
| 60 | - $principal = null; |
|
| 61 | - } |
|
| 62 | - $this->principalsByPath[$path] = $principal; |
|
| 63 | - return $principal; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { |
|
| 67 | - throw new \Sabre\DAV\Exception('Updating remote user principal is not supported'); |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { |
|
| 71 | - // Searching is not supported |
|
| 72 | - return []; |
|
| 73 | - } |
|
| 74 | - |
|
| 75 | - public function findByUri($uri, $principalPrefix) { |
|
| 76 | - if (str_starts_with($uri, 'principal:')) { |
|
| 77 | - $principal = substr($uri, strlen('principal:')); |
|
| 78 | - $principal = $this->getPrincipalByPath($principal); |
|
| 79 | - if ($principal !== null) { |
|
| 80 | - return $principal['uri']; |
|
| 81 | - } |
|
| 82 | - } |
|
| 83 | - |
|
| 84 | - return null; |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - public function getGroupMemberSet($principal) { |
|
| 88 | - return []; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - public function getGroupMembership($principal) { |
|
| 92 | - // TODO: for now the group principal has only one member, the user itself |
|
| 93 | - $principal = $this->getPrincipalByPath($principal); |
|
| 94 | - if (!$principal) { |
|
| 95 | - throw new \Sabre\DAV\Exception('Principal not found'); |
|
| 96 | - } |
|
| 97 | - |
|
| 98 | - return [$principal['uri']]; |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - public function setGroupMemberSet($principal, array $members) { |
|
| 102 | - throw new \Sabre\DAV\Exception('Adding members to remote user is not supported'); |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - /** |
|
| 106 | - * @return array{'{DAV:}displayname': string, '{http://nextcloud.com/ns}cloud-id': ICloudId, uri: string} |
|
| 107 | - */ |
|
| 108 | - private function principalUriToPrincipal(string $principalUri): array { |
|
| 109 | - [, $name] = \Sabre\Uri\split($principalUri); |
|
| 110 | - $cloudId = $this->cloudIdManager->resolveCloudId(base64_decode($name)); |
|
| 111 | - return [ |
|
| 112 | - 'uri' => $principalUri, |
|
| 113 | - '{DAV:}displayname' => $cloudId->getDisplayId(), |
|
| 114 | - '{http://nextcloud.com/ns}cloud-id' => $cloudId, |
|
| 115 | - ]; |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - private function loadChildren(): void { |
|
| 119 | - $rows = $this->sharingMapper->getPrincipalUrisByPrefix('calendar', self::PRINCIPAL_PREFIX); |
|
| 120 | - $this->principals = array_map( |
|
| 121 | - fn (array $row) => $this->principalUriToPrincipal($row['principaluri']), |
|
| 122 | - $rows, |
|
| 123 | - ); |
|
| 124 | - |
|
| 125 | - $this->principalsByPath = []; |
|
| 126 | - foreach ($this->principals as $child) { |
|
| 127 | - $this->principalsByPath[$child['uri']] = $child; |
|
| 128 | - } |
|
| 129 | - } |
|
| 18 | + public const PRINCIPAL_PREFIX = 'principals/remote-users'; |
|
| 19 | + |
|
| 20 | + private bool $hasCachedAllChildren = false; |
|
| 21 | + |
|
| 22 | + /** @var array<string, mixed>[] */ |
|
| 23 | + private array $principals = []; |
|
| 24 | + |
|
| 25 | + /** @var array<string, array<string, mixed>|null> */ |
|
| 26 | + private array $principalsByPath = []; |
|
| 27 | + |
|
| 28 | + public function __construct( |
|
| 29 | + private readonly ICloudIdManager $cloudIdManager, |
|
| 30 | + private readonly SharingMapper $sharingMapper, |
|
| 31 | + ) { |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + public function getPrincipalsByPrefix($prefixPath) { |
|
| 35 | + if ($prefixPath !== self::PRINCIPAL_PREFIX) { |
|
| 36 | + return []; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + if (!$this->hasCachedAllChildren) { |
|
| 40 | + $this->loadChildren(); |
|
| 41 | + $this->hasCachedAllChildren = true; |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + return $this->principals; |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + public function getPrincipalByPath($path) { |
|
| 48 | + [$prefix] = \Sabre\Uri\split($path); |
|
| 49 | + if ($prefix !== self::PRINCIPAL_PREFIX) { |
|
| 50 | + return null; |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + if (isset($this->principalsByPath[$path])) { |
|
| 54 | + return $this->principalsByPath[$path]; |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + try { |
|
| 58 | + $principal = $this->principalUriToPrincipal($path); |
|
| 59 | + } catch (\Exception $e) { |
|
| 60 | + $principal = null; |
|
| 61 | + } |
|
| 62 | + $this->principalsByPath[$path] = $principal; |
|
| 63 | + return $principal; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { |
|
| 67 | + throw new \Sabre\DAV\Exception('Updating remote user principal is not supported'); |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { |
|
| 71 | + // Searching is not supported |
|
| 72 | + return []; |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + public function findByUri($uri, $principalPrefix) { |
|
| 76 | + if (str_starts_with($uri, 'principal:')) { |
|
| 77 | + $principal = substr($uri, strlen('principal:')); |
|
| 78 | + $principal = $this->getPrincipalByPath($principal); |
|
| 79 | + if ($principal !== null) { |
|
| 80 | + return $principal['uri']; |
|
| 81 | + } |
|
| 82 | + } |
|
| 83 | + |
|
| 84 | + return null; |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + public function getGroupMemberSet($principal) { |
|
| 88 | + return []; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + public function getGroupMembership($principal) { |
|
| 92 | + // TODO: for now the group principal has only one member, the user itself |
|
| 93 | + $principal = $this->getPrincipalByPath($principal); |
|
| 94 | + if (!$principal) { |
|
| 95 | + throw new \Sabre\DAV\Exception('Principal not found'); |
|
| 96 | + } |
|
| 97 | + |
|
| 98 | + return [$principal['uri']]; |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + public function setGroupMemberSet($principal, array $members) { |
|
| 102 | + throw new \Sabre\DAV\Exception('Adding members to remote user is not supported'); |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + /** |
|
| 106 | + * @return array{'{DAV:}displayname': string, '{http://nextcloud.com/ns}cloud-id': ICloudId, uri: string} |
|
| 107 | + */ |
|
| 108 | + private function principalUriToPrincipal(string $principalUri): array { |
|
| 109 | + [, $name] = \Sabre\Uri\split($principalUri); |
|
| 110 | + $cloudId = $this->cloudIdManager->resolveCloudId(base64_decode($name)); |
|
| 111 | + return [ |
|
| 112 | + 'uri' => $principalUri, |
|
| 113 | + '{DAV:}displayname' => $cloudId->getDisplayId(), |
|
| 114 | + '{http://nextcloud.com/ns}cloud-id' => $cloudId, |
|
| 115 | + ]; |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + private function loadChildren(): void { |
|
| 119 | + $rows = $this->sharingMapper->getPrincipalUrisByPrefix('calendar', self::PRINCIPAL_PREFIX); |
|
| 120 | + $this->principals = array_map( |
|
| 121 | + fn (array $row) => $this->principalUriToPrincipal($row['principaluri']), |
|
| 122 | + $rows, |
|
| 123 | + ); |
|
| 124 | + |
|
| 125 | + $this->principalsByPath = []; |
|
| 126 | + foreach ($this->principals as $child) { |
|
| 127 | + $this->principalsByPath[$child['uri']] = $child; |
|
| 128 | + } |
|
| 129 | + } |
|
| 130 | 130 | } |
@@ -26,186 +26,186 @@ |
||
| 26 | 26 | use Psr\Log\LoggerInterface; |
| 27 | 27 | |
| 28 | 28 | class CalendarFederationProvider implements ICloudFederationProvider { |
| 29 | - public const PROVIDER_ID = 'calendar'; |
|
| 30 | - public const CALENDAR_RESOURCE = 'calendar'; |
|
| 31 | - public const USER_SHARE_TYPE = 'user'; |
|
| 32 | - |
|
| 33 | - public function __construct( |
|
| 34 | - private readonly LoggerInterface $logger, |
|
| 35 | - private readonly FederatedCalendarMapper $federatedCalendarMapper, |
|
| 36 | - private readonly CalendarFederationConfig $calendarFederationConfig, |
|
| 37 | - private readonly IJobList $jobList, |
|
| 38 | - private readonly ICloudIdManager $cloudIdManager, |
|
| 39 | - ) { |
|
| 40 | - } |
|
| 41 | - |
|
| 42 | - public function getShareType(): string { |
|
| 43 | - return self::PROVIDER_ID; |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - public function shareReceived(ICloudFederationShare $share): string { |
|
| 47 | - if (!$this->calendarFederationConfig->isFederationEnabled()) { |
|
| 48 | - $this->logger->debug('Received a federation invite but federation is disabled'); |
|
| 49 | - throw new ProviderCouldNotAddShareException( |
|
| 50 | - 'Server does not support calendar federation', |
|
| 51 | - '', |
|
| 52 | - Http::STATUS_SERVICE_UNAVAILABLE, |
|
| 53 | - ); |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - if (!in_array($share->getShareType(), $this->getSupportedShareTypes(), true)) { |
|
| 57 | - $this->logger->debug('Received a federation invite for invalid share type'); |
|
| 58 | - throw new ProviderCouldNotAddShareException( |
|
| 59 | - 'Support for sharing with non-users not implemented yet', |
|
| 60 | - '', |
|
| 61 | - Http::STATUS_NOT_IMPLEMENTED, |
|
| 62 | - ); |
|
| 63 | - // TODO: Implement group shares |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - $rawProtocol = $share->getProtocol(); |
|
| 67 | - if (!isset($rawProtocol[ICalendarFederationProtocol::PROP_VERSION])) { |
|
| 68 | - throw new ProviderCouldNotAddShareException( |
|
| 69 | - 'No protocol version', |
|
| 70 | - '', |
|
| 71 | - Http::STATUS_BAD_REQUEST, |
|
| 72 | - ); |
|
| 73 | - } |
|
| 74 | - switch ($rawProtocol[ICalendarFederationProtocol::PROP_VERSION]) { |
|
| 75 | - case CalendarFederationProtocolV1::VERSION: |
|
| 76 | - try { |
|
| 77 | - $protocol = CalendarFederationProtocolV1::parse($rawProtocol); |
|
| 78 | - } catch (CalendarProtocolParseException $e) { |
|
| 79 | - throw new ProviderCouldNotAddShareException( |
|
| 80 | - 'Invalid protocol data (v1)', |
|
| 81 | - '', |
|
| 82 | - Http::STATUS_BAD_REQUEST, |
|
| 83 | - ); |
|
| 84 | - } |
|
| 85 | - $calendarUrl = $protocol->getUrl(); |
|
| 86 | - $displayName = $protocol->getDisplayName(); |
|
| 87 | - $color = $protocol->getColor(); |
|
| 88 | - $access = $protocol->getAccess(); |
|
| 89 | - $components = $protocol->getComponents(); |
|
| 90 | - break; |
|
| 91 | - default: |
|
| 92 | - throw new ProviderCouldNotAddShareException( |
|
| 93 | - 'Unknown protocol version', |
|
| 94 | - '', |
|
| 95 | - Http::STATUS_BAD_REQUEST, |
|
| 96 | - ); |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - if (!$calendarUrl || !$displayName) { |
|
| 100 | - throw new ProviderCouldNotAddShareException( |
|
| 101 | - 'Incomplete protocol data', |
|
| 102 | - '', |
|
| 103 | - Http::STATUS_BAD_REQUEST, |
|
| 104 | - ); |
|
| 105 | - } |
|
| 106 | - |
|
| 107 | - // TODO: implement read-write sharing |
|
| 108 | - $permissions = match ($access) { |
|
| 109 | - DavSharingBackend::ACCESS_READ => Constants::PERMISSION_READ, |
|
| 110 | - default => throw new ProviderCouldNotAddShareException( |
|
| 111 | - "Unsupported access value: $access", |
|
| 112 | - '', |
|
| 113 | - Http::STATUS_BAD_REQUEST, |
|
| 114 | - ), |
|
| 115 | - }; |
|
| 116 | - |
|
| 117 | - // The calendar uri is the local name of the calendar. As such it must not contain slashes. |
|
| 118 | - // Just use the hashed url for simplicity here. |
|
| 119 | - // Example: calendars/foo-bar-user/<calendar-uri> |
|
| 120 | - $calendarUri = hash('md5', $calendarUrl); |
|
| 121 | - |
|
| 122 | - $sharedWithPrincipal = 'principals/users/' . $share->getShareWith(); |
|
| 123 | - |
|
| 124 | - // Delete existing incoming federated share first |
|
| 125 | - $this->federatedCalendarMapper->deleteByUri($sharedWithPrincipal, $calendarUri); |
|
| 126 | - |
|
| 127 | - $calendar = new FederatedCalendarEntity(); |
|
| 128 | - $calendar->setPrincipaluri($sharedWithPrincipal); |
|
| 129 | - $calendar->setUri($calendarUri); |
|
| 130 | - $calendar->setRemoteUrl($calendarUrl); |
|
| 131 | - $calendar->setDisplayName($displayName); |
|
| 132 | - $calendar->setColor($color); |
|
| 133 | - $calendar->setToken($share->getShareSecret()); |
|
| 134 | - $calendar->setSharedBy($share->getSharedBy()); |
|
| 135 | - $calendar->setSharedByDisplayName($share->getSharedByDisplayName()); |
|
| 136 | - $calendar->setPermissions($permissions); |
|
| 137 | - $calendar->setComponents($components); |
|
| 138 | - $calendar = $this->federatedCalendarMapper->insert($calendar); |
|
| 139 | - |
|
| 140 | - $this->jobList->add(FederatedCalendarSyncJob::class, [ |
|
| 141 | - FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(), |
|
| 142 | - ]); |
|
| 143 | - |
|
| 144 | - return (string)$calendar->getId(); |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - public function notificationReceived( |
|
| 148 | - $notificationType, |
|
| 149 | - $providerId, |
|
| 150 | - array $notification, |
|
| 151 | - ): array { |
|
| 152 | - if ($providerId !== self::PROVIDER_ID) { |
|
| 153 | - throw new BadRequestException(['providerId']); |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - switch ($notificationType) { |
|
| 157 | - case CalendarFederationNotifier::NOTIFICATION_SYNC_CALENDAR: |
|
| 158 | - return $this->handleSyncCalendarNotification($notification); |
|
| 159 | - default: |
|
| 160 | - return []; |
|
| 161 | - } |
|
| 162 | - } |
|
| 163 | - |
|
| 164 | - /** |
|
| 165 | - * @return string[] |
|
| 166 | - */ |
|
| 167 | - public function getSupportedShareTypes(): array { |
|
| 168 | - return [self::USER_SHARE_TYPE]; |
|
| 169 | - } |
|
| 170 | - |
|
| 171 | - /** |
|
| 172 | - * @throws BadRequestException If notification props are missing. |
|
| 173 | - * @throws ShareNotFound If the notification is not related to a known share. |
|
| 174 | - */ |
|
| 175 | - private function handleSyncCalendarNotification(array $notification): array { |
|
| 176 | - $sharedSecret = $notification['sharedSecret']; |
|
| 177 | - $shareWithRaw = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH] ?? null; |
|
| 178 | - $calendarUrl = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL] ?? null; |
|
| 179 | - |
|
| 180 | - if ($shareWithRaw === null || $shareWithRaw === '') { |
|
| 181 | - throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH]); |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - if ($calendarUrl === null || $calendarUrl === '') { |
|
| 185 | - throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL]); |
|
| 186 | - } |
|
| 187 | - |
|
| 188 | - try { |
|
| 189 | - $shareWith = $this->cloudIdManager->resolveCloudId($shareWithRaw); |
|
| 190 | - } catch (\InvalidArgumentException $e) { |
|
| 191 | - throw new ShareNotFound('Invalid sharee cloud id'); |
|
| 192 | - } |
|
| 193 | - |
|
| 194 | - $calendars = $this->federatedCalendarMapper->findByRemoteUrl( |
|
| 195 | - $calendarUrl, |
|
| 196 | - 'principals/users/' . $shareWith->getUser(), |
|
| 197 | - $sharedSecret, |
|
| 198 | - ); |
|
| 199 | - if (empty($calendars)) { |
|
| 200 | - throw new ShareNotFound('Calendar is not shared with the sharee'); |
|
| 201 | - } |
|
| 202 | - |
|
| 203 | - foreach ($calendars as $calendar) { |
|
| 204 | - $this->jobList->add(FederatedCalendarSyncJob::class, [ |
|
| 205 | - FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(), |
|
| 206 | - ]); |
|
| 207 | - } |
|
| 208 | - |
|
| 209 | - return []; |
|
| 210 | - } |
|
| 29 | + public const PROVIDER_ID = 'calendar'; |
|
| 30 | + public const CALENDAR_RESOURCE = 'calendar'; |
|
| 31 | + public const USER_SHARE_TYPE = 'user'; |
|
| 32 | + |
|
| 33 | + public function __construct( |
|
| 34 | + private readonly LoggerInterface $logger, |
|
| 35 | + private readonly FederatedCalendarMapper $federatedCalendarMapper, |
|
| 36 | + private readonly CalendarFederationConfig $calendarFederationConfig, |
|
| 37 | + private readonly IJobList $jobList, |
|
| 38 | + private readonly ICloudIdManager $cloudIdManager, |
|
| 39 | + ) { |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + public function getShareType(): string { |
|
| 43 | + return self::PROVIDER_ID; |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + public function shareReceived(ICloudFederationShare $share): string { |
|
| 47 | + if (!$this->calendarFederationConfig->isFederationEnabled()) { |
|
| 48 | + $this->logger->debug('Received a federation invite but federation is disabled'); |
|
| 49 | + throw new ProviderCouldNotAddShareException( |
|
| 50 | + 'Server does not support calendar federation', |
|
| 51 | + '', |
|
| 52 | + Http::STATUS_SERVICE_UNAVAILABLE, |
|
| 53 | + ); |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + if (!in_array($share->getShareType(), $this->getSupportedShareTypes(), true)) { |
|
| 57 | + $this->logger->debug('Received a federation invite for invalid share type'); |
|
| 58 | + throw new ProviderCouldNotAddShareException( |
|
| 59 | + 'Support for sharing with non-users not implemented yet', |
|
| 60 | + '', |
|
| 61 | + Http::STATUS_NOT_IMPLEMENTED, |
|
| 62 | + ); |
|
| 63 | + // TODO: Implement group shares |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + $rawProtocol = $share->getProtocol(); |
|
| 67 | + if (!isset($rawProtocol[ICalendarFederationProtocol::PROP_VERSION])) { |
|
| 68 | + throw new ProviderCouldNotAddShareException( |
|
| 69 | + 'No protocol version', |
|
| 70 | + '', |
|
| 71 | + Http::STATUS_BAD_REQUEST, |
|
| 72 | + ); |
|
| 73 | + } |
|
| 74 | + switch ($rawProtocol[ICalendarFederationProtocol::PROP_VERSION]) { |
|
| 75 | + case CalendarFederationProtocolV1::VERSION: |
|
| 76 | + try { |
|
| 77 | + $protocol = CalendarFederationProtocolV1::parse($rawProtocol); |
|
| 78 | + } catch (CalendarProtocolParseException $e) { |
|
| 79 | + throw new ProviderCouldNotAddShareException( |
|
| 80 | + 'Invalid protocol data (v1)', |
|
| 81 | + '', |
|
| 82 | + Http::STATUS_BAD_REQUEST, |
|
| 83 | + ); |
|
| 84 | + } |
|
| 85 | + $calendarUrl = $protocol->getUrl(); |
|
| 86 | + $displayName = $protocol->getDisplayName(); |
|
| 87 | + $color = $protocol->getColor(); |
|
| 88 | + $access = $protocol->getAccess(); |
|
| 89 | + $components = $protocol->getComponents(); |
|
| 90 | + break; |
|
| 91 | + default: |
|
| 92 | + throw new ProviderCouldNotAddShareException( |
|
| 93 | + 'Unknown protocol version', |
|
| 94 | + '', |
|
| 95 | + Http::STATUS_BAD_REQUEST, |
|
| 96 | + ); |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + if (!$calendarUrl || !$displayName) { |
|
| 100 | + throw new ProviderCouldNotAddShareException( |
|
| 101 | + 'Incomplete protocol data', |
|
| 102 | + '', |
|
| 103 | + Http::STATUS_BAD_REQUEST, |
|
| 104 | + ); |
|
| 105 | + } |
|
| 106 | + |
|
| 107 | + // TODO: implement read-write sharing |
|
| 108 | + $permissions = match ($access) { |
|
| 109 | + DavSharingBackend::ACCESS_READ => Constants::PERMISSION_READ, |
|
| 110 | + default => throw new ProviderCouldNotAddShareException( |
|
| 111 | + "Unsupported access value: $access", |
|
| 112 | + '', |
|
| 113 | + Http::STATUS_BAD_REQUEST, |
|
| 114 | + ), |
|
| 115 | + }; |
|
| 116 | + |
|
| 117 | + // The calendar uri is the local name of the calendar. As such it must not contain slashes. |
|
| 118 | + // Just use the hashed url for simplicity here. |
|
| 119 | + // Example: calendars/foo-bar-user/<calendar-uri> |
|
| 120 | + $calendarUri = hash('md5', $calendarUrl); |
|
| 121 | + |
|
| 122 | + $sharedWithPrincipal = 'principals/users/' . $share->getShareWith(); |
|
| 123 | + |
|
| 124 | + // Delete existing incoming federated share first |
|
| 125 | + $this->federatedCalendarMapper->deleteByUri($sharedWithPrincipal, $calendarUri); |
|
| 126 | + |
|
| 127 | + $calendar = new FederatedCalendarEntity(); |
|
| 128 | + $calendar->setPrincipaluri($sharedWithPrincipal); |
|
| 129 | + $calendar->setUri($calendarUri); |
|
| 130 | + $calendar->setRemoteUrl($calendarUrl); |
|
| 131 | + $calendar->setDisplayName($displayName); |
|
| 132 | + $calendar->setColor($color); |
|
| 133 | + $calendar->setToken($share->getShareSecret()); |
|
| 134 | + $calendar->setSharedBy($share->getSharedBy()); |
|
| 135 | + $calendar->setSharedByDisplayName($share->getSharedByDisplayName()); |
|
| 136 | + $calendar->setPermissions($permissions); |
|
| 137 | + $calendar->setComponents($components); |
|
| 138 | + $calendar = $this->federatedCalendarMapper->insert($calendar); |
|
| 139 | + |
|
| 140 | + $this->jobList->add(FederatedCalendarSyncJob::class, [ |
|
| 141 | + FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(), |
|
| 142 | + ]); |
|
| 143 | + |
|
| 144 | + return (string)$calendar->getId(); |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + public function notificationReceived( |
|
| 148 | + $notificationType, |
|
| 149 | + $providerId, |
|
| 150 | + array $notification, |
|
| 151 | + ): array { |
|
| 152 | + if ($providerId !== self::PROVIDER_ID) { |
|
| 153 | + throw new BadRequestException(['providerId']); |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + switch ($notificationType) { |
|
| 157 | + case CalendarFederationNotifier::NOTIFICATION_SYNC_CALENDAR: |
|
| 158 | + return $this->handleSyncCalendarNotification($notification); |
|
| 159 | + default: |
|
| 160 | + return []; |
|
| 161 | + } |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + /** |
|
| 165 | + * @return string[] |
|
| 166 | + */ |
|
| 167 | + public function getSupportedShareTypes(): array { |
|
| 168 | + return [self::USER_SHARE_TYPE]; |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + /** |
|
| 172 | + * @throws BadRequestException If notification props are missing. |
|
| 173 | + * @throws ShareNotFound If the notification is not related to a known share. |
|
| 174 | + */ |
|
| 175 | + private function handleSyncCalendarNotification(array $notification): array { |
|
| 176 | + $sharedSecret = $notification['sharedSecret']; |
|
| 177 | + $shareWithRaw = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH] ?? null; |
|
| 178 | + $calendarUrl = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL] ?? null; |
|
| 179 | + |
|
| 180 | + if ($shareWithRaw === null || $shareWithRaw === '') { |
|
| 181 | + throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH]); |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + if ($calendarUrl === null || $calendarUrl === '') { |
|
| 185 | + throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL]); |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + try { |
|
| 189 | + $shareWith = $this->cloudIdManager->resolveCloudId($shareWithRaw); |
|
| 190 | + } catch (\InvalidArgumentException $e) { |
|
| 191 | + throw new ShareNotFound('Invalid sharee cloud id'); |
|
| 192 | + } |
|
| 193 | + |
|
| 194 | + $calendars = $this->federatedCalendarMapper->findByRemoteUrl( |
|
| 195 | + $calendarUrl, |
|
| 196 | + 'principals/users/' . $shareWith->getUser(), |
|
| 197 | + $sharedSecret, |
|
| 198 | + ); |
|
| 199 | + if (empty($calendars)) { |
|
| 200 | + throw new ShareNotFound('Calendar is not shared with the sharee'); |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + foreach ($calendars as $calendar) { |
|
| 204 | + $this->jobList->add(FederatedCalendarSyncJob::class, [ |
|
| 205 | + FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(), |
|
| 206 | + ]); |
|
| 207 | + } |
|
| 208 | + |
|
| 209 | + return []; |
|
| 210 | + } |
|
| 211 | 211 | } |
@@ -39,25 +39,25 @@ discard block |
||
| 39 | 39 | use Psr\Log\LoggerInterface; |
| 40 | 40 | |
| 41 | 41 | $authBackend = new Auth( |
| 42 | - Server::get(ISession::class), |
|
| 43 | - Server::get(IUserSession::class), |
|
| 44 | - Server::get(IRequest::class), |
|
| 45 | - Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
| 46 | - Server::get(IThrottler::class), |
|
| 47 | - 'principals/' |
|
| 42 | + Server::get(ISession::class), |
|
| 43 | + Server::get(IUserSession::class), |
|
| 44 | + Server::get(IRequest::class), |
|
| 45 | + Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
| 46 | + Server::get(IThrottler::class), |
|
| 47 | + 'principals/' |
|
| 48 | 48 | ); |
| 49 | 49 | $principalBackend = new Principal( |
| 50 | - Server::get(IUserManager::class), |
|
| 51 | - Server::get(IGroupManager::class), |
|
| 52 | - Server::get(IAccountManager::class), |
|
| 53 | - Server::get(\OCP\Share\IManager::class), |
|
| 54 | - Server::get(IUserSession::class), |
|
| 55 | - Server::get(IAppManager::class), |
|
| 56 | - Server::get(ProxyMapper::class), |
|
| 57 | - Server::get(KnownUserService::class), |
|
| 58 | - Server::get(IConfig::class), |
|
| 59 | - \OC::$server->getL10NFactory(), |
|
| 60 | - 'principals/' |
|
| 50 | + Server::get(IUserManager::class), |
|
| 51 | + Server::get(IGroupManager::class), |
|
| 52 | + Server::get(IAccountManager::class), |
|
| 53 | + Server::get(\OCP\Share\IManager::class), |
|
| 54 | + Server::get(IUserSession::class), |
|
| 55 | + Server::get(IAppManager::class), |
|
| 56 | + Server::get(ProxyMapper::class), |
|
| 57 | + Server::get(KnownUserService::class), |
|
| 58 | + Server::get(IConfig::class), |
|
| 59 | + \OC::$server->getL10NFactory(), |
|
| 60 | + 'principals/' |
|
| 61 | 61 | ); |
| 62 | 62 | $db = Server::get(IDBConnection::class); |
| 63 | 63 | $userManager = Server::get(IUserManager::class); |
@@ -70,17 +70,17 @@ discard block |
||
| 70 | 70 | $federatedCalendarFactory = Server::get(FederatedCalendarFactory::class); |
| 71 | 71 | |
| 72 | 72 | $calDavBackend = new CalDavBackend( |
| 73 | - $db, |
|
| 74 | - $principalBackend, |
|
| 75 | - $userManager, |
|
| 76 | - $random, |
|
| 77 | - $logger, |
|
| 78 | - $dispatcher, |
|
| 79 | - $config, |
|
| 80 | - Server::get(\OCA\DAV\CalDAV\Sharing\Backend::class), |
|
| 81 | - Server::get(FederatedCalendarMapper::class), |
|
| 82 | - Server::get(ICacheFactory::class), |
|
| 83 | - true |
|
| 73 | + $db, |
|
| 74 | + $principalBackend, |
|
| 75 | + $userManager, |
|
| 76 | + $random, |
|
| 77 | + $logger, |
|
| 78 | + $dispatcher, |
|
| 79 | + $config, |
|
| 80 | + Server::get(\OCA\DAV\CalDAV\Sharing\Backend::class), |
|
| 81 | + Server::get(FederatedCalendarMapper::class), |
|
| 82 | + Server::get(ICacheFactory::class), |
|
| 83 | + true |
|
| 84 | 84 | ); |
| 85 | 85 | |
| 86 | 86 | $debugging = Server::get(IConfig::class)->getSystemValue('debug', false); |
@@ -94,8 +94,8 @@ discard block |
||
| 94 | 94 | $addressBookRoot->disableListing = !$debugging; // Disable listing |
| 95 | 95 | |
| 96 | 96 | $nodes = [ |
| 97 | - $principalCollection, |
|
| 98 | - $addressBookRoot, |
|
| 97 | + $principalCollection, |
|
| 98 | + $addressBookRoot, |
|
| 99 | 99 | ]; |
| 100 | 100 | |
| 101 | 101 | // Fire up server |
@@ -111,7 +111,7 @@ discard block |
||
| 111 | 111 | |
| 112 | 112 | $server->addPlugin(new LegacyDAVACL()); |
| 113 | 113 | if ($debugging) { |
| 114 | - $server->addPlugin(new Sabre\DAV\Browser\Plugin()); |
|
| 114 | + $server->addPlugin(new Sabre\DAV\Browser\Plugin()); |
|
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); |
@@ -119,7 +119,7 @@ discard block |
||
| 119 | 119 | $server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(Server::get(IConfig::class), Server::get(LoggerInterface::class), Server::get(DefaultCalendarValidator::class))); |
| 120 | 120 | |
| 121 | 121 | if ($sendInvitations) { |
| 122 | - $server->addPlugin(Server::get(IMipPlugin::class)); |
|
| 122 | + $server->addPlugin(Server::get(IMipPlugin::class)); |
|
| 123 | 123 | } |
| 124 | 124 | $server->addPlugin(new ExceptionLoggerPlugin('caldav', $logger)); |
| 125 | 125 | $server->addPlugin(Server::get(RateLimitingPlugin::class)); |
@@ -16,116 +16,116 @@ |
||
| 16 | 16 | |
| 17 | 17 | class PropFindMonitorPluginTest extends TestCase { |
| 18 | 18 | |
| 19 | - private PropFindMonitorPlugin $plugin; |
|
| 20 | - private Server&MockObject $server; |
|
| 21 | - private LoggerInterface&MockObject $logger; |
|
| 22 | - private Request&MockObject $request; |
|
| 23 | - private Response&MockObject $response; |
|
| 19 | + private PropFindMonitorPlugin $plugin; |
|
| 20 | + private Server&MockObject $server; |
|
| 21 | + private LoggerInterface&MockObject $logger; |
|
| 22 | + private Request&MockObject $request; |
|
| 23 | + private Response&MockObject $response; |
|
| 24 | 24 | |
| 25 | - public static function dataTest(): array { |
|
| 26 | - $minQueriesTrigger = PropFindMonitorPlugin::THRESHOLD_QUERY_FACTOR |
|
| 27 | - * PropFindMonitorPlugin::THRESHOLD_NODES; |
|
| 28 | - return [ |
|
| 29 | - 'No queries logged' => [[], 0], |
|
| 30 | - 'Plugins with queries in less than threshold nodes should not be logged' => [ |
|
| 31 | - [ |
|
| 32 | - 'propFind' => [ |
|
| 33 | - [ |
|
| 34 | - 'PluginName' => [ |
|
| 35 | - 'queries' => 100, |
|
| 36 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1] |
|
| 37 | - ], |
|
| 38 | - [], |
|
| 39 | - ] |
|
| 40 | - ], |
|
| 41 | - 0 |
|
| 42 | - ], |
|
| 43 | - 'Plugins with query-to-node ratio less than threshold should not be logged' => [ |
|
| 44 | - [ |
|
| 45 | - 'propFind' => [ |
|
| 46 | - [ |
|
| 47 | - 'PluginName' => [ |
|
| 48 | - 'queries' => $minQueriesTrigger - 1, |
|
| 49 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES ], |
|
| 50 | - ], |
|
| 51 | - [], |
|
| 52 | - ] |
|
| 53 | - ], |
|
| 54 | - 0 |
|
| 55 | - ], |
|
| 56 | - 'Plugins with more nodes scanned than queries executed should not be logged' => [ |
|
| 57 | - [ |
|
| 58 | - 'propFind' => [ |
|
| 59 | - [ |
|
| 60 | - 'PluginName' => [ |
|
| 61 | - 'queries' => $minQueriesTrigger, |
|
| 62 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES * 2], |
|
| 63 | - ], |
|
| 64 | - [],] |
|
| 65 | - ], |
|
| 66 | - 0 |
|
| 67 | - ], |
|
| 68 | - 'Plugins with queries only in highest depth level should not be logged' => [ |
|
| 69 | - [ |
|
| 70 | - 'propFind' => [ |
|
| 71 | - [ |
|
| 72 | - 'PluginName' => [ |
|
| 73 | - 'queries' => $minQueriesTrigger, |
|
| 74 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1 |
|
| 75 | - ] |
|
| 76 | - ], |
|
| 77 | - [ |
|
| 78 | - 'PluginName' => [ |
|
| 79 | - 'queries' => $minQueriesTrigger * 2, |
|
| 80 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES |
|
| 81 | - ] |
|
| 82 | - ], |
|
| 83 | - ] |
|
| 84 | - ], |
|
| 85 | - 0 |
|
| 86 | - ], |
|
| 87 | - 'Plugins with too many queries should be logged' => [ |
|
| 88 | - [ |
|
| 89 | - 'propFind' => [ |
|
| 90 | - [ |
|
| 91 | - 'FirstPlugin' => [ |
|
| 92 | - 'queries' => $minQueriesTrigger, |
|
| 93 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, |
|
| 94 | - ], |
|
| 95 | - 'SecondPlugin' => [ |
|
| 96 | - 'queries' => $minQueriesTrigger, |
|
| 97 | - 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, |
|
| 98 | - ] |
|
| 99 | - ], |
|
| 100 | - [], |
|
| 101 | - ] |
|
| 102 | - ], |
|
| 103 | - 2 |
|
| 104 | - ] |
|
| 105 | - ]; |
|
| 106 | - } |
|
| 25 | + public static function dataTest(): array { |
|
| 26 | + $minQueriesTrigger = PropFindMonitorPlugin::THRESHOLD_QUERY_FACTOR |
|
| 27 | + * PropFindMonitorPlugin::THRESHOLD_NODES; |
|
| 28 | + return [ |
|
| 29 | + 'No queries logged' => [[], 0], |
|
| 30 | + 'Plugins with queries in less than threshold nodes should not be logged' => [ |
|
| 31 | + [ |
|
| 32 | + 'propFind' => [ |
|
| 33 | + [ |
|
| 34 | + 'PluginName' => [ |
|
| 35 | + 'queries' => 100, |
|
| 36 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1] |
|
| 37 | + ], |
|
| 38 | + [], |
|
| 39 | + ] |
|
| 40 | + ], |
|
| 41 | + 0 |
|
| 42 | + ], |
|
| 43 | + 'Plugins with query-to-node ratio less than threshold should not be logged' => [ |
|
| 44 | + [ |
|
| 45 | + 'propFind' => [ |
|
| 46 | + [ |
|
| 47 | + 'PluginName' => [ |
|
| 48 | + 'queries' => $minQueriesTrigger - 1, |
|
| 49 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES ], |
|
| 50 | + ], |
|
| 51 | + [], |
|
| 52 | + ] |
|
| 53 | + ], |
|
| 54 | + 0 |
|
| 55 | + ], |
|
| 56 | + 'Plugins with more nodes scanned than queries executed should not be logged' => [ |
|
| 57 | + [ |
|
| 58 | + 'propFind' => [ |
|
| 59 | + [ |
|
| 60 | + 'PluginName' => [ |
|
| 61 | + 'queries' => $minQueriesTrigger, |
|
| 62 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES * 2], |
|
| 63 | + ], |
|
| 64 | + [],] |
|
| 65 | + ], |
|
| 66 | + 0 |
|
| 67 | + ], |
|
| 68 | + 'Plugins with queries only in highest depth level should not be logged' => [ |
|
| 69 | + [ |
|
| 70 | + 'propFind' => [ |
|
| 71 | + [ |
|
| 72 | + 'PluginName' => [ |
|
| 73 | + 'queries' => $minQueriesTrigger, |
|
| 74 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1 |
|
| 75 | + ] |
|
| 76 | + ], |
|
| 77 | + [ |
|
| 78 | + 'PluginName' => [ |
|
| 79 | + 'queries' => $minQueriesTrigger * 2, |
|
| 80 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES |
|
| 81 | + ] |
|
| 82 | + ], |
|
| 83 | + ] |
|
| 84 | + ], |
|
| 85 | + 0 |
|
| 86 | + ], |
|
| 87 | + 'Plugins with too many queries should be logged' => [ |
|
| 88 | + [ |
|
| 89 | + 'propFind' => [ |
|
| 90 | + [ |
|
| 91 | + 'FirstPlugin' => [ |
|
| 92 | + 'queries' => $minQueriesTrigger, |
|
| 93 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, |
|
| 94 | + ], |
|
| 95 | + 'SecondPlugin' => [ |
|
| 96 | + 'queries' => $minQueriesTrigger, |
|
| 97 | + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, |
|
| 98 | + ] |
|
| 99 | + ], |
|
| 100 | + [], |
|
| 101 | + ] |
|
| 102 | + ], |
|
| 103 | + 2 |
|
| 104 | + ] |
|
| 105 | + ]; |
|
| 106 | + } |
|
| 107 | 107 | |
| 108 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTest')] |
|
| 109 | - public function test(array $queries, $expectedLogCalls): void { |
|
| 110 | - $this->plugin->initialize($this->server); |
|
| 111 | - $this->server->expects($this->once())->method('getPluginQueries') |
|
| 112 | - ->willReturn($queries); |
|
| 108 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTest')] |
|
| 109 | + public function test(array $queries, $expectedLogCalls): void { |
|
| 110 | + $this->plugin->initialize($this->server); |
|
| 111 | + $this->server->expects($this->once())->method('getPluginQueries') |
|
| 112 | + ->willReturn($queries); |
|
| 113 | 113 | |
| 114 | - $this->server->expects(empty($queries) ? $this->never() : $this->once()) |
|
| 115 | - ->method('getLogger') |
|
| 116 | - ->willReturn($this->logger); |
|
| 114 | + $this->server->expects(empty($queries) ? $this->never() : $this->once()) |
|
| 115 | + ->method('getLogger') |
|
| 116 | + ->willReturn($this->logger); |
|
| 117 | 117 | |
| 118 | - $this->logger->expects($this->exactly($expectedLogCalls))->method('error'); |
|
| 119 | - $this->plugin->afterResponse($this->request, $this->response); |
|
| 120 | - } |
|
| 118 | + $this->logger->expects($this->exactly($expectedLogCalls))->method('error'); |
|
| 119 | + $this->plugin->afterResponse($this->request, $this->response); |
|
| 120 | + } |
|
| 121 | 121 | |
| 122 | - protected function setUp(): void { |
|
| 123 | - parent::setUp(); |
|
| 122 | + protected function setUp(): void { |
|
| 123 | + parent::setUp(); |
|
| 124 | 124 | |
| 125 | - $this->plugin = new PropFindMonitorPlugin(); |
|
| 126 | - $this->server = $this->createMock(Server::class); |
|
| 127 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
| 128 | - $this->request = $this->createMock(Request::class); |
|
| 129 | - $this->response = $this->createMock(Response::class); |
|
| 130 | - } |
|
| 125 | + $this->plugin = new PropFindMonitorPlugin(); |
|
| 126 | + $this->server = $this->createMock(Server::class); |
|
| 127 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
| 128 | + $this->request = $this->createMock(Request::class); |
|
| 129 | + $this->response = $this->createMock(Response::class); |
|
| 130 | + } |
|
| 131 | 131 | } |
@@ -23,167 +23,167 @@ |
||
| 23 | 23 | * Global storages controller |
| 24 | 24 | */ |
| 25 | 25 | class GlobalStoragesController extends StoragesController { |
| 26 | - /** |
|
| 27 | - * Creates a new global storages controller. |
|
| 28 | - * |
|
| 29 | - * @param string $AppName application name |
|
| 30 | - * @param IRequest $request request object |
|
| 31 | - * @param IL10N $l10n l10n service |
|
| 32 | - * @param GlobalStoragesService $globalStoragesService storage service |
|
| 33 | - * @param LoggerInterface $logger |
|
| 34 | - * @param IUserSession $userSession |
|
| 35 | - * @param IGroupManager $groupManager |
|
| 36 | - * @param IConfig $config |
|
| 37 | - */ |
|
| 38 | - public function __construct( |
|
| 39 | - $appName, |
|
| 40 | - IRequest $request, |
|
| 41 | - IL10N $l10n, |
|
| 42 | - GlobalStoragesService $globalStoragesService, |
|
| 43 | - LoggerInterface $logger, |
|
| 44 | - IUserSession $userSession, |
|
| 45 | - IGroupManager $groupManager, |
|
| 46 | - IConfig $config, |
|
| 47 | - ) { |
|
| 48 | - parent::__construct( |
|
| 49 | - $appName, |
|
| 50 | - $request, |
|
| 51 | - $l10n, |
|
| 52 | - $globalStoragesService, |
|
| 53 | - $logger, |
|
| 54 | - $userSession, |
|
| 55 | - $groupManager, |
|
| 56 | - $config |
|
| 57 | - ); |
|
| 58 | - } |
|
| 26 | + /** |
|
| 27 | + * Creates a new global storages controller. |
|
| 28 | + * |
|
| 29 | + * @param string $AppName application name |
|
| 30 | + * @param IRequest $request request object |
|
| 31 | + * @param IL10N $l10n l10n service |
|
| 32 | + * @param GlobalStoragesService $globalStoragesService storage service |
|
| 33 | + * @param LoggerInterface $logger |
|
| 34 | + * @param IUserSession $userSession |
|
| 35 | + * @param IGroupManager $groupManager |
|
| 36 | + * @param IConfig $config |
|
| 37 | + */ |
|
| 38 | + public function __construct( |
|
| 39 | + $appName, |
|
| 40 | + IRequest $request, |
|
| 41 | + IL10N $l10n, |
|
| 42 | + GlobalStoragesService $globalStoragesService, |
|
| 43 | + LoggerInterface $logger, |
|
| 44 | + IUserSession $userSession, |
|
| 45 | + IGroupManager $groupManager, |
|
| 46 | + IConfig $config, |
|
| 47 | + ) { |
|
| 48 | + parent::__construct( |
|
| 49 | + $appName, |
|
| 50 | + $request, |
|
| 51 | + $l10n, |
|
| 52 | + $globalStoragesService, |
|
| 53 | + $logger, |
|
| 54 | + $userSession, |
|
| 55 | + $groupManager, |
|
| 56 | + $config |
|
| 57 | + ); |
|
| 58 | + } |
|
| 59 | 59 | |
| 60 | - /** |
|
| 61 | - * Create an external storage entry. |
|
| 62 | - * |
|
| 63 | - * @param string $mountPoint storage mount point |
|
| 64 | - * @param string $backend backend identifier |
|
| 65 | - * @param string $authMechanism authentication mechanism identifier |
|
| 66 | - * @param array $backendOptions backend-specific options |
|
| 67 | - * @param array $mountOptions mount-specific options |
|
| 68 | - * @param array $applicableUsers users for which to mount the storage |
|
| 69 | - * @param array $applicableGroups groups for which to mount the storage |
|
| 70 | - * @param int $priority priority |
|
| 71 | - * |
|
| 72 | - * @return DataResponse |
|
| 73 | - */ |
|
| 74 | - #[PasswordConfirmationRequired(strict: true)] |
|
| 75 | - public function create( |
|
| 76 | - $mountPoint, |
|
| 77 | - $backend, |
|
| 78 | - $authMechanism, |
|
| 79 | - $backendOptions, |
|
| 80 | - $mountOptions, |
|
| 81 | - $applicableUsers, |
|
| 82 | - $applicableGroups, |
|
| 83 | - $priority, |
|
| 84 | - ) { |
|
| 85 | - $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); |
|
| 86 | - if (!$canCreateNewLocalStorage && $backend === 'local') { |
|
| 87 | - return new DataResponse( |
|
| 88 | - [ |
|
| 89 | - 'message' => $this->l10n->t('Forbidden to manage local mounts') |
|
| 90 | - ], |
|
| 91 | - Http::STATUS_FORBIDDEN |
|
| 92 | - ); |
|
| 93 | - } |
|
| 60 | + /** |
|
| 61 | + * Create an external storage entry. |
|
| 62 | + * |
|
| 63 | + * @param string $mountPoint storage mount point |
|
| 64 | + * @param string $backend backend identifier |
|
| 65 | + * @param string $authMechanism authentication mechanism identifier |
|
| 66 | + * @param array $backendOptions backend-specific options |
|
| 67 | + * @param array $mountOptions mount-specific options |
|
| 68 | + * @param array $applicableUsers users for which to mount the storage |
|
| 69 | + * @param array $applicableGroups groups for which to mount the storage |
|
| 70 | + * @param int $priority priority |
|
| 71 | + * |
|
| 72 | + * @return DataResponse |
|
| 73 | + */ |
|
| 74 | + #[PasswordConfirmationRequired(strict: true)] |
|
| 75 | + public function create( |
|
| 76 | + $mountPoint, |
|
| 77 | + $backend, |
|
| 78 | + $authMechanism, |
|
| 79 | + $backendOptions, |
|
| 80 | + $mountOptions, |
|
| 81 | + $applicableUsers, |
|
| 82 | + $applicableGroups, |
|
| 83 | + $priority, |
|
| 84 | + ) { |
|
| 85 | + $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); |
|
| 86 | + if (!$canCreateNewLocalStorage && $backend === 'local') { |
|
| 87 | + return new DataResponse( |
|
| 88 | + [ |
|
| 89 | + 'message' => $this->l10n->t('Forbidden to manage local mounts') |
|
| 90 | + ], |
|
| 91 | + Http::STATUS_FORBIDDEN |
|
| 92 | + ); |
|
| 93 | + } |
|
| 94 | 94 | |
| 95 | - $newStorage = $this->createStorage( |
|
| 96 | - $mountPoint, |
|
| 97 | - $backend, |
|
| 98 | - $authMechanism, |
|
| 99 | - $backendOptions, |
|
| 100 | - $mountOptions, |
|
| 101 | - $applicableUsers, |
|
| 102 | - $applicableGroups, |
|
| 103 | - $priority |
|
| 104 | - ); |
|
| 105 | - if ($newStorage instanceof DataResponse) { |
|
| 106 | - return $newStorage; |
|
| 107 | - } |
|
| 95 | + $newStorage = $this->createStorage( |
|
| 96 | + $mountPoint, |
|
| 97 | + $backend, |
|
| 98 | + $authMechanism, |
|
| 99 | + $backendOptions, |
|
| 100 | + $mountOptions, |
|
| 101 | + $applicableUsers, |
|
| 102 | + $applicableGroups, |
|
| 103 | + $priority |
|
| 104 | + ); |
|
| 105 | + if ($newStorage instanceof DataResponse) { |
|
| 106 | + return $newStorage; |
|
| 107 | + } |
|
| 108 | 108 | |
| 109 | - $response = $this->validate($newStorage); |
|
| 110 | - if (!empty($response)) { |
|
| 111 | - return $response; |
|
| 112 | - } |
|
| 109 | + $response = $this->validate($newStorage); |
|
| 110 | + if (!empty($response)) { |
|
| 111 | + return $response; |
|
| 112 | + } |
|
| 113 | 113 | |
| 114 | - $newStorage = $this->service->addStorage($newStorage); |
|
| 114 | + $newStorage = $this->service->addStorage($newStorage); |
|
| 115 | 115 | |
| 116 | - $this->updateStorageStatus($newStorage); |
|
| 116 | + $this->updateStorageStatus($newStorage); |
|
| 117 | 117 | |
| 118 | - return new DataResponse( |
|
| 119 | - $newStorage->jsonSerialize(true), |
|
| 120 | - Http::STATUS_CREATED |
|
| 121 | - ); |
|
| 122 | - } |
|
| 118 | + return new DataResponse( |
|
| 119 | + $newStorage->jsonSerialize(true), |
|
| 120 | + Http::STATUS_CREATED |
|
| 121 | + ); |
|
| 122 | + } |
|
| 123 | 123 | |
| 124 | - /** |
|
| 125 | - * Update an external storage entry. |
|
| 126 | - * |
|
| 127 | - * @param int $id storage id |
|
| 128 | - * @param string $mountPoint storage mount point |
|
| 129 | - * @param string $backend backend identifier |
|
| 130 | - * @param string $authMechanism authentication mechanism identifier |
|
| 131 | - * @param array $backendOptions backend-specific options |
|
| 132 | - * @param array $mountOptions mount-specific options |
|
| 133 | - * @param array $applicableUsers users for which to mount the storage |
|
| 134 | - * @param array $applicableGroups groups for which to mount the storage |
|
| 135 | - * @param int $priority priority |
|
| 136 | - * |
|
| 137 | - * @return DataResponse |
|
| 138 | - */ |
|
| 139 | - #[PasswordConfirmationRequired(strict: true)] |
|
| 140 | - public function update( |
|
| 141 | - $id, |
|
| 142 | - $mountPoint, |
|
| 143 | - $backend, |
|
| 144 | - $authMechanism, |
|
| 145 | - $backendOptions, |
|
| 146 | - $mountOptions, |
|
| 147 | - $applicableUsers, |
|
| 148 | - $applicableGroups, |
|
| 149 | - $priority, |
|
| 150 | - ) { |
|
| 151 | - $storage = $this->createStorage( |
|
| 152 | - $mountPoint, |
|
| 153 | - $backend, |
|
| 154 | - $authMechanism, |
|
| 155 | - $backendOptions, |
|
| 156 | - $mountOptions, |
|
| 157 | - $applicableUsers, |
|
| 158 | - $applicableGroups, |
|
| 159 | - $priority |
|
| 160 | - ); |
|
| 161 | - if ($storage instanceof DataResponse) { |
|
| 162 | - return $storage; |
|
| 163 | - } |
|
| 164 | - $storage->setId($id); |
|
| 124 | + /** |
|
| 125 | + * Update an external storage entry. |
|
| 126 | + * |
|
| 127 | + * @param int $id storage id |
|
| 128 | + * @param string $mountPoint storage mount point |
|
| 129 | + * @param string $backend backend identifier |
|
| 130 | + * @param string $authMechanism authentication mechanism identifier |
|
| 131 | + * @param array $backendOptions backend-specific options |
|
| 132 | + * @param array $mountOptions mount-specific options |
|
| 133 | + * @param array $applicableUsers users for which to mount the storage |
|
| 134 | + * @param array $applicableGroups groups for which to mount the storage |
|
| 135 | + * @param int $priority priority |
|
| 136 | + * |
|
| 137 | + * @return DataResponse |
|
| 138 | + */ |
|
| 139 | + #[PasswordConfirmationRequired(strict: true)] |
|
| 140 | + public function update( |
|
| 141 | + $id, |
|
| 142 | + $mountPoint, |
|
| 143 | + $backend, |
|
| 144 | + $authMechanism, |
|
| 145 | + $backendOptions, |
|
| 146 | + $mountOptions, |
|
| 147 | + $applicableUsers, |
|
| 148 | + $applicableGroups, |
|
| 149 | + $priority, |
|
| 150 | + ) { |
|
| 151 | + $storage = $this->createStorage( |
|
| 152 | + $mountPoint, |
|
| 153 | + $backend, |
|
| 154 | + $authMechanism, |
|
| 155 | + $backendOptions, |
|
| 156 | + $mountOptions, |
|
| 157 | + $applicableUsers, |
|
| 158 | + $applicableGroups, |
|
| 159 | + $priority |
|
| 160 | + ); |
|
| 161 | + if ($storage instanceof DataResponse) { |
|
| 162 | + return $storage; |
|
| 163 | + } |
|
| 164 | + $storage->setId($id); |
|
| 165 | 165 | |
| 166 | - $response = $this->validate($storage); |
|
| 167 | - if (!empty($response)) { |
|
| 168 | - return $response; |
|
| 169 | - } |
|
| 166 | + $response = $this->validate($storage); |
|
| 167 | + if (!empty($response)) { |
|
| 168 | + return $response; |
|
| 169 | + } |
|
| 170 | 170 | |
| 171 | - try { |
|
| 172 | - $storage = $this->service->updateStorage($storage); |
|
| 173 | - } catch (NotFoundException $e) { |
|
| 174 | - return new DataResponse( |
|
| 175 | - [ |
|
| 176 | - 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 177 | - ], |
|
| 178 | - Http::STATUS_NOT_FOUND |
|
| 179 | - ); |
|
| 180 | - } |
|
| 171 | + try { |
|
| 172 | + $storage = $this->service->updateStorage($storage); |
|
| 173 | + } catch (NotFoundException $e) { |
|
| 174 | + return new DataResponse( |
|
| 175 | + [ |
|
| 176 | + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 177 | + ], |
|
| 178 | + Http::STATUS_NOT_FOUND |
|
| 179 | + ); |
|
| 180 | + } |
|
| 181 | 181 | |
| 182 | - $this->updateStorageStatus($storage); |
|
| 182 | + $this->updateStorageStatus($storage); |
|
| 183 | 183 | |
| 184 | - return new DataResponse( |
|
| 185 | - $storage->jsonSerialize(true), |
|
| 186 | - Http::STATUS_OK |
|
| 187 | - ); |
|
| 188 | - } |
|
| 184 | + return new DataResponse( |
|
| 185 | + $storage->jsonSerialize(true), |
|
| 186 | + Http::STATUS_OK |
|
| 187 | + ); |
|
| 188 | + } |
|
| 189 | 189 | } |
@@ -30,164 +30,164 @@ |
||
| 30 | 30 | * User global storages controller |
| 31 | 31 | */ |
| 32 | 32 | class UserGlobalStoragesController extends StoragesController { |
| 33 | - /** |
|
| 34 | - * Creates a new user global storages controller. |
|
| 35 | - * |
|
| 36 | - * @param string $AppName application name |
|
| 37 | - * @param IRequest $request request object |
|
| 38 | - * @param IL10N $l10n l10n service |
|
| 39 | - * @param UserGlobalStoragesService $userGlobalStoragesService storage service |
|
| 40 | - * @param LoggerInterface $logger |
|
| 41 | - * @param IUserSession $userSession |
|
| 42 | - * @param IGroupManager $groupManager |
|
| 43 | - */ |
|
| 44 | - public function __construct( |
|
| 45 | - $appName, |
|
| 46 | - IRequest $request, |
|
| 47 | - IL10N $l10n, |
|
| 48 | - UserGlobalStoragesService $userGlobalStoragesService, |
|
| 49 | - LoggerInterface $logger, |
|
| 50 | - IUserSession $userSession, |
|
| 51 | - IGroupManager $groupManager, |
|
| 52 | - IConfig $config, |
|
| 53 | - ) { |
|
| 54 | - parent::__construct( |
|
| 55 | - $appName, |
|
| 56 | - $request, |
|
| 57 | - $l10n, |
|
| 58 | - $userGlobalStoragesService, |
|
| 59 | - $logger, |
|
| 60 | - $userSession, |
|
| 61 | - $groupManager, |
|
| 62 | - $config |
|
| 63 | - ); |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - /** |
|
| 67 | - * Get all storage entries |
|
| 68 | - * |
|
| 69 | - * @return DataResponse |
|
| 70 | - */ |
|
| 71 | - #[NoAdminRequired] |
|
| 72 | - public function index() { |
|
| 73 | - /** @var UserGlobalStoragesService */ |
|
| 74 | - $service = $this->service; |
|
| 75 | - $storages = array_map(function ($storage) { |
|
| 76 | - // remove configuration data, this must be kept private |
|
| 77 | - $this->sanitizeStorage($storage); |
|
| 78 | - return $storage->jsonSerialize(true); |
|
| 79 | - }, $service->getUniqueStorages()); |
|
| 80 | - |
|
| 81 | - return new DataResponse( |
|
| 82 | - $storages, |
|
| 83 | - Http::STATUS_OK |
|
| 84 | - ); |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - protected function manipulateStorageConfig(StorageConfig $storage) { |
|
| 88 | - /** @var AuthMechanism */ |
|
| 89 | - $authMechanism = $storage->getAuthMechanism(); |
|
| 90 | - $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 91 | - /** @var Backend */ |
|
| 92 | - $backend = $storage->getBackend(); |
|
| 93 | - $backend->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 94 | - } |
|
| 95 | - |
|
| 96 | - /** |
|
| 97 | - * Get an external storage entry. |
|
| 98 | - * |
|
| 99 | - * @param int $id storage id |
|
| 100 | - * @return DataResponse |
|
| 101 | - */ |
|
| 102 | - #[NoAdminRequired] |
|
| 103 | - public function show($id) { |
|
| 104 | - try { |
|
| 105 | - $storage = $this->service->getStorage($id); |
|
| 106 | - |
|
| 107 | - $this->updateStorageStatus($storage); |
|
| 108 | - } catch (NotFoundException $e) { |
|
| 109 | - return new DataResponse( |
|
| 110 | - [ |
|
| 111 | - 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 112 | - ], |
|
| 113 | - Http::STATUS_NOT_FOUND |
|
| 114 | - ); |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - $this->sanitizeStorage($storage); |
|
| 118 | - |
|
| 119 | - $data = $storage->jsonSerialize(true); |
|
| 120 | - $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); |
|
| 121 | - $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; |
|
| 122 | - |
|
| 123 | - return new DataResponse( |
|
| 124 | - $data, |
|
| 125 | - Http::STATUS_OK |
|
| 126 | - ); |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - /** |
|
| 130 | - * Update an external storage entry. |
|
| 131 | - * Only allows setting user provided backend fields |
|
| 132 | - * |
|
| 133 | - * @param int $id storage id |
|
| 134 | - * @param array $backendOptions backend-specific options |
|
| 135 | - * |
|
| 136 | - * @return DataResponse |
|
| 137 | - */ |
|
| 138 | - #[NoAdminRequired] |
|
| 139 | - #[PasswordConfirmationRequired(strict: true)] |
|
| 140 | - public function update( |
|
| 141 | - $id, |
|
| 142 | - $backendOptions, |
|
| 143 | - ) { |
|
| 144 | - try { |
|
| 145 | - $storage = $this->service->getStorage($id); |
|
| 146 | - $authMechanism = $storage->getAuthMechanism(); |
|
| 147 | - if ($authMechanism instanceof IUserProvided || $authMechanism instanceof UserGlobalAuth) { |
|
| 148 | - $authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions); |
|
| 149 | - $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 150 | - } else { |
|
| 151 | - return new DataResponse( |
|
| 152 | - [ |
|
| 153 | - 'message' => $this->l10n->t('Storage with ID "%d" is not editable by non-admins', [$id]) |
|
| 154 | - ], |
|
| 155 | - Http::STATUS_FORBIDDEN |
|
| 156 | - ); |
|
| 157 | - } |
|
| 158 | - } catch (NotFoundException $e) { |
|
| 159 | - return new DataResponse( |
|
| 160 | - [ |
|
| 161 | - 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 162 | - ], |
|
| 163 | - Http::STATUS_NOT_FOUND |
|
| 164 | - ); |
|
| 165 | - } |
|
| 166 | - |
|
| 167 | - $this->updateStorageStatus($storage); |
|
| 168 | - $this->sanitizeStorage($storage); |
|
| 169 | - |
|
| 170 | - return new DataResponse( |
|
| 171 | - $storage->jsonSerialize(true), |
|
| 172 | - Http::STATUS_OK |
|
| 173 | - ); |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - /** |
|
| 177 | - * Remove sensitive data from a StorageConfig before returning it to the user |
|
| 178 | - * |
|
| 179 | - * @param StorageConfig $storage |
|
| 180 | - */ |
|
| 181 | - protected function sanitizeStorage(StorageConfig $storage) { |
|
| 182 | - $storage->setBackendOptions([]); |
|
| 183 | - $storage->setMountOptions([]); |
|
| 184 | - |
|
| 185 | - if ($storage->getAuthMechanism() instanceof IUserProvided) { |
|
| 186 | - try { |
|
| 187 | - $storage->getAuthMechanism()->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 188 | - } catch (InsufficientDataForMeaningfulAnswerException $e) { |
|
| 189 | - // not configured yet |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - } |
|
| 33 | + /** |
|
| 34 | + * Creates a new user global storages controller. |
|
| 35 | + * |
|
| 36 | + * @param string $AppName application name |
|
| 37 | + * @param IRequest $request request object |
|
| 38 | + * @param IL10N $l10n l10n service |
|
| 39 | + * @param UserGlobalStoragesService $userGlobalStoragesService storage service |
|
| 40 | + * @param LoggerInterface $logger |
|
| 41 | + * @param IUserSession $userSession |
|
| 42 | + * @param IGroupManager $groupManager |
|
| 43 | + */ |
|
| 44 | + public function __construct( |
|
| 45 | + $appName, |
|
| 46 | + IRequest $request, |
|
| 47 | + IL10N $l10n, |
|
| 48 | + UserGlobalStoragesService $userGlobalStoragesService, |
|
| 49 | + LoggerInterface $logger, |
|
| 50 | + IUserSession $userSession, |
|
| 51 | + IGroupManager $groupManager, |
|
| 52 | + IConfig $config, |
|
| 53 | + ) { |
|
| 54 | + parent::__construct( |
|
| 55 | + $appName, |
|
| 56 | + $request, |
|
| 57 | + $l10n, |
|
| 58 | + $userGlobalStoragesService, |
|
| 59 | + $logger, |
|
| 60 | + $userSession, |
|
| 61 | + $groupManager, |
|
| 62 | + $config |
|
| 63 | + ); |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + /** |
|
| 67 | + * Get all storage entries |
|
| 68 | + * |
|
| 69 | + * @return DataResponse |
|
| 70 | + */ |
|
| 71 | + #[NoAdminRequired] |
|
| 72 | + public function index() { |
|
| 73 | + /** @var UserGlobalStoragesService */ |
|
| 74 | + $service = $this->service; |
|
| 75 | + $storages = array_map(function ($storage) { |
|
| 76 | + // remove configuration data, this must be kept private |
|
| 77 | + $this->sanitizeStorage($storage); |
|
| 78 | + return $storage->jsonSerialize(true); |
|
| 79 | + }, $service->getUniqueStorages()); |
|
| 80 | + |
|
| 81 | + return new DataResponse( |
|
| 82 | + $storages, |
|
| 83 | + Http::STATUS_OK |
|
| 84 | + ); |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + protected function manipulateStorageConfig(StorageConfig $storage) { |
|
| 88 | + /** @var AuthMechanism */ |
|
| 89 | + $authMechanism = $storage->getAuthMechanism(); |
|
| 90 | + $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 91 | + /** @var Backend */ |
|
| 92 | + $backend = $storage->getBackend(); |
|
| 93 | + $backend->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 94 | + } |
|
| 95 | + |
|
| 96 | + /** |
|
| 97 | + * Get an external storage entry. |
|
| 98 | + * |
|
| 99 | + * @param int $id storage id |
|
| 100 | + * @return DataResponse |
|
| 101 | + */ |
|
| 102 | + #[NoAdminRequired] |
|
| 103 | + public function show($id) { |
|
| 104 | + try { |
|
| 105 | + $storage = $this->service->getStorage($id); |
|
| 106 | + |
|
| 107 | + $this->updateStorageStatus($storage); |
|
| 108 | + } catch (NotFoundException $e) { |
|
| 109 | + return new DataResponse( |
|
| 110 | + [ |
|
| 111 | + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 112 | + ], |
|
| 113 | + Http::STATUS_NOT_FOUND |
|
| 114 | + ); |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + $this->sanitizeStorage($storage); |
|
| 118 | + |
|
| 119 | + $data = $storage->jsonSerialize(true); |
|
| 120 | + $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); |
|
| 121 | + $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; |
|
| 122 | + |
|
| 123 | + return new DataResponse( |
|
| 124 | + $data, |
|
| 125 | + Http::STATUS_OK |
|
| 126 | + ); |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + /** |
|
| 130 | + * Update an external storage entry. |
|
| 131 | + * Only allows setting user provided backend fields |
|
| 132 | + * |
|
| 133 | + * @param int $id storage id |
|
| 134 | + * @param array $backendOptions backend-specific options |
|
| 135 | + * |
|
| 136 | + * @return DataResponse |
|
| 137 | + */ |
|
| 138 | + #[NoAdminRequired] |
|
| 139 | + #[PasswordConfirmationRequired(strict: true)] |
|
| 140 | + public function update( |
|
| 141 | + $id, |
|
| 142 | + $backendOptions, |
|
| 143 | + ) { |
|
| 144 | + try { |
|
| 145 | + $storage = $this->service->getStorage($id); |
|
| 146 | + $authMechanism = $storage->getAuthMechanism(); |
|
| 147 | + if ($authMechanism instanceof IUserProvided || $authMechanism instanceof UserGlobalAuth) { |
|
| 148 | + $authMechanism->saveBackendOptions($this->userSession->getUser(), $id, $backendOptions); |
|
| 149 | + $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 150 | + } else { |
|
| 151 | + return new DataResponse( |
|
| 152 | + [ |
|
| 153 | + 'message' => $this->l10n->t('Storage with ID "%d" is not editable by non-admins', [$id]) |
|
| 154 | + ], |
|
| 155 | + Http::STATUS_FORBIDDEN |
|
| 156 | + ); |
|
| 157 | + } |
|
| 158 | + } catch (NotFoundException $e) { |
|
| 159 | + return new DataResponse( |
|
| 160 | + [ |
|
| 161 | + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]) |
|
| 162 | + ], |
|
| 163 | + Http::STATUS_NOT_FOUND |
|
| 164 | + ); |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + $this->updateStorageStatus($storage); |
|
| 168 | + $this->sanitizeStorage($storage); |
|
| 169 | + |
|
| 170 | + return new DataResponse( |
|
| 171 | + $storage->jsonSerialize(true), |
|
| 172 | + Http::STATUS_OK |
|
| 173 | + ); |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + /** |
|
| 177 | + * Remove sensitive data from a StorageConfig before returning it to the user |
|
| 178 | + * |
|
| 179 | + * @param StorageConfig $storage |
|
| 180 | + */ |
|
| 181 | + protected function sanitizeStorage(StorageConfig $storage) { |
|
| 182 | + $storage->setBackendOptions([]); |
|
| 183 | + $storage->setMountOptions([]); |
|
| 184 | + |
|
| 185 | + if ($storage->getAuthMechanism() instanceof IUserProvided) { |
|
| 186 | + try { |
|
| 187 | + $storage->getAuthMechanism()->manipulateStorageConfig($storage, $this->userSession->getUser()); |
|
| 188 | + } catch (InsufficientDataForMeaningfulAnswerException $e) { |
|
| 189 | + // not configured yet |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + } |
|
| 193 | 193 | } |
@@ -30,288 +30,288 @@ |
||
| 30 | 30 | * Base class for storages controllers |
| 31 | 31 | */ |
| 32 | 32 | abstract class StoragesController extends Controller { |
| 33 | - /** |
|
| 34 | - * Creates a new storages controller. |
|
| 35 | - * |
|
| 36 | - * @param string $AppName application name |
|
| 37 | - * @param IRequest $request request object |
|
| 38 | - * @param IL10N $l10n l10n service |
|
| 39 | - * @param StoragesService $storagesService storage service |
|
| 40 | - * @param LoggerInterface $logger |
|
| 41 | - */ |
|
| 42 | - public function __construct( |
|
| 43 | - $appName, |
|
| 44 | - IRequest $request, |
|
| 45 | - protected IL10N $l10n, |
|
| 46 | - protected StoragesService $service, |
|
| 47 | - protected LoggerInterface $logger, |
|
| 48 | - protected IUserSession $userSession, |
|
| 49 | - protected IGroupManager $groupManager, |
|
| 50 | - protected IConfig $config, |
|
| 51 | - ) { |
|
| 52 | - parent::__construct($appName, $request); |
|
| 53 | - } |
|
| 33 | + /** |
|
| 34 | + * Creates a new storages controller. |
|
| 35 | + * |
|
| 36 | + * @param string $AppName application name |
|
| 37 | + * @param IRequest $request request object |
|
| 38 | + * @param IL10N $l10n l10n service |
|
| 39 | + * @param StoragesService $storagesService storage service |
|
| 40 | + * @param LoggerInterface $logger |
|
| 41 | + */ |
|
| 42 | + public function __construct( |
|
| 43 | + $appName, |
|
| 44 | + IRequest $request, |
|
| 45 | + protected IL10N $l10n, |
|
| 46 | + protected StoragesService $service, |
|
| 47 | + protected LoggerInterface $logger, |
|
| 48 | + protected IUserSession $userSession, |
|
| 49 | + protected IGroupManager $groupManager, |
|
| 50 | + protected IConfig $config, |
|
| 51 | + ) { |
|
| 52 | + parent::__construct($appName, $request); |
|
| 53 | + } |
|
| 54 | 54 | |
| 55 | - /** |
|
| 56 | - * Create a storage from its parameters |
|
| 57 | - * |
|
| 58 | - * @param string $mountPoint storage mount point |
|
| 59 | - * @param string $backend backend identifier |
|
| 60 | - * @param string $authMechanism authentication mechanism identifier |
|
| 61 | - * @param array $backendOptions backend-specific options |
|
| 62 | - * @param array|null $mountOptions mount-specific options |
|
| 63 | - * @param array|null $applicableUsers users for which to mount the storage |
|
| 64 | - * @param array|null $applicableGroups groups for which to mount the storage |
|
| 65 | - * @param int|null $priority priority |
|
| 66 | - * |
|
| 67 | - * @return StorageConfig|DataResponse |
|
| 68 | - */ |
|
| 69 | - protected function createStorage( |
|
| 70 | - $mountPoint, |
|
| 71 | - $backend, |
|
| 72 | - $authMechanism, |
|
| 73 | - $backendOptions, |
|
| 74 | - $mountOptions = null, |
|
| 75 | - $applicableUsers = null, |
|
| 76 | - $applicableGroups = null, |
|
| 77 | - $priority = null, |
|
| 78 | - ) { |
|
| 79 | - $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); |
|
| 80 | - if (!$canCreateNewLocalStorage && $backend === 'local') { |
|
| 81 | - return new DataResponse( |
|
| 82 | - [ |
|
| 83 | - 'message' => $this->l10n->t('Forbidden to manage local mounts') |
|
| 84 | - ], |
|
| 85 | - Http::STATUS_FORBIDDEN |
|
| 86 | - ); |
|
| 87 | - } |
|
| 55 | + /** |
|
| 56 | + * Create a storage from its parameters |
|
| 57 | + * |
|
| 58 | + * @param string $mountPoint storage mount point |
|
| 59 | + * @param string $backend backend identifier |
|
| 60 | + * @param string $authMechanism authentication mechanism identifier |
|
| 61 | + * @param array $backendOptions backend-specific options |
|
| 62 | + * @param array|null $mountOptions mount-specific options |
|
| 63 | + * @param array|null $applicableUsers users for which to mount the storage |
|
| 64 | + * @param array|null $applicableGroups groups for which to mount the storage |
|
| 65 | + * @param int|null $priority priority |
|
| 66 | + * |
|
| 67 | + * @return StorageConfig|DataResponse |
|
| 68 | + */ |
|
| 69 | + protected function createStorage( |
|
| 70 | + $mountPoint, |
|
| 71 | + $backend, |
|
| 72 | + $authMechanism, |
|
| 73 | + $backendOptions, |
|
| 74 | + $mountOptions = null, |
|
| 75 | + $applicableUsers = null, |
|
| 76 | + $applicableGroups = null, |
|
| 77 | + $priority = null, |
|
| 78 | + ) { |
|
| 79 | + $canCreateNewLocalStorage = $this->config->getSystemValue('files_external_allow_create_new_local', true); |
|
| 80 | + if (!$canCreateNewLocalStorage && $backend === 'local') { |
|
| 81 | + return new DataResponse( |
|
| 82 | + [ |
|
| 83 | + 'message' => $this->l10n->t('Forbidden to manage local mounts') |
|
| 84 | + ], |
|
| 85 | + Http::STATUS_FORBIDDEN |
|
| 86 | + ); |
|
| 87 | + } |
|
| 88 | 88 | |
| 89 | - try { |
|
| 90 | - return $this->service->createStorage( |
|
| 91 | - $mountPoint, |
|
| 92 | - $backend, |
|
| 93 | - $authMechanism, |
|
| 94 | - $backendOptions, |
|
| 95 | - $mountOptions, |
|
| 96 | - $applicableUsers, |
|
| 97 | - $applicableGroups, |
|
| 98 | - $priority |
|
| 99 | - ); |
|
| 100 | - } catch (\InvalidArgumentException $e) { |
|
| 101 | - $this->logger->error($e->getMessage(), ['exception' => $e]); |
|
| 102 | - return new DataResponse( |
|
| 103 | - [ |
|
| 104 | - 'message' => $this->l10n->t('Invalid backend or authentication mechanism class') |
|
| 105 | - ], |
|
| 106 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 107 | - ); |
|
| 108 | - } |
|
| 109 | - } |
|
| 89 | + try { |
|
| 90 | + return $this->service->createStorage( |
|
| 91 | + $mountPoint, |
|
| 92 | + $backend, |
|
| 93 | + $authMechanism, |
|
| 94 | + $backendOptions, |
|
| 95 | + $mountOptions, |
|
| 96 | + $applicableUsers, |
|
| 97 | + $applicableGroups, |
|
| 98 | + $priority |
|
| 99 | + ); |
|
| 100 | + } catch (\InvalidArgumentException $e) { |
|
| 101 | + $this->logger->error($e->getMessage(), ['exception' => $e]); |
|
| 102 | + return new DataResponse( |
|
| 103 | + [ |
|
| 104 | + 'message' => $this->l10n->t('Invalid backend or authentication mechanism class') |
|
| 105 | + ], |
|
| 106 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 107 | + ); |
|
| 108 | + } |
|
| 109 | + } |
|
| 110 | 110 | |
| 111 | - /** |
|
| 112 | - * Validate storage config |
|
| 113 | - * |
|
| 114 | - * @param StorageConfig $storage storage config |
|
| 115 | - *1 |
|
| 116 | - * @return DataResponse|null returns response in case of validation error |
|
| 117 | - */ |
|
| 118 | - protected function validate(StorageConfig $storage) { |
|
| 119 | - $mountPoint = $storage->getMountPoint(); |
|
| 120 | - if ($mountPoint === '') { |
|
| 121 | - return new DataResponse( |
|
| 122 | - [ |
|
| 123 | - 'message' => $this->l10n->t('Invalid mount point'), |
|
| 124 | - ], |
|
| 125 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 126 | - ); |
|
| 127 | - } |
|
| 111 | + /** |
|
| 112 | + * Validate storage config |
|
| 113 | + * |
|
| 114 | + * @param StorageConfig $storage storage config |
|
| 115 | + *1 |
|
| 116 | + * @return DataResponse|null returns response in case of validation error |
|
| 117 | + */ |
|
| 118 | + protected function validate(StorageConfig $storage) { |
|
| 119 | + $mountPoint = $storage->getMountPoint(); |
|
| 120 | + if ($mountPoint === '') { |
|
| 121 | + return new DataResponse( |
|
| 122 | + [ |
|
| 123 | + 'message' => $this->l10n->t('Invalid mount point'), |
|
| 124 | + ], |
|
| 125 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 126 | + ); |
|
| 127 | + } |
|
| 128 | 128 | |
| 129 | - if ($storage->getBackendOption('objectstore')) { |
|
| 130 | - // objectstore must not be sent from client side |
|
| 131 | - return new DataResponse( |
|
| 132 | - [ |
|
| 133 | - 'message' => $this->l10n->t('Objectstore forbidden'), |
|
| 134 | - ], |
|
| 135 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 136 | - ); |
|
| 137 | - } |
|
| 129 | + if ($storage->getBackendOption('objectstore')) { |
|
| 130 | + // objectstore must not be sent from client side |
|
| 131 | + return new DataResponse( |
|
| 132 | + [ |
|
| 133 | + 'message' => $this->l10n->t('Objectstore forbidden'), |
|
| 134 | + ], |
|
| 135 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 136 | + ); |
|
| 137 | + } |
|
| 138 | 138 | |
| 139 | - /** @var Backend */ |
|
| 140 | - $backend = $storage->getBackend(); |
|
| 141 | - /** @var AuthMechanism */ |
|
| 142 | - $authMechanism = $storage->getAuthMechanism(); |
|
| 143 | - if ($backend->checkDependencies()) { |
|
| 144 | - // invalid backend |
|
| 145 | - return new DataResponse( |
|
| 146 | - [ |
|
| 147 | - 'message' => $this->l10n->t('Invalid storage backend "%s"', [ |
|
| 148 | - $backend->getIdentifier(), |
|
| 149 | - ]), |
|
| 150 | - ], |
|
| 151 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 152 | - ); |
|
| 153 | - } |
|
| 139 | + /** @var Backend */ |
|
| 140 | + $backend = $storage->getBackend(); |
|
| 141 | + /** @var AuthMechanism */ |
|
| 142 | + $authMechanism = $storage->getAuthMechanism(); |
|
| 143 | + if ($backend->checkDependencies()) { |
|
| 144 | + // invalid backend |
|
| 145 | + return new DataResponse( |
|
| 146 | + [ |
|
| 147 | + 'message' => $this->l10n->t('Invalid storage backend "%s"', [ |
|
| 148 | + $backend->getIdentifier(), |
|
| 149 | + ]), |
|
| 150 | + ], |
|
| 151 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 152 | + ); |
|
| 153 | + } |
|
| 154 | 154 | |
| 155 | - if (!$backend->isVisibleFor($this->service->getVisibilityType())) { |
|
| 156 | - // not permitted to use backend |
|
| 157 | - return new DataResponse( |
|
| 158 | - [ |
|
| 159 | - 'message' => $this->l10n->t('Not permitted to use backend "%s"', [ |
|
| 160 | - $backend->getIdentifier(), |
|
| 161 | - ]), |
|
| 162 | - ], |
|
| 163 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 164 | - ); |
|
| 165 | - } |
|
| 166 | - if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) { |
|
| 167 | - // not permitted to use auth mechanism |
|
| 168 | - return new DataResponse( |
|
| 169 | - [ |
|
| 170 | - 'message' => $this->l10n->t('Not permitted to use authentication mechanism "%s"', [ |
|
| 171 | - $authMechanism->getIdentifier(), |
|
| 172 | - ]), |
|
| 173 | - ], |
|
| 174 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 175 | - ); |
|
| 176 | - } |
|
| 155 | + if (!$backend->isVisibleFor($this->service->getVisibilityType())) { |
|
| 156 | + // not permitted to use backend |
|
| 157 | + return new DataResponse( |
|
| 158 | + [ |
|
| 159 | + 'message' => $this->l10n->t('Not permitted to use backend "%s"', [ |
|
| 160 | + $backend->getIdentifier(), |
|
| 161 | + ]), |
|
| 162 | + ], |
|
| 163 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 164 | + ); |
|
| 165 | + } |
|
| 166 | + if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) { |
|
| 167 | + // not permitted to use auth mechanism |
|
| 168 | + return new DataResponse( |
|
| 169 | + [ |
|
| 170 | + 'message' => $this->l10n->t('Not permitted to use authentication mechanism "%s"', [ |
|
| 171 | + $authMechanism->getIdentifier(), |
|
| 172 | + ]), |
|
| 173 | + ], |
|
| 174 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 175 | + ); |
|
| 176 | + } |
|
| 177 | 177 | |
| 178 | - if (!$backend->validateStorage($storage)) { |
|
| 179 | - // unsatisfied parameters |
|
| 180 | - return new DataResponse( |
|
| 181 | - [ |
|
| 182 | - 'message' => $this->l10n->t('Unsatisfied backend parameters'), |
|
| 183 | - ], |
|
| 184 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 185 | - ); |
|
| 186 | - } |
|
| 187 | - if (!$authMechanism->validateStorage($storage)) { |
|
| 188 | - // unsatisfied parameters |
|
| 189 | - return new DataResponse( |
|
| 190 | - [ |
|
| 191 | - 'message' => $this->l10n->t('Unsatisfied authentication mechanism parameters'), |
|
| 192 | - ], |
|
| 193 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 194 | - ); |
|
| 195 | - } |
|
| 178 | + if (!$backend->validateStorage($storage)) { |
|
| 179 | + // unsatisfied parameters |
|
| 180 | + return new DataResponse( |
|
| 181 | + [ |
|
| 182 | + 'message' => $this->l10n->t('Unsatisfied backend parameters'), |
|
| 183 | + ], |
|
| 184 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 185 | + ); |
|
| 186 | + } |
|
| 187 | + if (!$authMechanism->validateStorage($storage)) { |
|
| 188 | + // unsatisfied parameters |
|
| 189 | + return new DataResponse( |
|
| 190 | + [ |
|
| 191 | + 'message' => $this->l10n->t('Unsatisfied authentication mechanism parameters'), |
|
| 192 | + ], |
|
| 193 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
| 194 | + ); |
|
| 195 | + } |
|
| 196 | 196 | |
| 197 | - return null; |
|
| 198 | - } |
|
| 197 | + return null; |
|
| 198 | + } |
|
| 199 | 199 | |
| 200 | - protected function manipulateStorageConfig(StorageConfig $storage) { |
|
| 201 | - /** @var AuthMechanism */ |
|
| 202 | - $authMechanism = $storage->getAuthMechanism(); |
|
| 203 | - $authMechanism->manipulateStorageConfig($storage); |
|
| 204 | - /** @var Backend */ |
|
| 205 | - $backend = $storage->getBackend(); |
|
| 206 | - $backend->manipulateStorageConfig($storage); |
|
| 207 | - } |
|
| 200 | + protected function manipulateStorageConfig(StorageConfig $storage) { |
|
| 201 | + /** @var AuthMechanism */ |
|
| 202 | + $authMechanism = $storage->getAuthMechanism(); |
|
| 203 | + $authMechanism->manipulateStorageConfig($storage); |
|
| 204 | + /** @var Backend */ |
|
| 205 | + $backend = $storage->getBackend(); |
|
| 206 | + $backend->manipulateStorageConfig($storage); |
|
| 207 | + } |
|
| 208 | 208 | |
| 209 | - /** |
|
| 210 | - * Check whether the given storage is available / valid. |
|
| 211 | - * |
|
| 212 | - * Note that this operation can be time consuming depending |
|
| 213 | - * on whether the remote storage is available or not. |
|
| 214 | - * |
|
| 215 | - * @param StorageConfig $storage storage configuration |
|
| 216 | - */ |
|
| 217 | - protected function updateStorageStatus(StorageConfig &$storage) { |
|
| 218 | - try { |
|
| 219 | - $this->manipulateStorageConfig($storage); |
|
| 209 | + /** |
|
| 210 | + * Check whether the given storage is available / valid. |
|
| 211 | + * |
|
| 212 | + * Note that this operation can be time consuming depending |
|
| 213 | + * on whether the remote storage is available or not. |
|
| 214 | + * |
|
| 215 | + * @param StorageConfig $storage storage configuration |
|
| 216 | + */ |
|
| 217 | + protected function updateStorageStatus(StorageConfig &$storage) { |
|
| 218 | + try { |
|
| 219 | + $this->manipulateStorageConfig($storage); |
|
| 220 | 220 | |
| 221 | - /** @var Backend */ |
|
| 222 | - $backend = $storage->getBackend(); |
|
| 223 | - // update status (can be time-consuming) |
|
| 224 | - $storage->setStatus( |
|
| 225 | - MountConfig::getBackendStatus( |
|
| 226 | - $backend->getStorageClass(), |
|
| 227 | - $storage->getBackendOptions(), |
|
| 228 | - ) |
|
| 229 | - ); |
|
| 230 | - } catch (InsufficientDataForMeaningfulAnswerException $e) { |
|
| 231 | - $status = $e->getCode() ?: StorageNotAvailableException::STATUS_INDETERMINATE; |
|
| 232 | - $storage->setStatus( |
|
| 233 | - (int)$status, |
|
| 234 | - $this->l10n->t('Insufficient data: %s', [$e->getMessage()]) |
|
| 235 | - ); |
|
| 236 | - } catch (StorageNotAvailableException $e) { |
|
| 237 | - $storage->setStatus( |
|
| 238 | - (int)$e->getCode(), |
|
| 239 | - $this->l10n->t('%s', [$e->getMessage()]) |
|
| 240 | - ); |
|
| 241 | - } catch (\Exception $e) { |
|
| 242 | - // FIXME: convert storage exceptions to StorageNotAvailableException |
|
| 243 | - $storage->setStatus( |
|
| 244 | - StorageNotAvailableException::STATUS_ERROR, |
|
| 245 | - get_class($e) . ': ' . $e->getMessage() |
|
| 246 | - ); |
|
| 247 | - } |
|
| 248 | - } |
|
| 221 | + /** @var Backend */ |
|
| 222 | + $backend = $storage->getBackend(); |
|
| 223 | + // update status (can be time-consuming) |
|
| 224 | + $storage->setStatus( |
|
| 225 | + MountConfig::getBackendStatus( |
|
| 226 | + $backend->getStorageClass(), |
|
| 227 | + $storage->getBackendOptions(), |
|
| 228 | + ) |
|
| 229 | + ); |
|
| 230 | + } catch (InsufficientDataForMeaningfulAnswerException $e) { |
|
| 231 | + $status = $e->getCode() ?: StorageNotAvailableException::STATUS_INDETERMINATE; |
|
| 232 | + $storage->setStatus( |
|
| 233 | + (int)$status, |
|
| 234 | + $this->l10n->t('Insufficient data: %s', [$e->getMessage()]) |
|
| 235 | + ); |
|
| 236 | + } catch (StorageNotAvailableException $e) { |
|
| 237 | + $storage->setStatus( |
|
| 238 | + (int)$e->getCode(), |
|
| 239 | + $this->l10n->t('%s', [$e->getMessage()]) |
|
| 240 | + ); |
|
| 241 | + } catch (\Exception $e) { |
|
| 242 | + // FIXME: convert storage exceptions to StorageNotAvailableException |
|
| 243 | + $storage->setStatus( |
|
| 244 | + StorageNotAvailableException::STATUS_ERROR, |
|
| 245 | + get_class($e) . ': ' . $e->getMessage() |
|
| 246 | + ); |
|
| 247 | + } |
|
| 248 | + } |
|
| 249 | 249 | |
| 250 | - /** |
|
| 251 | - * Get all storage entries |
|
| 252 | - * |
|
| 253 | - * @return DataResponse |
|
| 254 | - */ |
|
| 255 | - public function index() { |
|
| 256 | - $storages = array_map(static fn ($storage) => $storage->jsonSerialize(true), $this->service->getStorages()); |
|
| 250 | + /** |
|
| 251 | + * Get all storage entries |
|
| 252 | + * |
|
| 253 | + * @return DataResponse |
|
| 254 | + */ |
|
| 255 | + public function index() { |
|
| 256 | + $storages = array_map(static fn ($storage) => $storage->jsonSerialize(true), $this->service->getStorages()); |
|
| 257 | 257 | |
| 258 | - return new DataResponse( |
|
| 259 | - $storages, |
|
| 260 | - Http::STATUS_OK |
|
| 261 | - ); |
|
| 262 | - } |
|
| 258 | + return new DataResponse( |
|
| 259 | + $storages, |
|
| 260 | + Http::STATUS_OK |
|
| 261 | + ); |
|
| 262 | + } |
|
| 263 | 263 | |
| 264 | - /** |
|
| 265 | - * Get an external storage entry. |
|
| 266 | - * |
|
| 267 | - * @param int $id storage id |
|
| 268 | - * |
|
| 269 | - * @return DataResponse |
|
| 270 | - */ |
|
| 271 | - public function show(int $id) { |
|
| 272 | - try { |
|
| 273 | - $storage = $this->service->getStorage($id); |
|
| 264 | + /** |
|
| 265 | + * Get an external storage entry. |
|
| 266 | + * |
|
| 267 | + * @param int $id storage id |
|
| 268 | + * |
|
| 269 | + * @return DataResponse |
|
| 270 | + */ |
|
| 271 | + public function show(int $id) { |
|
| 272 | + try { |
|
| 273 | + $storage = $this->service->getStorage($id); |
|
| 274 | 274 | |
| 275 | - $this->updateStorageStatus($storage); |
|
| 276 | - } catch (NotFoundException $e) { |
|
| 277 | - return new DataResponse( |
|
| 278 | - [ |
|
| 279 | - 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), |
|
| 280 | - ], |
|
| 281 | - Http::STATUS_NOT_FOUND |
|
| 282 | - ); |
|
| 283 | - } |
|
| 275 | + $this->updateStorageStatus($storage); |
|
| 276 | + } catch (NotFoundException $e) { |
|
| 277 | + return new DataResponse( |
|
| 278 | + [ |
|
| 279 | + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), |
|
| 280 | + ], |
|
| 281 | + Http::STATUS_NOT_FOUND |
|
| 282 | + ); |
|
| 283 | + } |
|
| 284 | 284 | |
| 285 | - $data = $storage->jsonSerialize(true); |
|
| 286 | - $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); |
|
| 287 | - $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; |
|
| 285 | + $data = $storage->jsonSerialize(true); |
|
| 286 | + $isAdmin = $this->groupManager->isAdmin($this->userSession->getUser()->getUID()); |
|
| 287 | + $data['can_edit'] = $storage->getType() === StorageConfig::MOUNT_TYPE_PERSONAL || $isAdmin; |
|
| 288 | 288 | |
| 289 | - return new DataResponse( |
|
| 290 | - $data, |
|
| 291 | - Http::STATUS_OK |
|
| 292 | - ); |
|
| 293 | - } |
|
| 289 | + return new DataResponse( |
|
| 290 | + $data, |
|
| 291 | + Http::STATUS_OK |
|
| 292 | + ); |
|
| 293 | + } |
|
| 294 | 294 | |
| 295 | - /** |
|
| 296 | - * Deletes the storage with the given id. |
|
| 297 | - * |
|
| 298 | - * @param int $id storage id |
|
| 299 | - * |
|
| 300 | - * @return DataResponse |
|
| 301 | - */ |
|
| 302 | - #[PasswordConfirmationRequired(strict: true)] |
|
| 303 | - public function destroy(int $id) { |
|
| 304 | - try { |
|
| 305 | - $this->service->removeStorage($id); |
|
| 306 | - } catch (NotFoundException $e) { |
|
| 307 | - return new DataResponse( |
|
| 308 | - [ |
|
| 309 | - 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), |
|
| 310 | - ], |
|
| 311 | - Http::STATUS_NOT_FOUND |
|
| 312 | - ); |
|
| 313 | - } |
|
| 295 | + /** |
|
| 296 | + * Deletes the storage with the given id. |
|
| 297 | + * |
|
| 298 | + * @param int $id storage id |
|
| 299 | + * |
|
| 300 | + * @return DataResponse |
|
| 301 | + */ |
|
| 302 | + #[PasswordConfirmationRequired(strict: true)] |
|
| 303 | + public function destroy(int $id) { |
|
| 304 | + try { |
|
| 305 | + $this->service->removeStorage($id); |
|
| 306 | + } catch (NotFoundException $e) { |
|
| 307 | + return new DataResponse( |
|
| 308 | + [ |
|
| 309 | + 'message' => $this->l10n->t('Storage with ID "%d" not found', [$id]), |
|
| 310 | + ], |
|
| 311 | + Http::STATUS_NOT_FOUND |
|
| 312 | + ); |
|
| 313 | + } |
|
| 314 | 314 | |
| 315 | - return new DataResponse([], Http::STATUS_NO_CONTENT); |
|
| 316 | - } |
|
| 315 | + return new DataResponse([], Http::STATUS_NO_CONTENT); |
|
| 316 | + } |
|
| 317 | 317 | } |