diff options
author | Maxence Lange <maxence@artificial-owl.com> | 2022-04-04 15:11:47 +0300 |
---|---|---|
committer | Maxence Lange <maxence@artificial-owl.com> | 2022-04-06 18:56:45 +0300 |
commit | d6544d41e3e2ad5d3157003c2c8f662b73c43b00 (patch) | |
tree | 2f83f9f9df86cf6ebbcb2fe51d3920015d0319fe /lib | |
parent | 87725112afc2bfd679ac46a687446e080d443e20 (diff) |
bypass/limit permissions
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Command/CirclesConfig.php | 8 | ||||
-rw-r--r-- | lib/Controller/LocalController.php | 20 | ||||
-rw-r--r-- | lib/Exceptions/InsufficientPermissionException.php | 35 | ||||
-rw-r--r-- | lib/FederatedItems/CircleConfig.php | 16 | ||||
-rw-r--r-- | lib/Model/Member.php | 6 | ||||
-rw-r--r-- | lib/Service/CircleService.php | 8 | ||||
-rw-r--r-- | lib/Service/ConfigService.php | 7 | ||||
-rw-r--r-- | lib/Service/PermissionService.php | 184 |
8 files changed, 273 insertions, 11 deletions
diff --git a/lib/Command/CirclesConfig.php b/lib/Command/CirclesConfig.php index 5a823db3..68ecfa42 100644 --- a/lib/Command/CirclesConfig.php +++ b/lib/Command/CirclesConfig.php @@ -172,6 +172,14 @@ class CirclesConfig extends Base { if (strtolower($input->getOption('output')) === 'json') { $output->writeln(json_encode($outcome, JSON_PRETTY_PRINT)); + } elseif (strtolower($input->getOption('output')) !== 'none') { + $circle = $this->circleService->getCircle($circleId); + $output->writeln( + json_encode( + Circle::getCircleFlags($circle, Circle::FLAGS_LONG), + JSON_PRETTY_PRINT + ) + ); } return 0; diff --git a/lib/Controller/LocalController.php b/lib/Controller/LocalController.php index efa78363..fa7a454e 100644 --- a/lib/Controller/LocalController.php +++ b/lib/Controller/LocalController.php @@ -31,8 +31,6 @@ declare(strict_types=1); namespace OCA\Circles\Controller; -use OCA\Circles\Tools\Traits\TDeserialize; -use OCA\Circles\Tools\Traits\TNCLogger; use Exception; use OCA\Circles\Exceptions\FederatedUserException; use OCA\Circles\Exceptions\FederatedUserNotFoundException; @@ -49,7 +47,10 @@ use OCA\Circles\Service\ConfigService; use OCA\Circles\Service\FederatedUserService; use OCA\Circles\Service\MemberService; use OCA\Circles\Service\MembershipService; +use OCA\Circles\Service\PermissionService; use OCA\Circles\Service\SearchService; +use OCA\Circles\Tools\Traits\TDeserialize; +use OCA\Circles\Tools\Traits\TNCLogger; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCSController; @@ -81,6 +82,9 @@ class LocalController extends OcsController { /** @var MembershipService */ private $membershipService; + /** @var PermissionService */ + private $permissionService; + /** @var SearchService */ private $searchService; @@ -109,6 +113,7 @@ class LocalController extends OcsController { CircleService $circleService, MemberService $memberService, MembershipService $membershipService, + PermissionService $permissionService, SearchService $searchService, ConfigService $configService ) { @@ -119,6 +124,7 @@ class LocalController extends OcsController { $this->circleService = $circleService; $this->memberService = $memberService; $this->membershipService = $membershipService; + $this->permissionService = $permissionService; $this->searchService = $searchService; $this->configService = $configService; @@ -139,6 +145,7 @@ class LocalController extends OcsController { public function create(string $name, bool $personal = false, bool $local = false): DataResponse { try { $this->setCurrentFederatedUser(); + $this->permissionService->confirmCircleCreation(); $circle = $this->circleService->create($name, null, $personal, $local); @@ -572,14 +579,15 @@ class LocalController extends OcsController { /** + * @return void + * @throws FederatedUserException * @throws FederatedUserNotFoundException + * @throws FrontendException * @throws InvalidIdException - * @throws FederatedUserException - * @throws SingleCircleNotFoundException * @throws RequestBuilderException - * @throws FrontendException + * @throws SingleCircleNotFoundException */ - private function setCurrentFederatedUser() { + private function setCurrentFederatedUser(): void { if (!$this->configService->getAppValueBool(ConfigService::FRONTEND_ENABLED)) { throw new FrontendException('frontend disabled'); } diff --git a/lib/Exceptions/InsufficientPermissionException.php b/lib/Exceptions/InsufficientPermissionException.php new file mode 100644 index 00000000..b3c07537 --- /dev/null +++ b/lib/Exceptions/InsufficientPermissionException.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + + +/** + * Circles - Bring cloud-users closer together. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2021 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace OCA\Circles\Exceptions; + +class InsufficientPermissionException extends FederatedItemForbiddenException { +} diff --git a/lib/FederatedItems/CircleConfig.php b/lib/FederatedItems/CircleConfig.php index 522c0847..29339845 100644 --- a/lib/FederatedItems/CircleConfig.php +++ b/lib/FederatedItems/CircleConfig.php @@ -34,12 +34,14 @@ namespace OCA\Circles\FederatedItems; use OCA\Circles\Db\CircleRequest; use OCA\Circles\Exceptions\FederatedItemBadRequestException; use OCA\Circles\Exceptions\FederatedItemException; +use OCA\Circles\Exceptions\RequestBuilderException; use OCA\Circles\IFederatedItem; use OCA\Circles\IFederatedItemAsyncProcess; use OCA\Circles\Model\Circle; use OCA\Circles\Model\Federated\FederatedEvent; use OCA\Circles\Model\Helpers\MemberHelper; use OCA\Circles\Service\ConfigService; +use OCA\Circles\Service\PermissionService; use OCA\Circles\Tools\Traits\TDeserialize; /** @@ -56,6 +58,9 @@ class CircleConfig implements /** @var CircleRequest */ private $circleRequest; + /** @var PermissionService */ + private $permissionService; + /** @var ConfigService */ private $configService; @@ -64,10 +69,16 @@ class CircleConfig implements * CircleConfig constructor. * * @param CircleRequest $circleRequest + * @param PermissionService $permissionService * @param ConfigService $configService */ - public function __construct(CircleRequest $circleRequest, ConfigService $configService) { + public function __construct( + CircleRequest $circleRequest, + PermissionService $permissionService, + ConfigService $configService + ) { $this->circleRequest = $circleRequest; + $this->permissionService = $permissionService; $this->configService = $configService; } @@ -76,6 +87,7 @@ class CircleConfig implements * @param FederatedEvent $event * * @throws FederatedItemException + * @throws RequestBuilderException */ public function verify(FederatedEvent $event): void { $circle = $event->getCircle(); @@ -150,7 +162,7 @@ class CircleConfig implements $new = clone $circle; $new->setConfig($config); - $this->configService->confirmAllowedCircleTypes($new); + $this->permissionService->confirmAllowedCircleTypes($new, $circle); $event->getData()->sInt('config', $new->getConfig()); diff --git a/lib/Model/Member.php b/lib/Model/Member.php index 35ce1b21..4dd420b8 100644 --- a/lib/Model/Member.php +++ b/lib/Model/Member.php @@ -738,8 +738,10 @@ class Member extends ManagedModel implements * @throws RequestBuilderException */ public function getLink(string $singleId, bool $detailed = false): Membership { - $this->getManager()->getLink($this, $singleId, $detailed); - + if ($singleId !== '') { + $this->getManager()->getLink($this, $singleId, $detailed); + } + throw new MembershipNotFoundException(); } diff --git a/lib/Service/CircleService.php b/lib/Service/CircleService.php index bf33bc47..b5f2d967 100644 --- a/lib/Service/CircleService.php +++ b/lib/Service/CircleService.php @@ -101,6 +101,9 @@ class CircleService { /** @var MemberService */ private $memberService; + /** @var PermissionService */ + private $permissionService; + /** @var ConfigService */ private $configService; @@ -114,6 +117,7 @@ class CircleService { * @param FederatedUserService $federatedUserService * @param FederatedEventService $federatedEventService * @param MemberService $memberService + * @param PermissionService $permissionService * @param ConfigService $configService */ public function __construct( @@ -125,6 +129,7 @@ class CircleService { FederatedUserService $federatedUserService, FederatedEventService $federatedEventService, MemberService $memberService, + PermissionService $permissionService, ConfigService $configService ) { $this->l10n = $l10n; @@ -135,6 +140,7 @@ class CircleService { $this->federatedUserService = $federatedUserService; $this->federatedEventService = $federatedEventService; $this->memberService = $memberService; + $this->permissionService = $permissionService; $this->configService = $configService; $this->setup('app', Application::APP_ID); @@ -197,7 +203,7 @@ class CircleService { } $this->confirmName($circle); - $this->configService->confirmAllowedCircleTypes($circle); + $this->permissionService->confirmAllowedCircleTypes($circle); $member = new Member(); $member->importFromIFederatedUser($owner); diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 03315bd0..649c4c30 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -106,6 +106,10 @@ class ConfigService { public const ALLOWED_TYPES = 'allowed_types'; public const CIRCLE_TYPES_FORCE = 'circle_types_force'; public const CIRCLE_TYPES_BLOCK = 'circle_types_block'; + + public const BYPASS_CIRCLE_TYPES = 'bypass_circle_types'; + public const LIMIT_CIRCLE_CREATION = 'limit_circle_creation'; + public const MIGRATION_BYPASS = 'migration_bypass'; public const MIGRATION_22 = 'migration_22'; public const MIGRATION_22_1 = 'migration_22_1'; @@ -183,6 +187,9 @@ class ConfigService { self::CIRCLE_TYPES_FORCE => '0', self::CIRCLE_TYPES_BLOCK => '0', + self::BYPASS_CIRCLE_TYPES => '', + self::LIMIT_CIRCLE_CREATION => '', + self::MIGRATION_BYPASS => '0', self::MIGRATION_22 => '0', self::MIGRATION_22_1 => '0', diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php new file mode 100644 index 00000000..81c8fbb9 --- /dev/null +++ b/lib/Service/PermissionService.php @@ -0,0 +1,184 @@ +<?php + +declare(strict_types=1); + + +/** + * Circles - Bring cloud-users closer together. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace OCA\Circles\Service; + +use OCA\Circles\Exceptions\InitiatorNotFoundException; +use OCA\Circles\Exceptions\InsufficientPermissionException; +use OCA\Circles\Exceptions\MembershipNotFoundException; +use OCA\Circles\Exceptions\RequestBuilderException; +use OCA\Circles\Model\Circle; +use OCP\IL10N; + +class PermissionService { + + + /** @var IL10N */ + private $l10n; + + /** @var FederatedUserService */ + private $federatedUserService; + + /** @var ConfigService */ + private $configService; + + + /** + * @param IL10N $l10n + * @param FederatedUserService $federatedUserService + * @param ConfigService $configService + */ + public function __construct( + IL10N $l10n, + FederatedUserService $federatedUserService, + ConfigService $configService + ) { + $this->l10n = $l10n; + $this->federatedUserService = $federatedUserService; + $this->configService = $configService; + } + + + /** + * @throws RequestBuilderException + * @throws InitiatorNotFoundException + * @throws InsufficientPermissionException + */ + public function confirmCircleCreation(): void { + try { + $this->confirm(ConfigService::LIMIT_CIRCLE_CREATION); + } catch (InsufficientPermissionException $e) { + throw new InsufficientPermissionException( + $this->l10n->t('You have no permission to create a new circle') + ); + } + } + + + /** + * @param string $config + * + * @throws InsufficientPermissionException + * @throws RequestBuilderException + * @throws InitiatorNotFoundException + */ + private function confirm(string $config): void { + $singleId = $this->configService->getAppValue($config); + if ($singleId === '') { + return; + } + + $this->federatedUserService->mustHaveCurrentUser(); + $federatedUser = $this->federatedUserService->getCurrentUser(); + try { + $federatedUser->getLink($singleId); + } catch (MembershipNotFoundException $e) { + throw new InsufficientPermissionException(); + } + } + + + /** + * @param Circle $circle + * + * @return bool + * @throws RequestBuilderException + */ + private function canBypassCircleTypes(Circle $circle): bool { + try { + if (!$circle->hasInitiator()) { + throw new MembershipNotFoundException(); + } + + $circle->getInitiator()->getLink( + $this->configService->getAppValue(ConfigService::BYPASS_CIRCLE_TYPES) + ); + + return true; + } catch (MembershipNotFoundException $e) { + } + + return false; + } + + + /** + * Enforce or Block circle's config/type + * + * @param Circle $circle + * @param Circle|null $previous + * + * @throws RequestBuilderException + */ + public function confirmAllowedCircleTypes(Circle $circle, ?Circle $previous = null): void { + if ($this->canBypassCircleTypes($circle)) { + return; + } + + $config = $circle->getConfig(); + $force = $this->configService->getAppValueInt(ConfigService::CIRCLE_TYPES_FORCE); + $block = $this->configService->getAppValueInt(ConfigService::CIRCLE_TYPES_BLOCK); + + if (is_null($previous)) { + $config |= $force; + $config &= ~$block; + } else { + // if we have a previous entry, we compare old and new config. + foreach (array_merge($this->extractBitwise($force), $this->extractBitwise($block)) as $bit) { + if ($previous->isConfig($bit)) { + $config |= $bit; + } else { + $config &= ~$bit; + } + } + } + + $circle->setConfig($config); + } + + + /** + * @return int[] + */ + private function extractBitwise(int $bitwise): array { + $values = []; + $b = 1; + while ($b <= $bitwise) { + if (($bitwise & $b) !== 0) { + $values[] = $b; + } + + $b = $b << 1; + } + + return $values; + } +} |