diff --git a/appinfo/info.xml b/appinfo/info.xml
index c1bb3821b..2154069e0 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -57,6 +57,7 @@ Those groups of users (or "circles") can then be used by any other app for shari
OCA\Circles\Command\CirclesDestroy
OCA\Circles\Command\CirclesDetails
OCA\Circles\Command\CirclesConfig
+ OCA\Circles\Command\CirclesSetting
OCA\Circles\Command\CirclesSync
OCA\Circles\Command\CirclesCheck
OCA\Circles\Command\CirclesTest
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 13b0e5ebb..9b4b6d21e 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -58,7 +58,7 @@
['name' => 'Local#editName', 'url' => '/circles/{circleId}/name', 'verb' => 'PUT'],
['name' => 'Local#editDescription', 'url' => '/circles/{circleId}/description', 'verb' => 'PUT'],
- ['name' => 'Local#editSettings', 'url' => '/circles/{circleId}/settings', 'verb' => 'PUT'],
+ ['name' => 'Local#editSetting', 'url' => '/circles/{circleId}/setting', 'verb' => 'PUT'],
['name' => 'Local#editConfig', 'url' => '/circles/{circleId}/config', 'verb' => 'PUT'],
['name' => 'Local#link', 'url' => '/link/{circleId}/{singleId}', 'verb' => 'GET'],
@@ -95,7 +95,7 @@
['name' => 'Admin#circleLeave', 'url' => '/admin/{emulated}/circles/{circleId}/leave', 'verb' => 'PUT'],
['name' => 'Admin#editName', 'url' => '/admin/{emulated}/circles/{circleId}/name', 'verb' => 'PUT'],
['name' => 'Admin#editDescription', 'url' => '/admin/{emulated}/circles/{circleId}/description', 'verb' => 'PUT'],
- ['name' => 'Admin#editSettings', 'url' => '/admin/{emulated}/circles/{circleId}/settings', 'verb' => 'PUT'],
+ ['name' => 'Admin#editSetting', 'url' => '/admin/{emulated}/circles/{circleId}/setting', 'verb' => 'PUT'],
['name' => 'Admin#editConfig', 'url' => '/admin/{emulated}/circles/{circleId}/config', 'verb' => 'PUT'],
['name' => 'Admin#link', 'url' => '/admin/{emulated}/link/{circleId}/{singleId}', 'verb' => 'GET']
],
diff --git a/lib/Circles/FileSharingBroadcaster.php b/lib/Circles/FileSharingBroadcaster.php
index 89cc376a4..316ab8358 100644
--- a/lib/Circles/FileSharingBroadcaster.php
+++ b/lib/Circles/FileSharingBroadcaster.php
@@ -220,14 +220,14 @@ public function createShareToMember(SharingFrame $frame, DeprecatedMember $membe
$password = '';
$sendPasswordByMail = true;
- if ($this->configService->enforcePasswordProtection($circle)) {
- if ($circle->getSetting('password_single_enabled') === 'true') {
- $password = $circle->getPasswordSingle();
- $sendPasswordByMail = false;
- } else {
- $password = $this->miscService->token(15);
- }
- }
+// if ($this->configService->enforcePasswordProtection($circle)) {
+// if ($circle->getSetting('password_single_enabled') === 'true') {
+// $password = $circle->getPasswordSingle();
+// $sendPasswordByMail = false;
+// } else {
+// $password = $this->miscService->token(15);
+// }
+// }
$sharesToken =
$this->tokensRequest->generateTokenForMember($member, $share->getId(), $password);
@@ -473,9 +473,9 @@ protected function sendMail($fileName, $link, $author, $circleName, $email) {
* @throws Exception
*/
protected function sendPasswordByMail(IShare $share, $circleName, $email, $password) {
- if (!$this->configService->sendPasswordByMail() || $password === '') {
- return;
- }
+// if (!$this->configService->sendPasswordByMail() || $password === '') {
+// return;
+// }
$message = $this->mailer->createMessage();
@@ -594,9 +594,9 @@ public function sendMailExitingShares(
$data = [];
$password = '';
- if ($this->configService->enforcePasswordProtection($circle)) {
- $password = $this->miscService->token(15);
- }
+// if ($this->configService->enforcePasswordProtection($circle)) {
+// $password = $this->miscService->token(15);
+// }
foreach ($unknownShares as $share) {
try {
@@ -628,9 +628,9 @@ public function sendMailExitingShares(
* @throws Exception
*/
protected function sendPasswordExistingShares(DeprecatedMember $author, string $email, string $password) {
- if (!$this->configService->sendPasswordByMail() || $password === '') {
- return;
- }
+// if (!$this->configService->sendPasswordByMail() || $password === '') {
+// return;
+// }
$message = $this->mailer->createMessage();
diff --git a/lib/Command/CirclesSetting.php b/lib/Command/CirclesSetting.php
new file mode 100644
index 000000000..6044632cf
--- /dev/null
+++ b/lib/Command/CirclesSetting.php
@@ -0,0 +1,171 @@
+
+ * @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 .
+ *
+ */
+
+
+namespace OCA\Circles\Command;
+
+use OC\Core\Command\Base;
+use OCA\Circles\Exceptions\CircleNotFoundException;
+use OCA\Circles\Exceptions\FederatedEventException;
+use OCA\Circles\Exceptions\FederatedItemException;
+use OCA\Circles\Exceptions\FederatedUserException;
+use OCA\Circles\Exceptions\FederatedUserNotFoundException;
+use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
+use OCA\Circles\Exceptions\InitiatorNotFoundException;
+use OCA\Circles\Exceptions\InvalidIdException;
+use OCA\Circles\Exceptions\MemberNotFoundException;
+use OCA\Circles\Exceptions\OwnerNotFoundException;
+use OCA\Circles\Exceptions\RemoteInstanceException;
+use OCA\Circles\Exceptions\RemoteNotFoundException;
+use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
+use OCA\Circles\Exceptions\RequestBuilderException;
+use OCA\Circles\Exceptions\SingleCircleNotFoundException;
+use OCA\Circles\Exceptions\UnknownRemoteException;
+use OCA\Circles\Exceptions\UserTypeNotFoundException;
+use OCA\Circles\Model\Helpers\MemberHelper;
+use OCA\Circles\Model\Member;
+use OCA\Circles\Service\CircleService;
+use OCA\Circles\Service\FederatedUserService;
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class CirclesSetting extends Base {
+
+
+ /** @var FederatedUserService */
+ private $federatedUserService;
+
+ /** @var CircleService */
+ private $circleService;
+
+
+ /**
+ * @param FederatedUserService $federatedUserService
+ * @param CircleService $circlesService
+ */
+ public function __construct(FederatedUserService $federatedUserService, CircleService $circlesService) {
+ parent::__construct();
+
+ $this->federatedUserService = $federatedUserService;
+ $this->circleService = $circlesService;
+ }
+
+
+ /**
+ *
+ */
+ protected function configure() {
+ parent::configure();
+ $this->setName('circles:manage:setting')
+ ->setDescription('edit setting for a Circle')
+ ->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
+ ->addArgument('setting', InputArgument::OPTIONAL, 'setting to edit', '')
+ ->addArgument('value', InputArgument::OPTIONAL, 'value', '')
+ ->addOption('unset', '', InputOption::VALUE_NONE, 'unset the setting')
+ ->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
+ ->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
+ ->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
+ }
+
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ *
+ * @return int
+ * @throws FederatedEventException
+ * @throws FederatedItemException
+ * @throws InitiatorNotFoundException
+ * @throws RequestBuilderException
+ * @throws CircleNotFoundException
+ * @throws FederatedUserException
+ * @throws FederatedUserNotFoundException
+ * @throws InitiatorNotConfirmedException
+ * @throws InvalidIdException
+ * @throws MemberNotFoundException
+ * @throws OwnerNotFoundException
+ * @throws RemoteInstanceException
+ * @throws RemoteNotFoundException
+ * @throws RemoteResourceNotFoundException
+ * @throws SingleCircleNotFoundException
+ * @throws UnknownRemoteException
+ * @throws UserTypeNotFoundException
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $circleId = (string)$input->getArgument('circle_id');
+ $setting = (string)$input->getArgument('setting');
+ $value = (string)$input->getArgument('value');
+
+ try {
+ $this->federatedUserService->commandLineInitiator(
+ $input->getOption('initiator'),
+ Member::parseTypeString($input->getOption('initiator-type')),
+ $circleId,
+ false
+ );
+
+ if ($setting === '') {
+ $circle = $this->circleService->getCircle($circleId);
+ $initiatorHelper = new MemberHelper($circle->getInitiator());
+ $initiatorHelper->mustBeAdmin();
+ $output->writeln(json_encode($circle->getSettings(), JSON_PRETTY_PRINT));
+
+ return 0;
+ }
+
+ if (!$input->getOption('unset') && $value === '') {
+ throw new InvalidArgumentException('you need to specify a value');
+ }
+
+ $outcome = $this->circleService->updateSetting(
+ $circleId,
+ $setting,
+ ($input->getOption('unset')) ? null : $value,
+ );
+ } catch (FederatedItemException $e) {
+ if ($input->getOption('status-code')) {
+ throw new FederatedItemException(
+ ' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
+ );
+ }
+
+ throw $e;
+ }
+
+ if (strtolower($input->getOption('output')) === 'json') {
+ $output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
+ }
+
+ return 0;
+ }
+}
diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php
index 0919c0e62..ed0aefaa6 100644
--- a/lib/Controller/AdminController.php
+++ b/lib/Controller/AdminController.php
@@ -462,25 +462,29 @@ public function editDescription(string $emulated, string $circleId, string $valu
/**
* @param string $emulated
* @param string $circleId
- * @param array $value
+ * @param string $setting
+ * @param string|null $value
*
* @return DataResponse
* @throws OCSException
*/
- public function editSettings(string $emulated, string $circleId, array $value): DataResponse {
+ public function editSetting(string $emulated, string $circleId, string $setting, ?string $value = null): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
- $outcome = $this->circleService->updateSettings($circleId, $value);
+ $outcome = $this->circleService->updateSetting($circleId, $setting, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
- $this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'value' => $value]);
+ $this->e($e, ['circleId' => $circleId, 'setting' => $setting, 'value' => $value]);
throw new OCSException($e->getMessage(), $e->getCode());
}
}
+
+
+
/**
* @param string $emulated
* @param string $circleId
diff --git a/lib/Controller/LocalController.php b/lib/Controller/LocalController.php
index 35ceee4fb..4a6ec8225 100644
--- a/lib/Controller/LocalController.php
+++ b/lib/Controller/LocalController.php
@@ -413,6 +413,7 @@ public function memberRemove(string $circleId, string $memberId): DataResponse {
*
* @param int $limit
* @param int $offset
+ *
* @return DataResponse
* @throws OCSException
*/
@@ -505,20 +506,21 @@ public function editDescription(string $circleId, string $value): DataResponse {
* @NoAdminRequired
*
* @param string $circleId
- * @param array $value
+ * @param string $setting
+ * @param string|null $value
*
* @return DataResponse
* @throws OCSException
*/
- public function editSettings(string $circleId, array $value): DataResponse {
+ public function editSetting(string $circleId, string $setting, ?string $value = null): DataResponse {
try {
$this->setCurrentFederatedUser();
- $outcome = $this->circleService->updateSettings($circleId, $value);
+ $outcome = $this->circleService->updateSetting($circleId, $setting, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
- $this->e($e, ['circleId' => $circleId, 'value' => $value]);
+ $this->e($e, ['circleId' => $circleId, 'setting' => $setting, 'value' => $value]);
throw new OCSException($e->getMessage(), $e->getCode());
}
}
diff --git a/lib/Db/CircleRequest.php b/lib/Db/CircleRequest.php
index e3fef6ad3..b1e810d3b 100644
--- a/lib/Db/CircleRequest.php
+++ b/lib/Db/CircleRequest.php
@@ -150,6 +150,18 @@ public function updateConfig(Circle $circle) {
}
+ /**
+ * @param Circle $circle
+ */
+ public function updateSettings(Circle $circle) {
+ $qb = $this->getCircleUpdateSql();
+ $qb->set('settings', $qb->createNamedParameter(json_encode($circle->getSettings())));
+ $qb->limitToUniqueId($circle->getSingleId());
+
+ $qb->execute();
+ }
+
+
/**
* @param IFederatedUser|null $initiator
* @param CircleProbe $probe
diff --git a/lib/FederatedItems/CircleSetting.php b/lib/FederatedItems/CircleSetting.php
new file mode 100644
index 000000000..167a96a11
--- /dev/null
+++ b/lib/FederatedItems/CircleSetting.php
@@ -0,0 +1,118 @@
+
+ * @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 .
+ *
+ */
+
+
+namespace OCA\Circles\FederatedItems;
+
+use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Deserialize;
+use OCA\Circles\Db\CircleRequest;
+use OCA\Circles\IFederatedItem;
+use OCA\Circles\IFederatedItemAsyncProcess;
+use OCA\Circles\Model\Federated\FederatedEvent;
+use OCA\Circles\Model\Helpers\MemberHelper;
+
+class CircleSetting implements
+ IFederatedItem,
+ IFederatedItemAsyncProcess {
+ use TNC22Deserialize;
+
+
+ /** @var CircleRequest */
+ private $circleRequest;
+
+
+ /**
+ * CircleConfig constructor.
+ *
+ * @param CircleRequest $circleRequest
+ */
+ public function __construct(CircleRequest $circleRequest) {
+ $this->circleRequest = $circleRequest;
+ }
+
+
+ /**
+ * @param FederatedEvent $event
+ */
+ public function verify(FederatedEvent $event): void {
+ $circle = $event->getCircle();
+
+ $initiatorHelper = new MemberHelper($circle->getInitiator());
+ $initiatorHelper->mustBeAdmin();
+
+ $params = $event->getParams();
+ $setting = $params->g('setting');
+ $value = $params->gBool('unset') ? null : $params->g('value');
+
+ $settings = $circle->getSettings();
+
+ if (!is_null($value)) {
+ $settings[$setting] = $value;
+ } elseif (array_key_exists($setting, $settings)) {
+ unset($settings[$setting]);
+ }
+
+ $event->getData()->sArray('settings', $settings);
+
+ $new = clone $circle;
+ $new->setSettings($settings);
+
+ $event->setOutcome($this->serialize($new));
+ }
+
+
+ /**
+ * @param FederatedEvent $event
+ */
+ public function manage(FederatedEvent $event): void {
+ $circle = clone $event->getCircle();
+ $settings = $event->getData()->gArray('settings');
+
+ $circle->setSettings($settings);
+ // TODO list imported from FederatedItem/CircleConfig.php - need to check first there.
+
+ // TODO: Check locally that circle is not un-federated during the process
+ // TODO: if the circle is managed remotely, remove the circle locally
+ // TODO: if the circle is managed locally, remove non-local users
+
+ // TODO: Check locally that circle is not federated during the process
+ // TODO: sync if it is to broadcast to Trusted RemoteInstance
+
+ $this->circleRequest->updateSettings($circle);
+ }
+
+
+ /**
+ * @param FederatedEvent $event
+ * @param array $results
+ */
+ public function result(FederatedEvent $event, array $results): void {
+ }
+}
diff --git a/lib/GlobalScale/FileShare.php b/lib/GlobalScale/FileShare.php
index c3d7a8aec..2a2da0a37 100644
--- a/lib/GlobalScale/FileShare.php
+++ b/lib/GlobalScale/FileShare.php
@@ -198,14 +198,14 @@ private function sendShareToContact(GSEvent $event, DeprecatedCircle $circle, st
$newCircle = $this->circlesRequest->forceGetCircle($circle->getUniqueId(), true);
$password = '';
$sendPasswordByMail = true;
- if ($this->configService->enforcePasswordProtection($newCircle)) {
- if ($newCircle->getSetting('password_single_enabled') === 'true') {
- $password = $newCircle->getPasswordSingle();
- $sendPasswordByMail = false;
- } else {
- $password = $this->miscService->token(15);
- }
- }
+// if ($this->configService->enforcePasswordProtection($newCircle)) {
+// if ($newCircle->getSetting('password_single_enabled') === 'true') {
+// $password = $newCircle->getPasswordSingle();
+// $sendPasswordByMail = false;
+// } else {
+// $password = $this->miscService->token(15);
+// }
+// }
try {
$sharesToken =
@@ -310,9 +310,9 @@ protected function sendMail($fileName, $link, $author, $circleName, $email) {
* @throws Exception
*/
protected function sendPasswordByMail(IShare $share, $circleName, $email, $password) {
- if (!$this->configService->sendPasswordByMail() || $password === '') {
- return;
- }
+// if (!$this->configService->sendPasswordByMail() || $password === '') {
+// return;
+// }
$message = $this->mailer->createMessage();
diff --git a/lib/GlobalScale/MemberAdd.php b/lib/GlobalScale/MemberAdd.php
index 201cefec2..288f02f56 100644
--- a/lib/GlobalScale/MemberAdd.php
+++ b/lib/GlobalScale/MemberAdd.php
@@ -55,7 +55,7 @@
/**
* Class MemberAdd
- *
+ * @deprecated
* @package OCA\Circles\GlobalScale
*/
class MemberAdd extends AGlobalScaleEvent {
@@ -111,14 +111,14 @@ public function verify(GSEvent $event, bool $localCheck = false, bool $mustBeChe
$password = '';
$sendPasswordByMail = false;
- if ($this->configService->enforcePasswordProtection($circle)) {
- if ($circle->getSetting('password_single_enabled') === 'true') {
- $password = $circle->getPasswordSingle();
- } else {
- $sendPasswordByMail = true;
- $password = $this->miscService->token(15);
- }
- }
+// if ($this->configService->enforcePasswordProtection($circle)) {
+// if ($circle->getSetting('password_single_enabled') === 'true') {
+// $password = $circle->getPasswordSingle();
+// } else {
+// $sendPasswordByMail = true;
+// $password = $this->miscService->token(15);
+// }
+// }
$event->setData(
new SimpleDataStore(
diff --git a/lib/Listeners/Files/PreparingMemberSendMail.php b/lib/Listeners/Files/PreparingMemberSendMail.php
index fc42626cb..b473984fd 100644
--- a/lib/Listeners/Files/PreparingMemberSendMail.php
+++ b/lib/Listeners/Files/PreparingMemberSendMail.php
@@ -44,11 +44,11 @@
use OCA\Circles\Model\Member;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\ContactService;
+use OCA\Circles\Service\SendMailService;
use OCA\Circles\Service\ShareTokenService;
use OCA\Circles\Service\ShareWrapperService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
-use OCP\Security\IHasher;
/**
* Class PreparingMemberSendMail
@@ -60,15 +60,15 @@ class PreparingMemberSendMail implements IEventListener {
use TNC22Logger;
- /** @var IHasher */
- private $hasher;
-
/** @var ShareWrapperService */
private $shareWrapperService;
/** @var ShareTokenService */
private $shareTokenService;
+ /** @var SendMailService */
+ private $sendMailService;
+
/** @var ConfigService */
private $configService;
@@ -79,22 +79,22 @@ class PreparingMemberSendMail implements IEventListener {
/**
* AddingMember constructor.
*
- * @param IHasher $hasher
* @param ShareWrapperService $shareWrapperService
* @param ShareTokenService $shareTokenService
+ * @param SendMailService $sendMailService
* @param ContactService $contactService
* @param ConfigService $configService
*/
public function __construct(
- IHasher $hasher,
ShareWrapperService $shareWrapperService,
ShareTokenService $shareTokenService,
+ SendMailService $sendMailService,
ContactService $contactService,
ConfigService $configService
) {
- $this->hasher = $hasher;
$this->shareWrapperService = $shareWrapperService;
$this->shareTokenService = $shareTokenService;
+ $this->sendMailService = $sendMailService;
$this->contactService = $contactService;
$this->configService = $configService;
@@ -113,8 +113,12 @@ public function __construct(
* @throws UnknownRemoteException
*/
public function handle(Event $event): void {
- if (!$event instanceof PreparingCircleMemberEvent
- || !$this->configService->enforcePasswordOnSharedFile()) {
+ if (!$event instanceof PreparingCircleMemberEvent) {
+ return;
+ }
+
+ $circle = $event->getCircle();
+ if (!$this->configService->enforcePasswordOnSharedFile($circle)) {
return;
}
@@ -126,9 +130,8 @@ public function handle(Event $event): void {
}
$federatedEvent = $event->getFederatedEvent();
- $clearPasswords = $federatedEvent->getInternal()->gArray('clearPasswords');
- $hashedPasswords = $federatedEvent->getParams()->gArray('hashedPasswords');
+ $hashedPasswords = $clearPasswords = [];
foreach ($members as $member) {
if (($member->getUserType() !== Member::TYPE_MAIL
&& $member->getUserType() !== Member::TYPE_CONTACT)
@@ -137,9 +140,9 @@ public function handle(Event $event): void {
continue;
}
- $clearPassword = $this->token(14);
+ [$clearPassword, $hashedPassword] = $this->sendMailService->getPassword($circle);
$clearPasswords[$member->getSingleId()] = $clearPassword;
- $hashedPasswords[$member->getSingleId()] = $this->hasher->hash($clearPassword);
+ $hashedPasswords[$member->getSingleId()] = $hashedPassword;
}
$federatedEvent->getInternal()->aArray('clearPasswords', $clearPasswords);
diff --git a/lib/Listeners/Files/PreparingShareSendMail.php b/lib/Listeners/Files/PreparingShareSendMail.php
index 54def7b71..233349a60 100644
--- a/lib/Listeners/Files/PreparingShareSendMail.php
+++ b/lib/Listeners/Files/PreparingShareSendMail.php
@@ -44,6 +44,7 @@
use OCA\Circles\Model\Member;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\ContactService;
+use OCA\Circles\Service\SendMailService;
use OCA\Circles\Service\ShareTokenService;
use OCA\Circles\Service\ShareWrapperService;
use OCP\EventDispatcher\Event;
@@ -69,6 +70,9 @@ class PreparingShareSendMail implements IEventListener {
/** @var ShareTokenService */
private $shareTokenService;
+ /** @var SendMailService */
+ private $sendMailService;
+
/** @var ConfigService */
private $configService;
@@ -82,6 +86,7 @@ class PreparingShareSendMail implements IEventListener {
* @param IHasher $hasher
* @param ShareWrapperService $shareWrapperService
* @param ShareTokenService $shareTokenService
+ * @param SendMailService $sendMailService
* @param ContactService $contactService
* @param ConfigService $configService
*/
@@ -89,12 +94,14 @@ public function __construct(
IHasher $hasher,
ShareWrapperService $shareWrapperService,
ShareTokenService $shareTokenService,
+ SendMailService $sendMailService,
ContactService $contactService,
ConfigService $configService
) {
$this->hasher = $hasher;
$this->shareWrapperService = $shareWrapperService;
$this->shareTokenService = $shareTokenService;
+ $this->sendMailService = $sendMailService;
$this->contactService = $contactService;
$this->configService = $configService;
@@ -113,16 +120,18 @@ public function __construct(
* @throws UnknownRemoteException
*/
public function handle(Event $event): void {
- if (!$event instanceof PreparingFileShareEvent
- || !$this->configService->enforcePasswordOnSharedFile()) {
+ if (!$event instanceof PreparingFileShareEvent) {
return;
}
$circle = $event->getCircle();
+ if (!$this->configService->enforcePasswordOnSharedFile($circle)) {
+ return;
+ }
+
$federatedEvent = $event->getFederatedEvent();
- $clearPasswords = $federatedEvent->getInternal()->gArray('clearPasswords');
- $hashedPasswords = $federatedEvent->getParams()->gArray('hashedPasswords');
+ $hashedPasswords = $clearPasswords = [];
foreach ($circle->getInheritedMembers(false, true) as $member) {
if (($member->getUserType() !== Member::TYPE_MAIL
&& $member->getUserType() !== Member::TYPE_CONTACT)
@@ -132,9 +141,9 @@ public function handle(Event $event): void {
continue;
}
- $clearPassword = $this->token(14);
+ [$clearPassword, $hashedPassword] = $this->sendMailService->getPassword($circle);
$clearPasswords[$member->getSingleId()] = $clearPassword;
- $hashedPasswords[$member->getSingleId()] = $this->hasher->hash($clearPassword);
+ $hashedPasswords[$member->getSingleId()] = $hashedPassword;
}
$federatedEvent->getInternal()->aArray('clearPasswords', $clearPasswords);
diff --git a/lib/Model/Circle.php b/lib/Model/Circle.php
index 9a56e2693..24fc9ccc2 100644
--- a/lib/Model/Circle.php
+++ b/lib/Model/Circle.php
@@ -40,6 +40,8 @@
use JsonSerializable;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
+use OCA\Circles\Exceptions\MemberHelperException;
+use OCA\Circles\Exceptions\MemberLevelException;
use OCA\Circles\Exceptions\MembershipNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
@@ -48,6 +50,7 @@
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\IEntity;
+use OCA\Circles\Model\Helpers\MemberHelper;
/**
* Class Circle
@@ -805,6 +808,12 @@ public function import(array $data): IDeserializable {
/**
* @return array
+ * @throws FederatedItemException
+ * @throws RemoteInstanceException
+ * @throws RemoteNotFoundException
+ * @throws RemoteResourceNotFoundException
+ * @throws RequestBuilderException
+ * @throws UnknownRemoteException
*/
public function jsonSerialize(): array {
$arr = [
@@ -816,7 +825,6 @@ public function jsonSerialize(): array {
'population' => $this->getPopulation(),
'config' => $this->getConfig(),
'description' => $this->getDescription(),
- 'settings' => $this->getSettings(),
'url' => $this->getUrl(),
'creation' => $this->getCreation(),
'initiator' => ($this->hasInitiator()) ? $this->getInitiator() : null
@@ -838,6 +846,16 @@ public function jsonSerialize(): array {
$arr['memberships'] = $this->getMemberships();
}
+ // settings should only be available to admin
+ if ($this->hasInitiator()) {
+ $initiatorHelper = new MemberHelper($this->getInitiator());
+ try {
+ $initiatorHelper->mustBeAdmin();
+ $arr['settings'] = $this->getSettings();
+ } catch (MemberHelperException | MemberLevelException $e) {
+ }
+ }
+
return $arr;
}
diff --git a/lib/Service/CircleService.php b/lib/Service/CircleService.php
index 5cf639627..57ac800a8 100644
--- a/lib/Service/CircleService.php
+++ b/lib/Service/CircleService.php
@@ -57,7 +57,7 @@
use OCA\Circles\FederatedItems\CircleEdit;
use OCA\Circles\FederatedItems\CircleJoin;
use OCA\Circles\FederatedItems\CircleLeave;
-use OCA\Circles\FederatedItems\CircleSettings;
+use OCA\Circles\FederatedItems\CircleSetting;
use OCA\Circles\IEntity;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Circle;
@@ -69,12 +69,8 @@
use OCA\Circles\Model\Probes\MemberProbe;
use OCA\Circles\StatusCode;
use OCP\IL10N;
+use OCP\Security\IHasher;
-/**
- * Class CircleService
- *
- * @package OCA\Circles\Service
- */
class CircleService {
use TArrayTools;
use TStringTools;
@@ -84,6 +80,9 @@ class CircleService {
/** @var IL10N */
private $l10n;
+ /** @var IHasher */
+ private $hasher;
+
/** @var CircleRequest */
private $circleRequest;
@@ -107,8 +106,8 @@ class CircleService {
/**
- * CircleService constructor.
- *
+ * @param IL10N $l10n
+ * @param IHasher $hasher
* @param CircleRequest $circleRequest
* @param MemberRequest $memberRequest
* @param RemoteStreamService $remoteStreamService
@@ -119,6 +118,7 @@ class CircleService {
*/
public function __construct(
IL10N $l10n,
+ IHasher $hasher,
CircleRequest $circleRequest,
MemberRequest $memberRequest,
RemoteStreamService $remoteStreamService,
@@ -128,6 +128,7 @@ public function __construct(
ConfigService $configService
) {
$this->l10n = $l10n;
+ $this->hasher = $hasher;
$this->circleRequest = $circleRequest;
$this->memberRequest = $memberRequest;
$this->remoteStreamService = $remoteStreamService;
@@ -273,8 +274,11 @@ public function updateConfig(string $circleId, int $config): array {
/**
+ * if $value is null, setting is unset
+ *
* @param string $circleId
- * @param string $name
+ * @param string $setting
+ * @param string|null $value
*
* @return array
* @throws CircleNotFoundException
@@ -289,21 +293,34 @@ public function updateConfig(string $circleId, int $config): array {
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
- public function updateName(string $circleId, string $name): array {
+ public function updateSetting(string $circleId, string $setting, ?string $value): array {
$circle = $this->getCircle($circleId);
- $event = new FederatedEvent(CircleEdit::class);
+ if (strtolower($setting) === 'password_single' && !is_null($value)) {
+ $value = $this->hasher->hash($value);
+ }
+
+ $event = new FederatedEvent(CircleSetting::class);
$event->setCircle($circle);
- $event->setParams(new SimpleDataStore(['name' => $name]));
+ $event->setParams(
+ new SimpleDataStore(
+ [
+ 'setting' => $setting,
+ 'value' => $value,
+ 'unset' => is_null($value)
+ ]
+ )
+ );
$this->federatedEventService->newEvent($event);
return $event->getOutcome();
}
+
/**
* @param string $circleId
- * @param string $description
+ * @param string $name
*
* @return array
* @throws CircleNotFoundException
@@ -318,12 +335,12 @@ public function updateName(string $circleId, string $name): array {
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
- public function updateDescription(string $circleId, string $description): array {
+ public function updateName(string $circleId, string $name): array {
$circle = $this->getCircle($circleId);
$event = new FederatedEvent(CircleEdit::class);
$event->setCircle($circle);
- $event->setParams(new SimpleDataStore(['description' => $description]));
+ $event->setParams(new SimpleDataStore(['name' => $name]));
$this->federatedEventService->newEvent($event);
@@ -332,7 +349,7 @@ public function updateDescription(string $circleId, string $description): array
/**
* @param string $circleId
- * @param array $settings
+ * @param string $description
*
* @return array
* @throws CircleNotFoundException
@@ -347,12 +364,12 @@ public function updateDescription(string $circleId, string $description): array
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
- public function updateSettings(string $circleId, array $settings): array {
+ public function updateDescription(string $circleId, string $description): array {
$circle = $this->getCircle($circleId);
- $event = new FederatedEvent(CircleSettings::class);
+ $event = new FederatedEvent(CircleEdit::class);
$event->setCircle($circle);
- $event->setParams(new SimpleDataStore(['settings' => $settings]));
+ $event->setParams(new SimpleDataStore(['description' => $description]));
$this->federatedEventService->newEvent($event);
diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php
index 43a9aec25..0f86ad3cc 100644
--- a/lib/Service/ConfigService.php
+++ b/lib/Service/ConfigService.php
@@ -40,7 +40,6 @@
use OCA\Circles\Exceptions\GSStatusException;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Circle;
-use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\Member;
use OCP\IConfig;
use OCP\IURLGenerator;
@@ -328,56 +327,52 @@ public function contactsBackendType(): int {
/**
+ * true if:
+ * - password is generated randomly
+ *
+ * @param Circle $circle
+ *
* @return bool
- * @deprecated
- * should the password for a mail share be send to the recipient
*/
- public function sendPasswordByMail(): bool {
- return false;
+ public function sendPasswordByMail(Circle $circle): bool {
+ if (!$this->enforcePasswordOnSharedFile($circle)) {
+ return false;
+ }
+
+ return (!$this->getBool('password_single_enabled', $circle->getSettings(), false)
+ || $this->get('password_single', $circle->getSettings()) === '');
}
/**
+ * true if:
+ * - global setting of Nextcloud enforce password on shares.
+ * - setting of Circles' app enforce password on shares.
+ * - setting for specific Circle enforce password on shares.
+ *
+ * @param Circle $circle
+ *
* @return bool
*/
- public function enforcePasswordOnSharedFile(): bool {
- $localPolicy = $this->getAppValueInt(ConfigService::ENFORCE_PASSWORD);
- if ($localPolicy !== $this->getInt(ConfigService::ENFORCE_PASSWORD, self::$defaults)) {
- return ($localPolicy === 1);
+ public function enforcePasswordOnSharedFile(Circle $circle): bool {
+ if ($this->config->getAppValue(
+ 'core',
+ 'shareapi_enforce_links_password',
+ 'no'
+ ) === 'yes') {
+ return true;
}
- // TODO: reimplement a way to set password protection on a single Circle
-// if ($circle->getSetting('password_enforcement') === 'true') {
-// return true;
-// }
-
- $sendPasswordMail = $this->config->getAppValue(
- 'sharebymail',
- 'sendpasswordmail',
- 'yes'
- );
-
- $enforcePasswordProtection = $this->config->getAppValue(
- 'core',
- 'shareapi_enforce_links_password',
- 'no'
- );
-
- return ($sendPasswordMail === 'yes'
- && $enforcePasswordProtection === 'yes');
- }
+ if ($this->getAppValueInt(ConfigService::ENFORCE_PASSWORD) === 1) {
+ return true;
+ }
+ // Compat NC21
+ if ($this->getBool('password_enforcement', $circle->getSettings(), false)) {
+ return true;
+ }
- /**
- * @param DeprecatedCircle $circle
- *
- * @return bool
- * @deprecated
- * do we require a share by mail to be password protected
- *
- */
- public function enforcePasswordProtection(DeprecatedCircle $circle) {
- return false;
+ return $this->getBool('enforce_password', $circle->getSettings(), false);
}
diff --git a/lib/Service/SendMailService.php b/lib/Service/SendMailService.php
index 8506d8a99..7c3305b35 100644
--- a/lib/Service/SendMailService.php
+++ b/lib/Service/SendMailService.php
@@ -31,6 +31,8 @@
namespace OCA\Circles\Service;
+use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
use Exception;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
@@ -39,19 +41,20 @@
use OCP\IL10N;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
+use OCP\Security\IHasher;
use OCP\Util;
-/**
- * Class SendMailService
- *
- * @package OCA\Circles\Service
- */
class SendMailService {
+ use TArrayTools;
+ use TStringTools;
/** @var IL10N */
private $l10n;
+ /** @var IHasher */
+ private $hasher;
+
/** @var IMailer */
private $mailer;
@@ -72,11 +75,13 @@ class SendMailService {
*/
public function __construct(
IL10N $l10n,
+ IHasher $hasher,
IMailer $mailer,
Defaults $defaults,
ConfigService $configService
) {
$this->l10n = $l10n;
+ $this->hasher = $hasher;
$this->mailer = $mailer;
$this->defaults = $defaults;
$this->configService = $configService;
@@ -132,7 +137,7 @@ public function generateMail(
} catch (Exception $e) {
}
- $this->sendMailPassword($author, $circle->getDisplayName(), $mail, $password);
+ $this->sendMailPassword($circle, $author, $mail, $password);
}
}
@@ -213,18 +218,20 @@ private function sendMailExistingShares(
/**
+ * @param Circle $circle
* @param string $author
- * @param string $circleName
* @param string $email
* @param string $password
+ *
+ * @throws Exception
*/
private function sendMailPassword(
+ Circle $circle,
string $author,
- string $circleName,
string $email,
string $password
): void {
- if (!$this->configService->enforcePasswordOnSharedFile() || $password === '') {
+ if (!$this->configService->sendPasswordByMail($circle) || $password === '') {
return;
}
@@ -246,7 +253,7 @@ private function sendMailPassword(
'initiator' => $author,
// 'initiatorEmail' => Util::getDefaultEmailAddress(''),
'initiatorEmail' => '',
- 'shareWith' => $circleName
+ 'shareWith' => $circle->getDisplayName()
]
);
@@ -280,4 +287,23 @@ private function sendMailPassword(
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}
+
+
+ /**
+ * @param Circle $circle
+ *
+ * @return array
+ */
+ public function getPassword(Circle $circle): array {
+ $clearPassword = $hashedPassword = '';
+ if (!$this->configService->sendPasswordByMail($circle)) {
+ $hashedPassword = $this->get('password_single', $circle->getSettings());
+ }
+ if ($hashedPassword === '') {
+ $clearPassword = $this->token(14);
+ $hashedPassword = $this->hasher->hash($clearPassword);
+ }
+
+ return [$clearPassword, $hashedPassword];
+ }
}