diff --git a/css/navigation.css b/css/navigation.css index 71b540c9c..76bedccac 100644 --- a/css/navigation.css +++ b/css/navigation.css @@ -343,6 +343,9 @@ div.circle .owner, div.circle .type, div.circle .resume { #settings-name { width: 300px; } +#single-password { + width: 270px; +} #settings-desc { width: 300px; diff --git a/js/circles.app.actions.js b/js/circles.app.actions.js index 0a5f1ff0b..07ff6e456 100644 --- a/js/circles.app.actions.js +++ b/js/circles.app.actions.js @@ -43,7 +43,7 @@ var actions = { - changeMemberLevel: function (member, type, instance, level) { + changeMemberLevel: function(member, type, instance, level) { if (Number(level) === define.levelOwner) { actions.changeMemberOwner(member, type, instance); return; @@ -53,7 +53,7 @@ var actions = { }, - changeGroupLevel: function (group, level) { + changeGroupLevel: function(group, level) { if (level === 'remove_group') { api.unlinkGroup(curr.circle, group, resultGroups.unlinkGroupResult); } else { @@ -63,11 +63,11 @@ var actions = { }, - changeMemberOwner: function (member, type, instance) { + changeMemberOwner: function(member, type, instance) { OC.dialogs.confirm( t('circles', 'Are you sure you want to transfer your ownership?', [member]), t('circles', 'This action is irreversible'), - function (e) { + function(e) { if (e === true) { api.levelMember(curr.circle, member, type, instance, define.levelOwner, resultMembers.levelMemberResult); @@ -80,7 +80,7 @@ var actions = { }, - changeMemberStatus: function (member, type, instance, value) { + changeMemberStatus: function(member, type, instance, value) { if (value === 'remove_member' || value === 'dismiss_request') { api.removeMember(curr.circle, member, type, instance, resultMembers.removeMemberResult); } @@ -90,17 +90,17 @@ var actions = { }, - changeLinkStatus: function (link, value) { + changeLinkStatus: function(link, value) { api.linkStatus(link, value, resultLinks.linkStatusResult); }, - validateEmail: function (email) { + validateEmail: function(email) { var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); }, - selectCircle: function (circle_id) { + selectCircle: function(circle_id) { curr.searchUser = ''; elements.addMember.val(''); elements.linkGroup.val(''); @@ -111,7 +111,7 @@ var actions = { }, - unselectCircle: function (circle_id) { + unselectCircle: function(circle_id) { elements.mainUIMembersTable.emptyTable(); elements.navigation.children(".circle[circle-id='" + circle_id + "']").remove(); elements.emptyContent.show(400); @@ -122,17 +122,20 @@ var actions = { }, - saveSettings: function () { + saveSettings: function() { let data = { circle_name: elements.settingsName.val(), circle_desc: elements.settingsDesc.val(), allow_links: (elements.settingsLink.is(":checked")), password_enforcement: (elements.settingsPassword.is(":checked")), - members_limit: (elements.settingsLimit.val()), + password_single_enabled: (elements.settingsSinglePasswordEnabled.is(":checked")), + password_single: elements.settingsSinglePassword.val(), + members_limit: elements.settingsLimit.val(), allow_links_auto: (elements.settingsLinkAuto.is(":checked")), allow_links_files: (elements.settingsLinkFiles.is(":checked")) }; + console.log(JSON.stringify(data)); api.settingsCircle(curr.circle, data, settings.saveSettingsResult); }, @@ -140,7 +143,7 @@ var actions = { * * @param search */ - searchMembersRequest: function (search) { + searchMembersRequest: function(search) { if (curr.searchUser === search) { return; @@ -155,7 +158,7 @@ var actions = { * * @param search */ - searchGroupsRequest: function (search) { + searchGroupsRequest: function(search) { if (curr.searchGroup === search) { return; @@ -173,7 +176,7 @@ var actions = { }, - getStringTypeFromType: function (type) { + getStringTypeFromType: function(type) { switch (Number(type)) { case define.typePersonal: @@ -193,7 +196,7 @@ var actions = { /** * */ - onEventNewCircle: function () { + onEventNewCircle: function() { curr.circle = 0; curr.circleLevel = 0; @@ -206,7 +209,7 @@ var actions = { /** * */ - onEventNewCircleName: function () { + onEventNewCircleName: function() { this.onEventNewCircle(); nav.displayOptionsNewCircle((elements.newName.val() !== '')); }, @@ -215,14 +218,13 @@ var actions = { /** * */ - onEventNewCircleType: function () { + onEventNewCircleType: function() { this.onEventNewCircle(); elements.newTypeDefinition.children('div').fadeOut(300); var selectedType = elements.newType.children('option:selected').val(); if (selectedType === '') { elements.newType.addClass('select_none'); - } - else { + } else { elements.newType.removeClass('select_none'); $('#circles_new_type_' + selectedType).fadeIn( 300); diff --git a/js/circles.app.elements.js b/js/circles.app.elements.js index a3484be79..aca048710 100644 --- a/js/circles.app.elements.js +++ b/js/circles.app.elements.js @@ -71,6 +71,9 @@ var elements = { settingsName: null, settingsDesc: null, settingsPassword: null, + settingsSinglePasswordEnabled: null, + settingsSinglePassword: null, + settingsEntrySinglePassword: null, settingsLimit: null, settingsEntryLimit: null, settingsLink: null, @@ -135,6 +138,9 @@ var elements = { elements.settingsDesc = $('#settings-desc'); elements.settingsEntryLimit = $('#settings-entry-limit'); elements.settingsPassword = $('#settings-password'); + elements.settingsSinglePassword = $('#single-password'); + elements.settingsSinglePasswordEnabled = $('#single-password-enabled'); + elements.settingsEntrySinglePassword = $('#settings-password-single'); elements.settingsLimit = $('#settings-limit'); // elements.settingsLimit.prop('disabled', !OC.isUserAdmin()); diff --git a/js/circles.app.settings.js b/js/circles.app.settings.js index 4de11d982..97a429a03 100644 --- a/js/circles.app.settings.js +++ b/js/circles.app.settings.js @@ -34,7 +34,7 @@ var settings = { - displaySettings: function (display) { + displaySettings: function(display) { if (display) { settings.initUISettings(); elements.circleDesc.hide(define.animationSpeed); @@ -46,10 +46,26 @@ var settings = { } }, - initUISettings: function () { + initUISettings: function() { elements.settingsName.val(curr.circleName); elements.settingsDesc.val(curr.circleDesc); elements.settingsLimit.val(curr.circleLimit); + + +// single-password-enabled + elements.settingsPassword.prop('checked', + (curr.circleSettings['password_enforcement'] === 'true')); + elements.settingsPassword.on('change', function() { + settings.interactUISettings(); + }); + + elements.settingsSinglePassword.val(''); + elements.settingsSinglePasswordEnabled.prop('checked', + (curr.circleSettings['password_single_enabled'] === 'true')); + elements.settingsSinglePasswordEnabled.on('change', function() { + settings.interactUISettings(); + }); + if (OC.isUserAdmin()) { elements.settingsEntryLimit.show(); } else { @@ -62,7 +78,7 @@ var settings = { elements.settingsLinkFiles.prop('checked', (curr.circleSettings['allow_links_files'] === 'true')); - elements.settingsLink.on('change', function () { + elements.settingsLink.on('change', function() { settings.interactUISettings(); }); @@ -70,7 +86,22 @@ var settings = { }, - interactUISettings: function () { + interactUISettings: function() { + if (elements.settingsPassword.is(":checked")) { + settings.enableSetting(elements.settingsEntrySinglePassword, elements.settingsSinglePassword, + true); + settings.enableSetting(null, elements.settingsSinglePasswordEnabled, true); + if (elements.settingsSinglePasswordEnabled.is(":checked")) { + settings.enableSetting(null, elements.settingsSinglePassword, true); + } else { + settings.enableSetting(null, elements.settingsSinglePassword, + false); + } + } else { + settings.enableSetting(null, elements.settingsSinglePasswordEnabled, false); + settings.enableSetting(elements.settingsEntrySinglePassword, elements.settingsSinglePassword, + false); + } if (curr.allowed_federated_circles !== '1' || curr.circleDetails.type === define.typePersonal) { @@ -88,12 +119,14 @@ var settings = { (elements.settingsLink.is(":checked"))); }, - enableSetting: function (entry, input, enable) { - entry.stop().fadeTo(curr.animationSpeed, (enable) ? 1 : 0.3); + enableSetting: function(entry, input, enable) { + if (entry !== null) { + entry.stop().fadeTo(curr.animationSpeed, (enable) ? 1 : 0.3); + } input.prop('disabled', !enable); }, - saveSettingsResult: function (result) { + saveSettingsResult: function(result) { if (result.status < 1) { OCA.notification.onFail( t('circles', 'Issue while saving settings') + ': ' + diff --git a/lib/Circles/FileSharingBroadcaster.php b/lib/Circles/FileSharingBroadcaster.php index fa2a48fe5..d115200f3 100644 --- a/lib/Circles/FileSharingBroadcaster.php +++ b/lib/Circles/FileSharingBroadcaster.php @@ -216,8 +216,14 @@ public function createShareToMember(SharingFrame $frame, Member $member) { } $password = ''; + $sendPasswordByMail = true; if ($this->configService->enforcePasswordProtection($circle)) { - $password = $this->miscService->token(15); + if ($circle->getSetting('password_single_enabled') === 'true') { + $password = $circle->getPasswordSingle(); + $sendPasswordByMail = false; + } else { + $password = $this->miscService->token(15); + } } $sharesToken = @@ -227,6 +233,10 @@ public function createShareToMember(SharingFrame $frame, Member $member) { $mails = $this->getMailsFromContact($member->getUserId()); } + if (!$sendPasswordByMail) { + $password = ''; + } + foreach ($mails as $mail) { $this->sharedByMail($circle, $share, $mail, $sharesToken, $password); } diff --git a/lib/Db/CirclesRequest.php b/lib/Db/CirclesRequest.php index 751df7a26..2c50e6776 100644 --- a/lib/Db/CirclesRequest.php +++ b/lib/Db/CirclesRequest.php @@ -33,6 +33,7 @@ use OCA\Circles\Exceptions\CircleAlreadyExistsException; use OCA\Circles\Exceptions\CircleDoesNotExistException; use OCA\Circles\Exceptions\ConfigNoCircleAvailableException; +use OCA\Circles\Exceptions\GSStatusException; use OCA\Circles\Model\Circle; use OCA\Circles\Model\Member; @@ -48,11 +49,12 @@ class CirclesRequest extends CirclesRequestBuilder { * In case of interaction with users, Please use getCircle() instead. * * @param string $circleUniqueId + * @param bool $allSettings * * @return Circle * @throws CircleDoesNotExistException */ - public function forceGetCircle($circleUniqueId) { + public function forceGetCircle($circleUniqueId, bool $allSettings = false) { $qb = $this->getCirclesSelectSql(); $this->leftJoinOwner($qb, ''); @@ -66,7 +68,7 @@ public function forceGetCircle($circleUniqueId) { throw new CircleDoesNotExistException($this->l10n->t('Circle not found')); } - return $this->parseCirclesSelectSql($data); + return $this->parseCirclesSelectSql($data, $allSettings); } @@ -89,7 +91,7 @@ public function forceGetCircles(string $ownerId = '') { $circles = []; $cursor = $qb->execute(); while ($data = $cursor->fetch()) { - $circles[] = $this->parseCirclesSelectSql($data); + $circles[] = $this->parseCirclesSelectSql($data, true); } $cursor->closeCursor(); @@ -138,6 +140,7 @@ public function forceGetCircleByName($name) { * * @return Circle[] * @throws ConfigNoCircleAvailableException + * @throws GSStatusException */ public function getCircles( string $userId, int $circleType = 0, string $name = '', int $level = 0, bool $forceAll = false, diff --git a/lib/Db/CirclesRequestBuilder.php b/lib/Db/CirclesRequestBuilder.php index e0f44286a..ba5d455de 100644 --- a/lib/Db/CirclesRequestBuilder.php +++ b/lib/Db/CirclesRequestBuilder.php @@ -378,10 +378,11 @@ protected function getCirclesSelectSql() { /** * @param array $data + * @param bool $allSettings * * @return Circle */ - protected function parseCirclesSelectSql($data) { + protected function parseCirclesSelectSql($data, bool $allSettings = false) { $circle = new Circle(); $circle->setId($data['id']); @@ -395,7 +396,7 @@ protected function parseCirclesSelectSql($data) { if ($data['contact_groupname'] !== null) { $circle->setContactGroupName($data['contact_groupname']); } - $circle->setSettings($data['settings']); + $circle->setSettings($data['settings'], $allSettings); $circle->setType($data['type']); $circle->setCreation($data['creation']); diff --git a/lib/Db/SharingFrameRequest.php b/lib/Db/SharingFrameRequest.php index 8c5e0c48e..7075d4834 100644 --- a/lib/Db/SharingFrameRequest.php +++ b/lib/Db/SharingFrameRequest.php @@ -118,4 +118,14 @@ public function updateSharingFrame(SharingFrame $frame) { } + + + public function updatePasswordOnShares(string $circleId, string $password) { + $qb = $this->getSharesUpdateSql($frame->getUniqueId()); + + $this->limitToShareWith($qb, $circleId); + $this->limitToShareType($qb, self::SHARE_TYPE); + } + + } diff --git a/lib/Db/TokensRequest.php b/lib/Db/TokensRequest.php index ea04eced1..bbe2476fe 100644 --- a/lib/Db/TokensRequest.php +++ b/lib/Db/TokensRequest.php @@ -164,5 +164,20 @@ public function removeTokensFromMember(Member $member) { $qb->execute(); } + + public function updateSinglePassword(string $circleId, string $password) { + $qb = $this->getTokensUpdateSql(); + + if ($password !== '') { + $hasher = \OC::$server->getHasher(); + $password = $hasher->hash($password); + } + + $this->limitToCircleId($qb, $circleId); + $qb->set('password', $qb->createNamedParameter($password)); + + $qb->execute(); + } + } diff --git a/lib/Db/TokensRequestBuilder.php b/lib/Db/TokensRequestBuilder.php index 074d37525..13fed45d5 100644 --- a/lib/Db/TokensRequestBuilder.php +++ b/lib/Db/TokensRequestBuilder.php @@ -61,12 +61,9 @@ protected function getTokensInsertSql() { /** * Base of the Sql Update request for Groups * - * @param int $circleId - * @param string $groupId - * * @return IQueryBuilder */ - protected function getTokensUpdateSql($circleId, $groupId) { + protected function getTokensUpdateSql() { $qb = $this->dbConnection->getQueryBuilder(); $qb->update(self::TABLE_TOKENS); diff --git a/lib/GlobalScale/CircleUpdate.php b/lib/GlobalScale/CircleUpdate.php index 86847bb76..dfd282bff 100644 --- a/lib/GlobalScale/CircleUpdate.php +++ b/lib/GlobalScale/CircleUpdate.php @@ -73,6 +73,7 @@ public function verify(GSEvent $event, bool $localCheck = false, bool $mustBeChe * @param GSEvent $event * * @throws CircleAlreadyExistsException + * @throws ConfigNoCircleAvailableException */ public function manage(GSEvent $event): void { if (!$event->hasCircle()) { @@ -83,7 +84,10 @@ public function manage(GSEvent $event): void { $settings = $event->getData() ->gArray('settings'); $ak = array_keys($settings); - foreach ($ak AS $k) { + foreach ($ak as $k) { + if ($k === 'password_single') { + $circle->setPasswordSingle($settings[$k]); + } $circle->setSetting($k, $settings[$k]); } @@ -98,6 +102,12 @@ public function manage(GSEvent $event): void { ] ); + $data = $event->getData(); + + if ($data->gBool('password_changed')) { + $this->circlesService->updatePasswordOnShares($circle); + } + $this->eventsService->onSettingsChange($circle, $oldSettings); } diff --git a/lib/GlobalScale/FileShare.php b/lib/GlobalScale/FileShare.php index 09c92d1b4..37025ae80 100644 --- a/lib/GlobalScale/FileShare.php +++ b/lib/GlobalScale/FileShare.php @@ -36,6 +36,8 @@ use OC; use OC\Share20\Share; use OCA\Circles\AppInfo\Application; +use OCA\Circles\Exceptions\CircleDoesNotExistException; +use OCA\Circles\Exceptions\GSStatusException; use OCA\Circles\Exceptions\TokenDoesNotExistException; use OCA\Circles\Model\Circle; use OCA\Circles\Model\GlobalScale\GSEvent; @@ -98,6 +100,9 @@ public function verify(GSEvent $event, bool $localCheck = false, bool $mustBeChe /** * @param GSEvent $event + * + * @throws GSStatusException + * @throws CircleDoesNotExistException */ public function manage(GSEvent $event): void { $circle = $event->getCircle(); @@ -140,7 +145,7 @@ public function manage(GSEvent $event): void { $accounts = []; foreach ($members as $member) { if ($member->getInstance() === '') { - $accounts[] = $this->getInfosFromContact($member); + $accounts[] = $this->miscService->getInfosFromContact($member); } } @@ -150,6 +155,8 @@ public function manage(GSEvent $event): void { /** * @param GSEvent[] $events + * + * @throws CircleDoesNotExistException */ public function result(array $events): void { $event = null; @@ -179,13 +186,10 @@ public function result(array $events): void { * @param Circle $circle * @param string $memberId * @param array $emails + * + * @throws CircleDoesNotExistException */ private function sendShareToContact(GSEvent $event, Circle $circle, string $memberId, array $emails) { - $password = ''; - if ($this->configService->enforcePasswordProtection($circle)) { - $password = $this->miscService->token(15); - } - try { $member = $this->membersRequest->forceGetMemberById($memberId); $share = $this->getShareFromData($event->getData()); @@ -193,6 +197,18 @@ private function sendShareToContact(GSEvent $event, Circle $circle, string $memb return; } + $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); + } + } + try { $sharesToken = $this->tokensRequest->generateTokenForMember($member, (int)$share->getId(), $password); @@ -200,6 +216,10 @@ private function sendShareToContact(GSEvent $event, Circle $circle, string $memb return; } + if (!$sendPasswordByMail) { + $password = ''; + } + foreach ($emails as $mail) { $this->sharedByMail($circle, $share, $mail, $sharesToken, $password); } @@ -400,22 +420,6 @@ private function generateEmailTemplate($subject, $text, $fileName, $link, $autho } - /** - * @param Member $member - * - * @return array - */ - private function getInfosFromContact(Member $member): array { - $contact = MiscService::getContactData($member->getUserId()); - - return [ - 'memberId' => $member->getMemberId(), - 'emails' => $this->getArray('EMAIL', $contact), - 'cloudIds' => $this->getArray('CLOUD', $contact) - ]; - } - - /** * @param string $circleId * diff --git a/lib/GlobalScale/GlobalSync.php b/lib/GlobalScale/GlobalSync.php index 53c66fd0b..fdb00f1cb 100644 --- a/lib/GlobalScale/GlobalSync.php +++ b/lib/GlobalScale/GlobalSync.php @@ -63,7 +63,7 @@ public function manage(GSEvent $event): void { $data = $event->getData(); $circles = []; foreach ($data->gAll() as $circle) { - $circle = Circle::fromArray($circle); + $circle = Circle::fromArray($circle, true); $circles[] = $circle; $this->syncCircle($circle, $event->getSource()); @@ -86,7 +86,7 @@ public function result(array $events): void { */ private function syncCircle(Circle $circle, string $source): void { try { - $knownCircle = $this->circlesRequest->forceGetCircle($circle->getUniqueId()); + $knownCircle = $this->circlesRequest->forceGetCircle($circle->getUniqueId(), true); if (!$this->compareCircles($knownCircle, $circle)) { try { diff --git a/lib/GlobalScale/MemberAdd.php b/lib/GlobalScale/MemberAdd.php index 5329e070b..ba39e51f6 100644 --- a/lib/GlobalScale/MemberAdd.php +++ b/lib/GlobalScale/MemberAdd.php @@ -109,11 +109,24 @@ public function verify(GSEvent $event, bool $localCheck = false, bool $mustBeChe $this->membersService->addMemberBasedOnItsType($circle, $member); $password = ''; + $sendPasswordByMail = false; if ($this->configService->enforcePasswordProtection($circle)) { - $password = $this->miscService->token(15); + if ($circle->getSetting('password_single_enabled') === 'true') { + $password = $circle->getPasswordSingle(); + } else { + $sendPasswordByMail = true; + $password = $this->miscService->token(15); + } } - $event->setData(new SimpleDataStore(['password' => $password])); + $event->setData( + new SimpleDataStore( + [ + 'password' => $password, + 'passwordByMail' => $sendPasswordByMail + ] + ) + ); $event->setMember($member); } @@ -137,16 +150,19 @@ public function manage(GSEvent $event): void { $password = $event->getData() ->g('password'); + $shares = $this->generateUnknownSharesLinks($circle, $member, $password); + $result = [ + 'unknownShares' => $shares, + 'cachedName' => $cachedName + ]; - $event->setResult( - new SimpleDataStore( - [ - 'unknownShares' => $shares, - 'cachedName' => $cachedName - ] - ) - ); + if ($member->getType() === Member::TYPE_CONTACT + && $member->getInstance() === $this->configService->getLocalCloudId()) { + $result['contact'] = $this->miscService->getInfosFromContact($member); + } + + $event->setResult(new SimpleDataStore($result)); $this->eventsService->onMemberNew($circle, $member); } @@ -160,10 +176,12 @@ public function result(array $events): void { $password = $cachedName = ''; $circle = $member = null; $links = []; + $recipients = []; foreach ($events as $event) { - $password = $event->getData() - ->g('password'); - + $data = $event->getData(); + if ($data->gBool('passwordByMail') !== false) { + $password = $data->g('password'); + } $circle = $event->getCircle(); $member = $event->getMember(); $result = $event->getResult(); @@ -172,6 +190,10 @@ public function result(array $events): void { } $links = array_merge($links, $result->gArray('unknownShares')); + $contact = $result->gArray('contact'); + if (!empty($contact)) { + $recipients = $contact['emails']; + } } if ($circle === null || $member === null) { @@ -185,18 +207,24 @@ public function result(array $events): void { if ($member->getType() === Member::TYPE_MAIL || $member->getType() === Member::TYPE_CONTACT) { - $this->memberIsMailbox($circle, $member, $links, $password); + if ($member->getType() === Member::TYPE_MAIL) { + $recipients = [$member->getUserId()]; + } + + foreach ($recipients as $recipient) { + $this->memberIsMailbox($circle, $recipient, $links, $password); + } } } /** - * @param Circle|null $circle - * @param Member|null $member + * @param Circle $circle + * @param string $recipient * @param array $links * @param string $password */ - private function memberIsMailbox(Circle $circle, Member $member, array $links, string $password) { + private function memberIsMailbox(Circle $circle, string $recipient, array $links, string $password) { if ($circle->getViewer() === null) { $author = $circle->getOwner() ->getUserId(); @@ -204,7 +232,6 @@ private function memberIsMailbox(Circle $circle, Member $member, array $links, s $author = $circle->getViewer() ->getUserId(); } - $recipient = $member->getUserId(); try { $template = $this->generateMailExitingShares($author, $circle->getName()); diff --git a/lib/Model/BaseCircle.php b/lib/Model/BaseCircle.php index 92967c0ec..28275df6e 100644 --- a/lib/Model/BaseCircle.php +++ b/lib/Model/BaseCircle.php @@ -34,6 +34,7 @@ class BaseCircle { const CIRCLES_SETTINGS_DEFAULT = [ 'password_enforcement' => 'false', + 'password_single' => '', 'allow_links' => 'false', 'allow_links_auto' => 'false', 'allow_links_files' => 'false' @@ -78,6 +79,9 @@ class BaseCircle { /** @var array */ private $settings = []; + /** @var string */ + private $passwordSingle = ''; + /** @var int */ private $type; @@ -345,16 +349,24 @@ public function getContactGroupName() { /** * @param string|array $settings + * @param bool $all * * @return $this */ - public function setSettings($settings) { + public function setSettings($settings, bool $all = false) { if (is_array($settings)) { $this->settings = $settings; } else if (is_string($settings)) { $this->settings = (array)json_decode($settings, true); } + if (array_key_exists('password_single', $this->settings)) { + $this->setPasswordSingle($this->settings['password_single']); + if (!$all) { + $this->settings['password_single'] = ''; + } + } + return $this; } @@ -426,6 +438,22 @@ public function getSetting($k) { return null; } + + /** + * @return string + */ + public function getPasswordSingle(): string { + return $this->passwordSingle; + } + + /** + * @param string $passwordSingle + */ + public function setPasswordSingle(string $passwordSingle): void { + $this->passwordSingle = $passwordSingle; + } + + /** * * @param string $type diff --git a/lib/Model/Circle.php b/lib/Model/Circle.php index 52e11c370..ac8184a6a 100644 --- a/lib/Model/Circle.php +++ b/lib/Model/Circle.php @@ -138,10 +138,11 @@ public function getJson($full = false, $light = false) { * set all infos from an Array. * * @param $arr + * @param bool $allSettings * * @return $this */ - public static function fromArray($arr) { + public static function fromArray($arr, bool $allSettings = false) { if ($arr === null || empty($arr)) { return new Circle(); } @@ -155,7 +156,7 @@ public static function fromArray($arr) { $circle->setUniqueId($arr['unique_id']); $circle->setDescription($arr['description']); - $circle->setSettings(self::getSettingsFromArray($arr)); + $circle->setSettings(self::getSettingsFromArray($arr), $allSettings); $circle->setLinks(self::getLinksFromArray($arr)); $circle->setCreation($arr['creation']); diff --git a/lib/Service/CirclesService.php b/lib/Service/CirclesService.php index f06bde2e0..d1bcb4484 100644 --- a/lib/Service/CirclesService.php +++ b/lib/Service/CirclesService.php @@ -30,6 +30,7 @@ namespace OCA\Circles\Service; +use daita\MySmallPhpTools\Traits\TArrayTools; use Exception; use OC; use OCA\Circles\AppInfo\Application; @@ -38,6 +39,7 @@ use OCA\Circles\Db\FederatedLinksRequest; use OCA\Circles\Db\MembersRequest; use OCA\Circles\Db\SharesRequest; +use OCA\Circles\Db\TokensRequest; use OCA\Circles\Exceptions\CircleAlreadyExistsException; use OCA\Circles\Exceptions\CircleDoesNotExistException; use OCA\Circles\Exceptions\CircleTypeDisabledException; @@ -55,6 +57,10 @@ class CirclesService { + + use TArrayTools; + + /** @var string */ private $userId; @@ -73,6 +79,9 @@ class CirclesService { /** @var MembersRequest */ private $membersRequest; + /** @var TokensRequest */ + private $tokensRequest; + /** @var SharesRequest */ private $sharesRequest; @@ -102,6 +111,7 @@ class CirclesService { * @param ConfigService $configService * @param CirclesRequest $circlesRequest * @param MembersRequest $membersRequest + * @param TokensRequest $tokensRequest * @param SharesRequest $sharesRequest * @param FederatedLinksRequest $federatedLinksRequest * @param GSUpstreamService $gsUpstreamService @@ -117,6 +127,7 @@ public function __construct( ConfigService $configService, CirclesRequest $circlesRequest, MembersRequest $membersRequest, + TokensRequest $tokensRequest, SharesRequest $sharesRequest, FederatedLinksRequest $federatedLinksRequest, GSUpstreamService $gsUpstreamService, @@ -138,6 +149,7 @@ public function __construct( $this->configService = $configService; $this->circlesRequest = $circlesRequest; $this->membersRequest = $membersRequest; + $this->tokensRequest = $tokensRequest; $this->sharesRequest = $sharesRequest; $this->federatedLinksRequest = $federatedLinksRequest; $this->gsUpstreamService = $gsUpstreamService; @@ -353,6 +365,11 @@ public function settingsCircle(string $circleUniqueId, array $settings) { $settings['members_limit'] = $circle->getSetting('members_limit'); } + if ($this->get('password_single', $settings) === '' + && $circle->getSetting('password_single_enabled') === 'false') { + $settings['password_single_enabled'] = false; + } + // can only be run from the instance of the circle's owner. $event = new GSEvent(GSEvent::CIRCLE_UPDATE); $event->setCircle($circle); @@ -360,12 +377,30 @@ public function settingsCircle(string $circleUniqueId, array $settings) { ->sBool('local_admin', $this->viewerIsAdmin()) ->sArray('settings', $settings); + if ($this->getBool('password_enforcement', $settings) === true + && $this->getBool('password_single_enabled', $settings) === true + && $this->get('password_single', $settings) !== '' + ) { + $event->getData() + ->sBool('password_changed', true); + } + $this->gsUpstreamService->newEvent($event); + $circle->setSettings($settings); + return $circle; } + /** + * @param Circle $circle + */ + public function updatePasswordOnShares(Circle $circle) { + $this->tokensRequest->updateSinglePassword($circle->getUniqueId(), $circle->getPasswordSingle()); + } + + /** * Join a circle. * diff --git a/lib/Service/MiscService.php b/lib/Service/MiscService.php index 607400ad0..df7ba6a10 100644 --- a/lib/Service/MiscService.php +++ b/lib/Service/MiscService.php @@ -26,6 +26,7 @@ namespace OCA\Circles\Service; +use daita\MySmallPhpTools\Traits\TArrayTools; use Exception; use OC; use OC\User\NoUserException; @@ -40,6 +41,10 @@ class MiscService { + + use TArrayTools; + + /** @var ILogger */ private $logger; @@ -354,5 +359,22 @@ public function token(int $length = 0): string { return $str; } + + /** + * @param Member $member + * + * @return array + */ + public function getInfosFromContact(Member $member) { + $contact = MiscService::getContactData($member->getUserId()); + + return [ + 'memberId' => $member->getMemberId(), + 'emails' => $this->getArray('EMAIL', $contact), + 'cloudIds' => $this->getArray('CLOUD', $contact) + ]; + + } + } diff --git a/templates/navigate.php b/templates/navigate.php index b5f2b1ce5..a78e07159 100644 --- a/templates/navigate.php +++ b/templates/navigate.php @@ -362,12 +362,12 @@ t('Name of the Circle')); ?> - + t('Description')); ?> - + @@ -379,7 +379,7 @@ ) ); ?> - + @@ -388,7 +388,19 @@ $l->t('External share will be protected by a randomly generated password') ); ?> - + + + + + t('Single password')); ?>
+ t( + 'One password for all shares. If not, a randomly generated password is sent by mail' + ) + ); ?> + + + @@ -399,7 +411,7 @@ ) ); ?> - +