diff options
Diffstat (limited to 'lib/private')
31 files changed, 1533 insertions, 250 deletions
diff --git a/lib/private/Activity/Event.php b/lib/private/Activity/Event.php index af0605d82c8..df6756940a0 100644 --- a/lib/private/Activity/Event.php +++ b/lib/private/Activity/Event.php @@ -24,229 +24,528 @@ namespace OC\Activity; use OCP\Activity\IEvent; +use OCP\RichObjectStrings\InvalidObjectExeption; +use OCP\RichObjectStrings\IValidator; class Event implements IEvent { + + /** @var string */ + protected $app = ''; + /** @var string */ + protected $type = ''; + /** @var string */ + protected $affectedUser = ''; + /** @var string */ + protected $author = ''; + /** @var int */ + protected $timestamp = 0; + /** @var string */ + protected $subject = ''; + /** @var array */ + protected $subjectParameters = []; + /** @var string */ + protected $subjectParsed; + /** @var string */ + protected $subjectRich; + /** @var array */ + protected $subjectRichParameters; + /** @var string */ + protected $message = ''; /** @var array */ - protected $data = [ - 'app' => null, - 'type' => null, - 'affected_user' => null, - 'author' => null, - 'timestamp' => null, - 'subject' => null, - 'subject_parameters' => null, - 'message' => '', - 'message_parameters' => [], - 'object_type' => '', - 'object_id' => 0, - 'object_name' => '', - 'link' => '', - ]; + protected $messageParameters = []; + /** @var string */ + protected $messageParsed; + /** @var string */ + protected $messageRich; + /** @var array */ + protected $messageRichParameters; + /** @var string */ + protected $objectType = ''; + /** @var int */ + protected $objectId = 0; + /** @var string */ + protected $objectName = ''; + /** @var string */ + protected $link = ''; + /** @var string */ + protected $icon = ''; + + /** @var IEvent */ + protected $child = null; + /** @var IValidator */ + protected $richValidator; + + /** + * @param IValidator $richValidator + */ + public function __construct(IValidator $richValidator) { + $this->richValidator = $richValidator; + } /** * Set the app of the activity * * @param string $app * @return IEvent + * @throws \InvalidArgumentException if the app id is invalid * @since 8.2.0 */ public function setApp($app) { - $this->data['app'] = (string) $app; + if (!is_string($app) || $app === '' || isset($app[32])) { + throw new \InvalidArgumentException('The given app is invalid'); + } + $this->app = (string) $app; return $this; } /** + * @return string + */ + public function getApp() { + return $this->app; + } + + /** * Set the type of the activity * * @param string $type * @return IEvent + * @throws \InvalidArgumentException if the type is invalid * @since 8.2.0 */ public function setType($type) { - $this->data['type'] = (string) $type; + if (!is_string($type) || $type === '' || isset($type[255])) { + throw new \InvalidArgumentException('The given type is invalid'); + } + $this->type = (string) $type; return $this; } /** + * @return string + */ + public function getType() { + return $this->type; + } + + /** * Set the affected user of the activity * * @param string $affectedUser * @return IEvent + * @throws \InvalidArgumentException if the affected user is invalid * @since 8.2.0 */ public function setAffectedUser($affectedUser) { - $this->data['affected_user'] = (string) $affectedUser; + if (!is_string($affectedUser) || $affectedUser === '' || isset($affectedUser[64])) { + throw new \InvalidArgumentException('The given affected user is invalid'); + } + $this->affectedUser = (string) $affectedUser; return $this; } /** + * @return string + */ + public function getAffectedUser() { + return $this->affectedUser; + } + + /** * Set the author of the activity * * @param string $author * @return IEvent + * @throws \InvalidArgumentException if the author is invalid * @since 8.2.0 */ public function setAuthor($author) { - $this->data['author'] = (string) $author; + if (!is_string($author) || isset($author[64])) { + throw new \InvalidArgumentException('The given author user is invalid'. serialize($author)); + } + $this->author = (string) $author; return $this; } /** + * @return string + */ + public function getAuthor() { + return $this->author; + } + + /** * Set the timestamp of the activity * * @param int $timestamp * @return IEvent + * @throws \InvalidArgumentException if the timestamp is invalid * @since 8.2.0 */ public function setTimestamp($timestamp) { - $this->data['timestamp'] = (int) $timestamp; + if (!is_int($timestamp)) { + throw new \InvalidArgumentException('The given timestamp is invalid'); + } + $this->timestamp = (int) $timestamp; return $this; } /** + * @return int + */ + public function getTimestamp() { + return $this->timestamp; + } + + /** * Set the subject of the activity * * @param string $subject * @param array $parameters * @return IEvent + * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 8.2.0 */ public function setSubject($subject, array $parameters = []) { - $this->data['subject'] = (string) $subject; - $this->data['subject_parameters'] = $parameters; + if (!is_string($subject) || isset($subject[255])) { + throw new \InvalidArgumentException('The given subject is invalid'); + } + $this->subject = (string) $subject; + $this->subjectParameters = $parameters; return $this; } /** + * @return string + */ + public function getSubject() { + return $this->subject; + } + + /** + * @return array + */ + public function getSubjectParameters() { + return $this->subjectParameters; + } + + /** + * @param string $subject + * @return $this + * @throws \InvalidArgumentException if the subject is invalid + * @since 11.0.0 + */ + public function setParsedSubject($subject) { + if (!is_string($subject) || $subject === '') { + throw new \InvalidArgumentException('The given parsed subject is invalid'); + } + $this->subjectParsed = $subject; + return $this; + } + + /** + * @return string + * @since 11.0.0 + */ + public function getParsedSubject() { + return $this->subjectParsed; + } + + /** + * @param string $subject + * @param array $parameters + * @return $this + * @throws \InvalidArgumentException if the subject or parameters are invalid + * @since 11.0.0 + */ + public function setRichSubject($subject, array $parameters = []) { + if (!is_string($subject) || $subject === '') { + throw new \InvalidArgumentException('The given parsed subject is invalid'); + } + $this->subjectRich = $subject; + + if (!is_array($parameters)) { + throw new \InvalidArgumentException('The given subject parameters are invalid'); + } + $this->subjectRichParameters = $parameters; + + return $this; + } + + /** + * @return string + * @since 11.0.0 + */ + public function getRichSubject() { + return $this->subjectRich; + } + + /** + * @return array[] + * @since 11.0.0 + */ + public function getRichSubjectParameters() { + return $this->subjectRichParameters; + } + + /** * Set the message of the activity * * @param string $message * @param array $parameters * @return IEvent + * @throws \InvalidArgumentException if the message or parameters are invalid * @since 8.2.0 */ public function setMessage($message, array $parameters = []) { - $this->data['message'] = (string) $message; - $this->data['message_parameters'] = $parameters; + if (!is_string($message) || isset($message[255])) { + throw new \InvalidArgumentException('The given message is invalid'); + } + $this->message = (string) $message; + $this->messageParameters = $parameters; return $this; } /** - * Set the object of the activity - * - * @param string $objectType - * @param int $objectId - * @param string $objectName - * @return IEvent - * @since 8.2.0 + * @return string */ - public function setObject($objectType, $objectId, $objectName = '') { - $this->data['object_type'] = (string) $objectType; - $this->data['object_id'] = (int) $objectId; - $this->data['object_name'] = (string) $objectName; + public function getMessage() { + return $this->message; + } + + /** + * @return array + */ + public function getMessageParameters() { + return $this->messageParameters; + } + + /** + * @param string $message + * @return $this + * @throws \InvalidArgumentException if the message is invalid + * @since 11.0.0 + */ + public function setParsedMessage($message) { + if (!is_string($message)) { + throw new \InvalidArgumentException('The given parsed message is invalid'); + } + $this->messageParsed = $message; return $this; } /** - * Set the link of the activity - * - * @param string $link - * @return IEvent - * @since 8.2.0 + * @return string + * @since 11.0.0 */ - public function setLink($link) { - $this->data['link'] = (string) $link; + public function getParsedMessage() { + return $this->messageParsed; + } + + /** + * @param string $message + * @param array $parameters + * @return $this + * @throws \InvalidArgumentException if the subject or parameters are invalid + * @since 11.0.0 + */ + public function setRichMessage($message, array $parameters = []) { + if (!is_string($message)) { + throw new \InvalidArgumentException('The given parsed message is invalid'); + } + $this->messageRich = $message; + + if (!is_array($parameters)) { + throw new \InvalidArgumentException('The given message parameters are invalid'); + } + $this->messageRichParameters = $parameters; + return $this; } /** * @return string + * @since 11.0.0 */ - public function getApp() { - return $this->data['app']; + public function getRichMessage() { + return $this->messageRich; + } + + /** + * @return array[] + * @since 11.0.0 + */ + public function getRichMessageParameters() { + return $this->messageRichParameters; + } + + /** + * Set the object of the activity + * + * @param string $objectType + * @param int $objectId + * @param string $objectName + * @return IEvent + * @throws \InvalidArgumentException if the object is invalid + * @since 8.2.0 + */ + public function setObject($objectType, $objectId, $objectName = '') { + if (!is_string($objectType) || isset($objectType[255])) { + throw new \InvalidArgumentException('The given object type is invalid'); + } + if (!is_int($objectId)) { + throw new \InvalidArgumentException('The given object id is invalid'); + } + if (!is_string($objectName) || isset($objectName[4000])) { + throw new \InvalidArgumentException('The given object name is invalid'); + } + $this->objectType = (string) $objectType; + $this->objectId = (int) $objectId; + $this->objectName = (string) $objectName; + return $this; } /** * @return string */ - public function getType() { - return $this->data['type']; + public function getObjectType() { + return $this->objectType; } /** * @return string */ - public function getAffectedUser() { - return $this->data['affected_user']; + public function getObjectId() { + return $this->objectId; } /** * @return string */ - public function getAuthor() { - return $this->data['author']; + public function getObjectName() { + return $this->objectName; } /** - * @return int + * Set the link of the activity + * + * @param string $link + * @return IEvent + * @throws \InvalidArgumentException if the link is invalid + * @since 8.2.0 */ - public function getTimestamp() { - return $this->data['timestamp']; + public function setLink($link) { + if (!is_string($link) || isset($link[4000])) { + throw new \InvalidArgumentException('The given link is invalid'); + } + $this->link = (string) $link; + return $this; } /** * @return string */ - public function getSubject() { - return $this->data['subject']; + public function getLink() { + return $this->link; } /** - * @return array + * @param string $icon + * @return $this + * @throws \InvalidArgumentException if the icon is invalid + * @since 11.0.0 */ - public function getSubjectParameters() { - return $this->data['subject_parameters']; + public function setIcon($icon) { + if (!is_string($icon) || isset($icon[4000])) { + throw new \InvalidArgumentException('The given icon is invalid'); + } + $this->icon = $icon; + return $this; } /** * @return string + * @since 11.0.0 */ - public function getMessage() { - return $this->data['message']; + public function getIcon() { + return $this->icon; } /** - * @return array + * @param IEvent $child + * @since 11.0.0 */ - public function getMessageParameters() { - return $this->data['message_parameters']; + public function setChildEvent(IEvent $child) { + $this->child = $child; } /** - * @return string + * @return IEvent|null + * @since 11.0.0 */ - public function getObjectType() { - return $this->data['object_type']; + public function getChildEvent() { + return $this->child; } /** - * @return string + * @return bool + * @since 8.2.0 */ - public function getObjectId() { - return $this->data['object_id']; + public function isValid() { + return + $this->isValidCommon() + && + $this->getSubject() !== '' + ; } /** - * @return string + * @return bool + * @since 8.2.0 */ - public function getObjectName() { - return $this->data['object_name']; + public function isValidParsed() { + if ($this->getRichSubject() !== '' || !empty($this->getRichSubjectParameters())) { + try { + $this->richValidator->validate($this->getRichSubject(), $this->getRichSubjectParameters()); + } catch (InvalidObjectExeption $e) { + return false; + } + } + + if ($this->getRichMessage() !== '' || !empty($this->getRichMessageParameters())) { + try { + $this->richValidator->validate($this->getRichMessage(), $this->getRichMessageParameters()); + } catch (InvalidObjectExeption $e) { + return false; + } + } + + return + $this->isValidCommon() + && + $this->getParsedSubject() !== '' + ; } /** - * @return string + * @return bool */ - public function getLink() { - return $this->data['link']; + protected function isValidCommon() { + return + $this->getApp() !== '' + && + $this->getType() !== '' + && + $this->getAffectedUser() !== '' + && + $this->getTimestamp() !== 0 + /** + * Disabled for BC with old activities + && + $this->getObjectType() !== '' + && + $this->getObjectId() !== 0 + */ + ; } } diff --git a/lib/private/Activity/LegacyFilter.php b/lib/private/Activity/LegacyFilter.php new file mode 100644 index 00000000000..eadb5b1558f --- /dev/null +++ b/lib/private/Activity/LegacyFilter.php @@ -0,0 +1,108 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @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 OC\Activity; + +use OCP\Activity\IFilter; +use OCP\Activity\IManager; + +class LegacyFilter implements IFilter { + + /** @var IManager */ + protected $manager; + /** @var string */ + protected $identifier; + /** @var string */ + protected $name; + /** @var bool */ + protected $isTopFilter; + + /** + * LegacySetting constructor. + * + * @param IManager $manager + * @param string $identifier + * @param string $name + * @param bool $isTopFilter + */ + public function __construct(IManager $manager, + $identifier, + $name, + $isTopFilter) { + $this->manager = $manager; + $this->identifier = $identifier; + $this->name = $name; + $this->isTopFilter = $isTopFilter; + } + + /** + * @return string Lowercase a-z and underscore only identifier + * @since 11.0.0 + */ + public function getIdentifier() { + return $this->identifier; + } + + /** + * @return string A translated string + * @since 11.0.0 + */ + public function getName() { + return $this->name; + } + + /** + * @return int whether the filter should be rather on the top or bottom of + * the admin section. The filters are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * @since 11.0.0 + */ + public function getPriority() { + return $this->isTopFilter ? 40 : 50; + } + + /** + * @return string Full URL to an icon, empty string when none is given + * @since 11.0.0 + */ + public function getIcon() { + // Old API was CSS class, so we can not use this... + return ''; + } + + /** + * @param string[] $types + * @return string[] An array of allowed apps from which activities should be displayed + * @since 11.0.0 + */ + public function filterTypes(array $types) { + return $this->manager->filterNotificationTypes($types, $this->getIdentifier()); + } + + /** + * @return string[] An array of allowed apps from which activities should be displayed + * @since 11.0.0 + */ + public function allowedApps() { + return []; + } +} + diff --git a/lib/private/Activity/LegacySetting.php b/lib/private/Activity/LegacySetting.php new file mode 100644 index 00000000000..27495afddb0 --- /dev/null +++ b/lib/private/Activity/LegacySetting.php @@ -0,0 +1,123 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @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 OC\Activity; + +use OCP\Activity\ISetting; + +class LegacySetting implements ISetting { + + /** @var string */ + protected $identifier; + /** @var string */ + protected $name; + /** @var bool */ + protected $canChangeStream; + /** @var bool */ + protected $isDefaultEnabledStream; + /** @var bool */ + protected $canChangeMail; + /** @var bool */ + protected $isDefaultEnabledMail; + + /** + * LegacySetting constructor. + * + * @param string $identifier + * @param string $name + * @param bool $canChangeStream + * @param bool $isDefaultEnabledStream + * @param bool $canChangeMail + * @param bool $isDefaultEnabledMail + */ + public function __construct($identifier, + $name, + $canChangeStream, + $isDefaultEnabledStream, + $canChangeMail, + $isDefaultEnabledMail) { + $this->identifier = $identifier; + $this->name = $name; + $this->canChangeStream = $canChangeStream; + $this->isDefaultEnabledStream = $isDefaultEnabledStream; + $this->canChangeMail = $canChangeMail; + $this->isDefaultEnabledMail = $isDefaultEnabledMail; + } + + /** + * @return string Lowercase a-z and underscore only identifier + * @since 11.0.0 + */ + public function getIdentifier() { + return $this->identifier; + } + + /** + * @return string A translated string + * @since 11.0.0 + */ + public function getName() { + return $this->name; + } + + /** + * @return int whether the filter should be rather on the top or bottom of + * the admin section. The filters are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * @since 11.0.0 + */ + public function getPriority() { + return 70; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function canChangeStream() { + return $this->canChangeStream; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function isDefaultEnabledStream() { + return $this->isDefaultEnabledStream; + } + + /** + * @return bool True when the option can be changed for the mail + * @since 11.0.0 + */ + public function canChangeMail() { + return $this->canChangeMail; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function isDefaultEnabledMail() { + return $this->isDefaultEnabledMail; + } +} + diff --git a/lib/private/Activity/Manager.php b/lib/private/Activity/Manager.php index 455bb3b8ee8..805124dc602 100644 --- a/lib/private/Activity/Manager.php +++ b/lib/private/Activity/Manager.php @@ -27,11 +27,15 @@ namespace OC\Activity; use OCP\Activity\IConsumer; use OCP\Activity\IEvent; use OCP\Activity\IExtension; +use OCP\Activity\IFilter; use OCP\Activity\IManager; +use OCP\Activity\IProvider; +use OCP\Activity\ISetting; use OCP\IConfig; use OCP\IRequest; use OCP\IUser; use OCP\IUserSession; +use OCP\RichObjectStrings\IValidator; class Manager implements IManager { /** @var IRequest */ @@ -43,6 +47,9 @@ class Manager implements IManager { /** @var IConfig */ protected $config; + /** @var IValidator */ + protected $validator; + /** @var string */ protected $formattingObjectType; @@ -58,13 +65,16 @@ class Manager implements IManager { * @param IRequest $request * @param IUserSession $session * @param IConfig $config + * @param IValidator $validator */ public function __construct(IRequest $request, IUserSession $session, - IConfig $config) { + IConfig $config, + IValidator $validator) { $this->request = $request; $this->session = $session; $this->config = $config; + $this->validator = $validator; } /** @var \Closure[] */ @@ -147,7 +157,7 @@ class Manager implements IManager { * @return IEvent */ public function generateEvent() { - return new Event(); + return new Event($this->validator); } /** @@ -160,24 +170,10 @@ class Manager implements IManager { * - setSubject() * * @param IEvent $event - * @return null * @throws \BadMethodCallException if required values have not been set */ public function publish(IEvent $event) { - if (!$event->getApp()) { - throw new \BadMethodCallException('App not set', 10); - } - if (!$event->getType()) { - throw new \BadMethodCallException('Type not set', 11); - } - if ($event->getAffectedUser() === null) { - throw new \BadMethodCallException('Affected user not set', 12); - } - if ($event->getSubject() === null || $event->getSubjectParameters() === null) { - throw new \BadMethodCallException('Subject not set', 13); - } - - if ($event->getAuthor() === null) { + if ($event->getAuthor() === '') { if ($this->session->getUser() instanceof IUser) { $event->setAuthor($this->session->getUser()->getUID()); } @@ -187,6 +183,10 @@ class Manager implements IManager { $event->setTimestamp(time()); } + if (!$event->isValid()) { + throw new \BadMethodCallException('The given event is invalid'); + } + foreach ($this->getConsumers() as $c) { $c->receive($event); } @@ -203,7 +203,6 @@ class Manager implements IManager { * @param string $affectedUser Recipient of the activity * @param string $type Type of the notification * @param int $priority Priority of the notification - * @return null */ public function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) { $event = $this->generateEvent(); @@ -235,59 +234,195 @@ class Manager implements IManager { * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * - * $callable has to return an instance of OCA\Activity\IConsumer + * $callable has to return an instance of OCA\Activity\IExtension * * @param \Closure $callable - * @return void */ public function registerExtension(\Closure $callable) { array_push($this->extensionsClosures, $callable); $this->extensions = []; } + /** @var string[] */ + protected $filterClasses = []; + + /** @var IFilter[] */ + protected $filters = []; + + /** @var bool */ + protected $loadedLegacyFilters = false; + /** - * Will return additional notification types as specified by other apps - * - * @param string $languageCode - * @return array + * @param string $filter Class must implement OCA\Activity\IFilter + * @return void */ - public function getNotificationTypes($languageCode) { - $filesNotificationTypes = []; - $sharingNotificationTypes = []; + public function registerFilter($filter) { + $this->filterClasses[$filter] = false; + } - $notificationTypes = array(); - foreach ($this->getExtensions() as $c) { - $result = $c->getNotificationTypes($languageCode); - if (is_array($result)) { - if (class_exists('\OCA\Files\Activity', false) && $c instanceof \OCA\Files\Activity) { - $filesNotificationTypes = $result; - continue; - } - if (class_exists('\OCA\Files_Sharing\Activity', false) && $c instanceof \OCA\Files_Sharing\Activity) { - $sharingNotificationTypes = $result; - continue; - } + /** + * @return IFilter[] + * @throws \InvalidArgumentException + */ + public function getFilters() { + if (!$this->loadedLegacyFilters) { + $legacyFilters = $this->getNavigation(); + + foreach ($legacyFilters['top'] as $filter => $data) { + $this->filters[$filter] = new LegacyFilter( + $this, $filter, $data['name'], true + ); + } - $notificationTypes = array_merge($notificationTypes, $result); + foreach ($legacyFilters['apps'] as $filter => $data) { + $this->filters[$filter] = new LegacyFilter( + $this, $filter, $data['name'], false + ); } + $this->loadedLegacyFilters = true; } - return array_merge($filesNotificationTypes, $sharingNotificationTypes, $notificationTypes); + foreach ($this->filterClasses as $class => $false) { + /** @var IFilter $filter */ + $filter = \OC::$server->query($class); + + if (!$filter instanceof IFilter) { + throw new \InvalidArgumentException('Invalid activity filter registered'); + } + + $this->filters[$filter->getIdentifier()] = $filter; + + unset($this->filterClasses[$class]); + } + return $this->filters; } /** - * @param string $method - * @return array + * @param string $id + * @return IFilter + * @throws \InvalidArgumentException when the filter was not found + * @since 11.0.0 */ - public function getDefaultTypes($method) { - $defaultTypes = array(); - foreach ($this->getExtensions() as $c) { - $types = $c->getDefaultTypes($method); - if (is_array($types)) { - $defaultTypes = array_merge($types, $defaultTypes); + public function getFilterById($id) { + $filters = $this->getFilters(); + + if (isset($filters[$id])) { + return $filters[$id]; + } + + throw new \InvalidArgumentException('Requested filter does not exist'); + } + + /** @var string[] */ + protected $providerClasses = []; + + /** @var IProvider[] */ + protected $providers = []; + + /** + * @param string $provider Class must implement OCA\Activity\IProvider + * @return void + */ + public function registerProvider($provider) { + $this->providerClasses[$provider] = false; + } + + /** + * @return IProvider[] + * @throws \InvalidArgumentException + */ + public function getProviders() { + foreach ($this->providerClasses as $class => $false) { + /** @var IProvider $provider */ + $provider = \OC::$server->query($class); + + if (!$provider instanceof IProvider) { + throw new \InvalidArgumentException('Invalid activity provider registered'); } + + $this->providers[] = $provider; + + unset($this->providerClasses[$class]); } - return $defaultTypes; + return $this->providers; + } + + /** @var string[] */ + protected $settingsClasses = []; + + /** @var ISetting[] */ + protected $settings = []; + + /** @var bool */ + protected $loadedLegacyTypes = false; + + /** + * @param string $setting Class must implement OCA\Activity\ISetting + * @return void + */ + public function registerSetting($setting) { + $this->settingsClasses[$setting] = false; + } + + /** + * @return ISetting[] + * @throws \InvalidArgumentException + */ + public function getSettings() { + if (!$this->loadedLegacyTypes) { + $l = \OC::$server->getL10N('core'); + $legacyTypes = $this->getNotificationTypes($l->getLanguageCode()); + $streamTypes = $this->getDefaultTypes(IExtension::METHOD_STREAM); + $mailTypes = $this->getDefaultTypes(IExtension::METHOD_MAIL); + foreach ($legacyTypes as $type => $data) { + if (is_string($data)) { + $desc = $data; + $canChangeStream = true; + $canChangeMail = true; + } else { + $desc = $data['desc']; + $canChangeStream = in_array(IExtension::METHOD_STREAM, $data['methods']); + $canChangeMail = in_array(IExtension::METHOD_MAIL, $data['methods']); + } + + $this->settings[$type] = new LegacySetting( + $type, $desc, + $canChangeStream, in_array($type, $streamTypes), + $canChangeMail, in_array($type, $mailTypes) + ); + } + $this->loadedLegacyTypes = true; + } + + foreach ($this->settingsClasses as $class => $false) { + /** @var ISetting $setting */ + $setting = \OC::$server->query($class); + + if (!$setting instanceof ISetting) { + throw new \InvalidArgumentException('Invalid activity filter registered'); + } + + $this->settings[$setting->getIdentifier()] = $setting; + + unset($this->settingsClasses[$class]); + } + return $this->settings; + } + + /** + * @param string $id + * @return ISetting + * @throws \InvalidArgumentException when the setting was not found + * @since 11.0.0 + */ + public function getSettingById($id) { + $settings = $this->getSettings(); + + if (isset($settings[$id])) { + return $settings[$id]; + } + + throw new \InvalidArgumentException('Requested setting does not exist'); } /** @@ -391,7 +526,62 @@ class Manager implements IManager { } /** + * Set the user we need to use + * + * @param string|null $currentUserId + * @throws \UnexpectedValueException If the user is invalid + */ + public function setCurrentUserId($currentUserId) { + if (!is_string($currentUserId) && $currentUserId !== null) { + throw new \UnexpectedValueException('The given current user is invalid'); + } + $this->currentUserId = $currentUserId; + } + + /** + * Get the user we need to use + * + * Either the user is logged in, or we try to get it from the token + * + * @return string + * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique + */ + public function getCurrentUserId() { + if ($this->currentUserId !== null) { + return $this->currentUserId; + } else if (!$this->session->isLoggedIn()) { + return $this->getUserFromToken(); + } else { + return $this->session->getUser()->getUID(); + } + } + + /** + * Get the user for the token + * + * @return string + * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique + */ + protected function getUserFromToken() { + $token = (string) $this->request->getParam('token', ''); + if (strlen($token) !== 30) { + throw new \UnexpectedValueException('The token is invalid'); + } + + $users = $this->config->getUsersForUserValue('activity', 'rsstoken', $token); + + if (sizeof($users) !== 1) { + // No unique user found + throw new \UnexpectedValueException('The token is invalid'); + } + + // Token found login as that user + return array_shift($users); + } + + /** * @return array + * @deprecated 11.0.0 - Use getFilters() instead */ public function getNavigation() { $entries = array( @@ -412,6 +602,7 @@ class Manager implements IManager { /** * @param string $filterValue * @return boolean + * @deprecated 11.0.0 - Use getFilterById() instead */ public function isFilterValid($filterValue) { if (isset($this->validFilters[$filterValue])) { @@ -433,6 +624,7 @@ class Manager implements IManager { * @param array $types * @param string $filter * @return array + * @deprecated 11.0.0 - Use getFilterById()->filterTypes() instead */ public function filterNotificationTypes($types, $filter) { if (!$this->isFilterValid($filter)) { @@ -451,6 +643,7 @@ class Manager implements IManager { /** * @param string $filter * @return array + * @deprecated 11.0.0 - Use getFilterById() instead */ public function getQueryForFilter($filter) { if (!$this->isFilterValid($filter)) { @@ -479,56 +672,42 @@ class Manager implements IManager { } /** - * Set the user we need to use + * Will return additional notification types as specified by other apps * - * @param string|null $currentUserId - * @throws \UnexpectedValueException If the user is invalid + * @param string $languageCode + * @return array + * @deprecated 11.0.0 - Use getSettings() instead */ - public function setCurrentUserId($currentUserId) { - if (!is_string($currentUserId) && $currentUserId !== null) { - throw new \UnexpectedValueException('The given current user is invalid'); - } - $this->currentUserId = $currentUserId; - } + public function getNotificationTypes($languageCode) { + $notificationTypes = $sharingNotificationTypes = []; + foreach ($this->getExtensions() as $c) { + $result = $c->getNotificationTypes($languageCode); + if (is_array($result)) { + if (class_exists('\OCA\Files_Sharing\Activity', false) && $c instanceof \OCA\Files_Sharing\Activity) { + $sharingNotificationTypes = $result; + continue; + } - /** - * Get the user we need to use - * - * Either the user is logged in, or we try to get it from the token - * - * @return string - * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique - */ - public function getCurrentUserId() { - if ($this->currentUserId !== null) { - return $this->currentUserId; - } else if (!$this->session->isLoggedIn()) { - return $this->getUserFromToken(); - } else { - return $this->session->getUser()->getUID(); + $notificationTypes = array_merge($notificationTypes, $result); + } } + + return array_merge($sharingNotificationTypes, $notificationTypes); } /** - * Get the user for the token - * - * @return string - * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique + * @param string $method + * @return array + * @deprecated 11.0.0 - Use getSettings()->isDefaulEnabled<method>() instead */ - protected function getUserFromToken() { - $token = (string) $this->request->getParam('token', ''); - if (strlen($token) !== 30) { - throw new \UnexpectedValueException('The token is invalid'); - } - - $users = $this->config->getUsersForUserValue('activity', 'rsstoken', $token); - - if (sizeof($users) !== 1) { - // No unique user found - throw new \UnexpectedValueException('The token is invalid'); + public function getDefaultTypes($method) { + $defaultTypes = array(); + foreach ($this->getExtensions() as $c) { + $types = $c->getDefaultTypes($method); + if (is_array($types)) { + $defaultTypes = array_merge($types, $defaultTypes); + } } - - // Token found login as that user - return array_shift($users); + return $defaultTypes; } } diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index af26d30d8e9..4e13d70371b 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -215,11 +215,13 @@ class AllConfig implements \OCP\IConfig { // TODO - FIXME $this->fixDIInit(); - if (isset($this->userCache[$userId][$appName][$key])) { - if ($this->userCache[$userId][$appName][$key] === (string)$value) { - return; - } else if ($preCondition !== null && $this->userCache[$userId][$appName][$key] !== (string)$preCondition) { + $prevValue = $this->getUserValue($userId, $appName, $key, null); + + if ($prevValue !== null) { + if ($prevValue === (string)$value) { return; + } else if ($preCondition !== null && $prevValue !== (string)$preCondition) { + throw new PreConditionNotMetException(); } else { $qb = $this->connection->getQueryBuilder(); $qb->update('preferences') diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php index c24b25ff14d..84d87efa3d3 100644 --- a/lib/private/App/DependencyAnalyzer.php +++ b/lib/private/App/DependencyAnalyzer.php @@ -336,13 +336,9 @@ class DependencyAnalyzer { switch ($version) { case '9.1': return '10'; - case '9.2': - return '11'; default: if (strpos($version, '9.1.') === 0) { $version = '10.0.' . substr($version, 4); - } else if (strpos($version, '9.2.') === 0) { - $version = '11.0.' . substr($version, 4); } return $version; } diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index 44f495534c9..47ce28e6e98 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -110,6 +110,18 @@ class InfoParser { if (!array_key_exists('commands', $array)) { $array['commands'] = []; } + if (!array_key_exists('activity', $array)) { + $array['activity'] = []; + } + if (!array_key_exists('filters', $array['activity'])) { + $array['activity']['filters'] = []; + } + if (!array_key_exists('settings', $array['activity'])) { + $array['activity']['settings'] = []; + } + if (!array_key_exists('providers', $array['activity'])) { + $array['activity']['providers'] = []; + } if (array_key_exists('types', $array)) { if (is_array($array['types'])) { @@ -144,6 +156,15 @@ class InfoParser { if (isset($array['commands']['command']) && is_array($array['commands']['command'])) { $array['commands'] = $array['commands']['command']; } + if (isset($array['activity']['filters']['filter']) && is_array($array['activity']['filters']['filter'])) { + $array['activity']['filters'] = $array['activity']['filters']['filter']; + } + if (isset($array['activity']['settings']['setting']) && is_array($array['activity']['settings']['setting'])) { + $array['activity']['settings'] = $array['activity']['settings']['setting']; + } + if (isset($array['activity']['providers']['provider']) && is_array($array['activity']['providers']['provider'])) { + $array['activity']['providers'] = $array['activity']['providers']['provider']; + } if(!is_null($this->cache)) { $this->cache->set($fileCacheKey, json_encode($array)); diff --git a/lib/private/AppFramework/Db/Db.php b/lib/private/AppFramework/Db/Db.php index 450549ffdbb..5aacdc517a2 100644 --- a/lib/private/AppFramework/Db/Db.php +++ b/lib/private/AppFramework/Db/Db.php @@ -306,7 +306,7 @@ class Db implements IDb { * Check whether or not the current database support 4byte wide unicode * * @return bool - * @since 9.2.0 + * @since 11.0.0 */ public function supports4ByteText() { return $this->connection->supports4ByteText(); diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index faef2f73b33..127430ea6cb 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -87,6 +87,17 @@ class DefaultToken extends Entity implements IToken { */ protected $lastCheck; + /** + * @var string + */ + protected $scope; + + public function __construct() { + $this->addType('type', 'int'); + $this->addType('lastActivity', 'int'); + $this->addType('lastCheck', 'int'); + } + public function getId() { return $this->id; } @@ -119,6 +130,7 @@ class DefaultToken extends Entity implements IToken { 'name' => $this->name, 'lastActivity' => $this->lastActivity, 'type' => $this->type, + 'scope' => $this->getScopeAsArray() ]; } @@ -140,4 +152,25 @@ class DefaultToken extends Entity implements IToken { return parent::setLastCheck($time); } + public function getScope() { + return parent::getScope(); + } + + public function getScopeAsArray() { + $scope = json_decode($this->getScope(), true); + if (!$scope) { + return [ + 'filesystem'=> true + ]; + } + return $scope; + } + + public function setScope($scope) { + if (is_array($scope)) { + parent::setScope(json_encode($scope)); + } else { + parent::setScope((string)$scope); + } + } } diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php index 752974ff240..8848cd3ec56 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -72,10 +72,9 @@ class DefaultTokenMapper extends Mapper { public function getToken($token) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check') + $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope') ->from('authtoken') - ->where($qb->expr()->eq('token', $qb->createParameter('token'))) - ->setParameter('token', $token) + ->where($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->execute(); $data = $result->fetch(); @@ -83,6 +82,30 @@ class DefaultTokenMapper extends Mapper { if ($data === false) { throw new DoesNotExistException('token does not exist'); } +; + return DefaultToken::fromRow($data); + } + + /** + * Get the token for $id + * + * @param string $id + * @throws DoesNotExistException + * @return DefaultToken + */ + public function getTokenById($id) { + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check', 'scope') + ->from('authtoken') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->execute(); + + $data = $result->fetch(); + $result->closeCursor(); + if ($data === false) { + throw new DoesNotExistException('token does not exist'); + }; return DefaultToken::fromRow($data); } @@ -98,7 +121,7 @@ class DefaultTokenMapper extends Mapper { public function getTokenByUser(IUser $user) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check') + $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope') ->from('authtoken') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->setMaxResults(1000); diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index 87f434c684c..0fdbc4a51dd 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -145,7 +145,7 @@ class DefaultTokenProvider implements IProvider { } /** - * Get a token by token id + * Get a token by token * * @param string $tokenId * @throws InvalidTokenException @@ -160,6 +160,21 @@ class DefaultTokenProvider implements IProvider { } /** + * Get a token by token id + * + * @param string $tokenId + * @throws InvalidTokenException + * @return DefaultToken + */ + public function getTokenById($tokenId) { + try { + return $this->mapper->getTokenById($tokenId); + } catch (DoesNotExistException $ex) { + throw new InvalidTokenException(); + } + } + + /** * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index ce14a5880c5..9f280263d76 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -50,7 +50,16 @@ interface IProvider { * @throws InvalidTokenException * @return IToken */ - public function getToken($tokenId) ; + public function getToken($tokenId); + + /** + * Get a token by token id + * + * @param string $tokenId + * @throws InvalidTokenException + * @return DefaultToken + */ + public function getTokenById($tokenId); /** * Duplicate an existing session token diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index 14811dd3201..49745b266c4 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -67,9 +67,30 @@ interface IToken extends JsonSerializable { public function getLastCheck(); /** - * Get the timestamp of the last password check + * Set the timestamp of the last password check * * @param int $time */ public function setLastCheck($time); + + /** + * Get the authentication scope for this token + * + * @return string + */ + public function getScope(); + + /** + * Get the authentication scope for this token + * + * @return array + */ + public function getScopeAsArray(); + + /** + * Set the authentication scope for this token + * + * @param array $scope + */ + public function setScope($scope); } diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php index b5f063be323..b9a6103f67f 100644 --- a/lib/private/Comments/Comment.php +++ b/lib/private/Comments/Comment.php @@ -207,7 +207,7 @@ class Comment implements IComment { * returns an array containing mentions that are included in the comment * * @return array each mention provides a 'type' and an 'id', see example below - * @since 9.2.0 + * @since 11.0.0 * * The return array looks like: * [ diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index 001f4f9441c..1467fef727b 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -768,7 +768,7 @@ class Manager implements ICommentsManager { * @param string $type * @param \Closure $closure * @throws \OutOfBoundsException - * @since 9.2.0 + * @since 11.0.0 * * Only one resolver shall be registered per type. Otherwise a * \OutOfBoundsException has to thrown. @@ -790,7 +790,7 @@ class Manager implements ICommentsManager { * @param string $id * @return string * @throws \OutOfBoundsException - * @since 9.2.0 + * @since 11.0.0 * * If a provided type was not registered, an \OutOfBoundsException shall * be thrown. It is upon the resolver discretion what to return of the diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 497ff0c8a26..a39dbe3783c 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -412,7 +412,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { * Check whether or not the current database support 4byte wide unicode * * @return bool - * @since 9.2.0 + * @since 11.0.0 */ public function supports4ByteText() { return ! ($this->getDatabasePlatform() instanceof MySqlPlatform && $this->getParams()['charset'] !== 'utf8mb4'); diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 55cf38bbdc9..ac0e66973d4 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -62,6 +62,7 @@ use OC\Cache\CappedMemoryCache; use OC\Files\Config\MountProviderCollection; use OC\Files\Mount\MountPoint; use OC\Files\Storage\StorageFactory; +use OC\Lockdown\Filesystem\NullStorage; use OCP\Files\Config\IMountProvider; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; @@ -216,7 +217,7 @@ class Filesystem { * @internal */ public static function logWarningWhenAddingStorageWrapper($shouldLog) { - self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; + self::$logWarningWhenAddingStorageWrapper = (bool)$shouldLog; } /** @@ -426,25 +427,36 @@ class Filesystem { self::$usersSetup[$user] = true; } - /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ - $mountConfigManager = \OC::$server->getMountProviderCollection(); + if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { + /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ + $mountConfigManager = \OC::$server->getMountProviderCollection(); - // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers - $homeMount = $mountConfigManager->getHomeMountForUser($userObject); + // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers + $homeMount = $mountConfigManager->getHomeMountForUser($userObject); - self::getMountManager()->addMount($homeMount); + self::getMountManager()->addMount($homeMount); - \OC\Files\Filesystem::getStorage($user); + \OC\Files\Filesystem::getStorage($user); - // Chance to mount for other storages - if ($userObject) { - $mounts = $mountConfigManager->getMountsForUser($userObject); - array_walk($mounts, array(self::$mounts, 'addMount')); - $mounts[] = $homeMount; - $mountConfigManager->registerMounts($userObject, $mounts); - } + // Chance to mount for other storages + if ($userObject) { + $mounts = $mountConfigManager->getMountsForUser($userObject); + array_walk($mounts, array(self::$mounts, 'addMount')); + $mounts[] = $homeMount; + $mountConfigManager->registerMounts($userObject, $mounts); + } - self::listenForNewMountProviders($mountConfigManager, $userManager); + self::listenForNewMountProviders($mountConfigManager, $userManager); + } else { + self::$mounts->addMount(new MountPoint( + new NullStorage([]), + '/' . $user + )); + self::$mounts->addMount(new MountPoint( + new NullStorage([]), + '/' . $user . '/files' + )); + } \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user)); } diff --git a/lib/private/Lockdown/Filesystem/NullCache.php b/lib/private/Lockdown/Filesystem/NullCache.php new file mode 100644 index 00000000000..8c6b5258aa8 --- /dev/null +++ b/lib/private/Lockdown/Filesystem/NullCache.php @@ -0,0 +1,122 @@ +<?php + +/** + * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Lockdown\Filesystem; + +use OC\Files\Cache\CacheEntry; +use OCP\Constants; +use OCP\Files\Cache\ICache; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\FileInfo; + +class NullCache implements ICache { + public function getNumericStorageId() { + return -1; + } + + public function get($file) { + return $file !== '' ? null : + new CacheEntry([ + 'fileid' => -1, + 'parent' => -1, + 'name' => '', + 'path' => '', + 'size' => '0', + 'mtime' => time(), + 'storage_mtime' => time(), + 'etag' => '', + 'mimetype' => FileInfo::MIMETYPE_FOLDER, + 'mimepart' => 'httpd', + 'permissions' => Constants::PERMISSION_READ + ]); + } + + public function getFolderContents($folder) { + return []; + } + + public function getFolderContentsById($fileId) { + return []; + } + + public function put($file, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function insert($file, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function update($id, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getId($file) { + return -1; + } + + public function getParentId($file) { + return -1; + } + + public function inCache($file) { + return $file === ''; + } + + public function remove($file) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function move($source, $target) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getStatus($file) { + return ICache::COMPLETE; + } + + public function search($pattern) { + return []; + } + + public function searchByMime($mimetype) { + return []; + } + + public function searchByTag($tag, $userId) { + return []; + } + + public function getIncomplete() { + return []; + } + + public function getPathById($id) { + return ''; + } + + public function normalize($path) { + return $path; + } + +} diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php new file mode 100644 index 00000000000..967b6d2c6e7 --- /dev/null +++ b/lib/private/Lockdown/Filesystem/NullStorage.php @@ -0,0 +1,177 @@ +<?php + +/** + * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Lockdown\Filesystem; + +use Icewind\Streams\IteratorDirectory; +use OC\Files\Storage\Common; + +class NullStorage extends Common { + public function __construct($parameters) { + parent::__construct($parameters); + } + + public function getId() { + return 'null'; + } + + public function mkdir($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function rmdir($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function opendir($path) { + return new IteratorDirectory([]); + } + + public function is_dir($path) { + return $path === ''; + } + + public function is_file($path) { + return false; + } + + public function stat($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function filetype($path) { + return ($path === '') ? 'dir' : false; + } + + public function filesize($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function isCreatable($path) { + return false; + } + + public function isReadable($path) { + return $path === ''; + } + + public function isUpdatable($path) { + return false; + } + + public function isDeletable($path) { + return false; + } + + public function isSharable($path) { + return false; + } + + public function getPermissions($path) { + return null; + } + + public function file_exists($path) { + return $path === ''; + } + + public function filemtime($path) { + return ($path === '') ? time() : false; + } + + public function file_get_contents($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function file_put_contents($path, $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function unlink($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function rename($path1, $path2) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function copy($path1, $path2) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function fopen($path, $mode) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getMimeType($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function hash($type, $path, $raw = false) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function free_space($path) { + return 0; + } + + public function touch($path, $mtime = null) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getLocalFile($path) { + return false; + } + + public function hasUpdated($path, $time) { + return false; + } + + public function getETag($path) { + return ''; + } + + public function isLocal() { + return false; + } + + public function getDirectDownload($path) { + return false; + } + + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function test() { + return true; + } + + public function getOwner($path) { + return null; + } + + public function getCache($path = '', $storage = null) { + return new NullCache(); + } +} diff --git a/lib/private/Lockdown/LockdownManager.php b/lib/private/Lockdown/LockdownManager.php new file mode 100644 index 00000000000..5ce52a03683 --- /dev/null +++ b/lib/private/Lockdown/LockdownManager.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Lockdown; + +use OC\Authentication\Token\IToken; +use OCP\Lockdown\ILockdownManager; + +class LockdownManager implements ILockdownManager { + private $enabled = false; + + /** @var array|null */ + private $scope; + + public function enable() { + $this->enabled = true; + } + + public function setToken(IToken $token) { + $this->scope = $token->getScopeAsArray(); + $this->enable(); + } + + public function canAccessFilesystem() { + if (!$this->enabled) { + return true; + } + return !$this->scope || $this->scope['filesystem']; + } +} diff --git a/lib/private/Notification/Notification.php b/lib/private/Notification/Notification.php index ffcb36af6e5..e5a8976f54d 100644 --- a/lib/private/Notification/Notification.php +++ b/lib/private/Notification/Notification.php @@ -289,7 +289,7 @@ class Notification implements INotification { * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 9.2.0 + * @since 11.0.0 */ public function setRichSubject($subject, array $parameters = []) { if (!is_string($subject) || $subject === '') { @@ -304,7 +304,7 @@ class Notification implements INotification { /** * @return string - * @since 9.2.0 + * @since 11.0.0 */ public function getRichSubject() { return $this->subjectRich; @@ -312,7 +312,7 @@ class Notification implements INotification { /** * @return array[] - * @since 9.2.0 + * @since 11.0.0 */ public function getRichSubjectParameters() { return $this->subjectRichParameters; @@ -379,7 +379,7 @@ class Notification implements INotification { * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid - * @since 9.2.0 + * @since 11.0.0 */ public function setRichMessage($message, array $parameters = []) { if (!is_string($message) || $message === '') { @@ -394,7 +394,7 @@ class Notification implements INotification { /** * @return string - * @since 9.2.0 + * @since 11.0.0 */ public function getRichMessage() { return $this->messageRich; @@ -402,7 +402,7 @@ class Notification implements INotification { /** * @return array[] - * @since 9.2.0 + * @since 11.0.0 */ public function getRichMessageParameters() { return $this->messageRichParameters; @@ -434,7 +434,7 @@ class Notification implements INotification { * @param string $icon * @return $this * @throws \InvalidArgumentException if the icon is invalid - * @since 9.2.0 + * @since 11.0.0 */ public function setIcon($icon) { if (!is_string($icon) || $icon === '' || isset($icon[4000])) { @@ -446,7 +446,7 @@ class Notification implements INotification { /** * @return string - * @since 9.2.0 + * @since 11.0.0 */ public function getIcon() { return $this->icon; diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index a2ef9659b3b..600557da5a0 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -155,7 +155,7 @@ class PreviewManager implements IPreview { * @param string $mimeType * @return ISimpleFile * @throws NotFoundException - * @since 9.2.0 + * @since 11.0.0 */ public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) { if ($this->generator === null) { diff --git a/lib/private/RichObjectStrings/Validator.php b/lib/private/RichObjectStrings/Validator.php index 11d17fef645..d90a6780ea8 100644 --- a/lib/private/RichObjectStrings/Validator.php +++ b/lib/private/RichObjectStrings/Validator.php @@ -30,7 +30,7 @@ use OCP\RichObjectStrings\IValidator; * Class Validator * * @package OCP\RichObjectStrings - * @since 9.2.0 + * @since 11.0.0 */ class Validator implements IValidator { @@ -53,7 +53,7 @@ class Validator implements IValidator { * @param string $subject * @param array[] $parameters * @throws InvalidObjectExeption - * @since 9.2.0 + * @since 11.0.0 */ public function validate($subject, array $parameters) { $matches = []; diff --git a/lib/private/Server.php b/lib/private/Server.php index 8e41ba212ad..c6755357a1d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -69,6 +69,7 @@ use OC\IntegrityCheck\Helpers\FileAccessHelper; use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; +use OC\Lockdown\LockdownManager; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Notification\Manager; @@ -90,6 +91,7 @@ use OC\Tagging\TagMapper; use OCA\Theming\ThemingDefaults; use OCP\IL10N; use OCP\IServerContainer; +use OCP\RichObjectStrings\IValidator; use OCP\Security\IContentSecurityPolicyManager; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -394,9 +396,11 @@ class Server extends ServerContainer implements IServerContainer { return new \OC\Activity\Manager( $c->getRequest(), $c->getUserSession(), - $c->getConfig() + $c->getConfig(), + $c->query(IValidator::class) ); }); + $this->registerAlias(IValidator::class, Validator::class); $this->registerService('AvatarManager', function (Server $c) { return new AvatarManager( $c->getUserManager(), @@ -662,7 +666,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerService('NotificationManager', function (Server $c) { return new Manager( - $c->query(Validator::class) + $c->query(IValidator::class) ); }); $this->registerService('CapabilitiesManager', function (Server $c) { @@ -792,6 +796,9 @@ class Server extends ServerContainer implements IServerContainer { $c->getSystemConfig() ); }); + $this->registerService('LockdownManager', function (Server $c) { + return new LockdownManager(); + }); } /** @@ -1531,4 +1538,11 @@ class Server extends ServerContainer implements IServerContainer { $factory = $this->query(\OC\Files\AppData\Factory::class); return $factory->get($app); } + + /** + * @return \OCP\Lockdown\ILockdownManager + */ + public function getLockdownManager() { + return $this->query('LockdownManager'); + } } diff --git a/lib/private/Settings/Admin/ServerDevNotice.php b/lib/private/Settings/Admin/ServerDevNotice.php new file mode 100644 index 00000000000..1d7f34f782d --- /dev/null +++ b/lib/private/Settings/Admin/ServerDevNotice.php @@ -0,0 +1,62 @@ +<?php +/** + * @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 OC\Settings\Admin; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use OC\Lock\DBLockingProvider; +use OC\Lock\NoopLockingProvider; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\Lock\ILockingProvider; +use OCP\Settings\ISettings; + +class ServerDevNotice implements ISettings { + /** + * @return TemplateResponse + */ + public function getForm() { + return new TemplateResponse('settings', 'admin/server.development.notice'); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'server'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 1000; + } +} diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php index e7be3d4e77d..a6f9aacccb2 100644 --- a/lib/private/Settings/Manager.php +++ b/lib/private/Settings/Manager.php @@ -369,6 +369,8 @@ class Manager implements IManager { /** @var ISettings $form */ $form = new Admin\Server($this->dbc, $this->config, $this->lockingProvider, $this->l); $forms[$form->getPriority()] = [$form]; + $form = new Admin\ServerDevNotice(); + $forms[$form->getPriority()] = [$form]; } if($section === 'encryption') { /** @var ISettings $form */ diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 43e09cdabcf..3d016700ee3 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -375,7 +375,7 @@ class Manager extends PublicEmitter implements IUserManager { * returns how many users have logged in once * * @return int - * @since 9.2.0 + * @since 11.0.0 */ public function countSeenUsers() { $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); @@ -395,7 +395,7 @@ class Manager extends PublicEmitter implements IUserManager { /** * @param \Closure $callback - * @since 9.2.0 + * @since 11.0.0 */ public function callForSeenUsers(\Closure $callback) { $limit = 1000; diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index ef408aa4077..6033f060504 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -525,6 +525,7 @@ class Session implements IUserSession, Emitter { //login $this->setUser($user); $this->setLoginName($dbToken->getLoginName()); + \OC::$server->getLockdownManager()->setToken($dbToken); $this->manager->emit('\OC\User', 'postLogin', array($user, $password)); if ($this->isLoggedIn()) { diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index a89a4650c5d..0d9adfa9d2b 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -162,6 +162,23 @@ class OC_App { } \OC::$server->getEventLogger()->end('load_app_' . $app); } + + $info = self::getAppInfo($app); + if (!empty($info['activity']['filters'])) { + foreach ($info['activity']['filters'] as $filter) { + \OC::$server->getActivityManager()->registerFilter($filter); + } + } + if (!empty($info['activity']['settings'])) { + foreach ($info['activity']['settings'] as $setting) { + \OC::$server->getActivityManager()->registerSetting($setting); + } + } + if (!empty($info['activity']['providers'])) { + foreach ($info['activity']['providers'] as $provider) { + \OC::$server->getActivityManager()->registerProvider($provider); + } + } } /** @@ -359,11 +376,15 @@ class OC_App { $config, $l ); + $appPath = self::getAppPath($appId); + self::registerAutoloading($appId, $appPath); $installer->installApp($appId); } else { // check for required dependencies $info = self::getAppInfo($appId); self::checkAppDependencies($config, $l, $info); + $appPath = self::getAppPath($appId); + self::registerAutoloading($appId, $appPath); $installer->installApp($appId); } diff --git a/lib/private/legacy/defaults.php b/lib/private/legacy/defaults.php index 38bbaa8daa1..23590cd9235 100644 --- a/lib/private/legacy/defaults.php +++ b/lib/private/legacy/defaults.php @@ -59,7 +59,7 @@ class OC_Defaults { $this->defaultiOSClientUrl = 'https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8'; $this->defaultiTunesAppId = '1125420102'; $this->defaultAndroidClientUrl = 'https://play.google.com/store/apps/details?id=com.nextcloud.client'; - $this->defaultDocBaseUrl = 'https://docs.nextcloud.org'; + $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; $this->defaultDocVersion = '10'; // used to generate doc links $this->defaultSlogan = $this->l->t('a safe home for all your data'); $this->defaultLogoClaim = ''; diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index ecc8f053704..3bd5b5586ab 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -66,6 +66,9 @@ class OC_Util { private static $rootMounted = false; private static $fsSetup = false; + /** @var array Local cache of version.php */ + private static $versionCache = null; + protected static function getAppManager() { return \OC::$server->getAppManager(); } @@ -397,7 +400,7 @@ class OC_Util { */ public static function getVersion() { OC_Util::loadVersion(); - return \OC::$server->getSession()->get('OC_Version'); + return self::$versionCache['OC_Version']; } /** @@ -407,7 +410,7 @@ class OC_Util { */ public static function getVersionString() { OC_Util::loadVersion(); - return \OC::$server->getSession()->get('OC_VersionString'); + return self::$versionCache['OC_VersionString']; } /** @@ -424,7 +427,13 @@ class OC_Util { */ public static function getChannel() { OC_Util::loadVersion(); - return \OC::$server->getSession()->get('OC_Channel'); + + // Allow overriding update channel + if (\OC::$server->getSystemConfig()->getValue('installed', false)) { + self::$versionCache['OC_Channel'] = \OC::$server->getAppConfig()->getValue('core', 'OC_Channel'); + } + + return self::$versionCache['OC_Channel']; } /** @@ -433,42 +442,30 @@ class OC_Util { */ public static function getBuild() { OC_Util::loadVersion(); - return \OC::$server->getSession()->get('OC_Build'); + return self::$versionCache['OC_Build']; } /** * @description load the version.php into the session as cache */ private static function loadVersion() { - $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); - if (!\OC::$server->getSession()->exists('OC_Version') or OC::$server->getSession()->get('OC_Version_Timestamp') != $timestamp) { - require OC::$SERVERROOT . '/version.php'; - $session = \OC::$server->getSession(); - /** @var $timestamp int */ - $session->set('OC_Version_Timestamp', $timestamp); - /** @var $OC_Version string */ - $session->set('OC_Version', $OC_Version); - /** @var $OC_VersionString string */ - $session->set('OC_VersionString', $OC_VersionString); - /** @var $OC_Build string */ - $session->set('OC_Build', $OC_Build); - - // Allow overriding update channel - - if (\OC::$server->getSystemConfig()->getValue('installed', false)) { - $channel = \OC::$server->getAppConfig()->getValue('core', 'OC_Channel'); - } else { - /** @var $OC_Channel string */ - $channel = $OC_Channel; - } - - if (!is_null($channel)) { - $session->set('OC_Channel', $channel); - } else { - /** @var $OC_Channel string */ - $session->set('OC_Channel', $OC_Channel); - } + if (self::$versionCache !== null) { + return; } + + $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); + require OC::$SERVERROOT . '/version.php'; + /** @var $timestamp int */ + self::$versionCache['OC_Version_Timestamp'] = $timestamp; + /** @var $OC_Version string */ + self::$versionCache['OC_Version'] = $OC_Version; + /** @var $OC_VersionString string */ + self::$versionCache['OC_VersionString'] = $OC_VersionString; + /** @var $OC_Build string */ + self::$versionCache['OC_Build'] = $OC_Build; + + /** @var $OC_Channel string */ + self::$versionCache['OC_Channel'] = $OC_Channel; } /** |