diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/base.php | 91 | ||||
-rw-r--r-- | lib/private/activity/event.php | 250 | ||||
-rw-r--r-- | lib/private/activitymanager.php | 132 | ||||
-rw-r--r-- | lib/private/app.php | 2 | ||||
-rw-r--r-- | lib/private/app/appmanager.php | 69 | ||||
-rw-r--r-- | lib/private/appframework/http/request.php | 22 | ||||
-rw-r--r-- | lib/private/backgroundjob/joblist.php | 17 | ||||
-rw-r--r-- | lib/private/config.php | 6 | ||||
-rw-r--r-- | lib/private/files/fileinfo.php | 2 | ||||
-rw-r--r-- | lib/private/http/client/client.php | 5 | ||||
-rw-r--r-- | lib/private/server.php | 49 | ||||
-rw-r--r-- | lib/private/session/cryptosessiondata.php | 147 | ||||
-rw-r--r-- | lib/private/session/cryptowrapper.php | 96 | ||||
-rw-r--r-- | lib/private/template.php | 2 | ||||
-rw-r--r-- | lib/private/util.php | 8 | ||||
-rw-r--r-- | lib/public/activity/iconsumer.php | 16 | ||||
-rw-r--r-- | lib/public/activity/ievent.php | 200 | ||||
-rw-r--r-- | lib/public/activity/imanager.php | 78 | ||||
-rw-r--r-- | lib/public/http/client/iclient.php | 5 |
19 files changed, 1062 insertions, 135 deletions
diff --git a/lib/base.php b/lib/base.php index c0f3e50142e..aceac2e53c3 100644 --- a/lib/base.php +++ b/lib/base.php @@ -134,18 +134,7 @@ class OC { OC_Config::$object = new \OC\Config(self::$configDir); OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT))); - /** - * FIXME: The following line is required because of a cyclic dependency - * on IRequest. - */ - $params = [ - 'server' => [ - 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], - 'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'], - ], - ]; - $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig())); - $scriptName = $fakeRequest->getScriptName(); + $scriptName = $_SERVER['SCRIPT_NAME']; if (substr($scriptName, -1) == '/') { $scriptName .= 'index.php'; //make sure suburi follows the same rules as scriptName @@ -346,27 +335,7 @@ class OC { if (\OCP\Util::needUpgrade()) { $systemConfig = \OC::$server->getSystemConfig(); if ($showTemplate && !$systemConfig->getValue('maintenance', false)) { - $version = OC_Util::getVersion(); - $oldTheme = $systemConfig->getValue('theme'); - $systemConfig->setValue('theme', ''); - OC_Util::addScript('config'); // needed for web root - OC_Util::addScript('update'); - $tmpl = new OC_Template('', 'update.admin', 'guest'); - $tmpl->assign('version', OC_Util::getVersionString()); - - // get third party apps - $apps = OC_App::getEnabledApps(); - $incompatibleApps = array(); - foreach ($apps as $appId) { - $info = OC_App::getAppInfo($appId); - if(!OC_App::isAppCompatible($version, $info)) { - $incompatibleApps[] = $info; - } - } - $tmpl->assign('appList', $incompatibleApps); - $tmpl->assign('productName', 'ownCloud'); // for now - $tmpl->assign('oldTheme', $oldTheme); - $tmpl->printPage(); + self::printUpgradePage(); exit(); } else { return true; @@ -375,6 +344,41 @@ class OC { return false; } + /** + * Prints the upgrade page + */ + private static function printUpgradePage() { + $systemConfig = \OC::$server->getSystemConfig(); + $oldTheme = $systemConfig->getValue('theme'); + $systemConfig->setValue('theme', ''); + \OCP\Util::addScript('config'); // needed for web root + \OCP\Util::addScript('update'); + + // check whether this is a core update or apps update + $installedVersion = $systemConfig->getValue('version', '0.0.0'); + $currentVersion = implode('.', OC_Util::getVersion()); + + $appManager = \OC::$server->getAppManager(); + + $tmpl = new OC_Template('', 'update.admin', 'guest'); + $tmpl->assign('version', OC_Util::getVersionString()); + + // if not a core upgrade, then it's apps upgrade + if (version_compare($currentVersion, $installedVersion, '=')) { + $tmpl->assign('isAppsOnlyUpgrade', true); + } else { + $tmpl->assign('isAppsOnlyUpgrade', false); + } + + // get third party apps + $ocVersion = OC_Util::getVersion(); + $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion)); + $tmpl->assign('incompatibleAppsList', $appManager->getIncompatibleApps($ocVersion)); + $tmpl->assign('productName', 'ownCloud'); // for now + $tmpl->assign('oldTheme', $oldTheme); + $tmpl->printPage(); + } + public static function initTemplateEngine() { // Add the stuff we need always // following logic will import all vendor libraries that are @@ -448,13 +452,15 @@ class OC { $useCustomSession = false; $session = self::$server->getSession(); OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession)); - if($useCustomSession) { - // use the session reference as the new Session - self::$server->setSession($session); - } else { + if (!$useCustomSession) { // set the session name to the instance id - which is unique - self::$server->setSession(new \OC\Session\Internal($sessionName)); + $session = new \OC\Session\Internal($sessionName); } + + $cryptoWrapper = \OC::$server->getSessionCryptoWrapper(); + $session = $cryptoWrapper->wrapSession($session); + self::$server->setSession($session); + // if session cant be started break with http 500 error } catch (Exception $e) { \OCP\Util::logException('base', $e); @@ -576,7 +582,7 @@ class OC { if (!defined('PHPUNIT_RUN')) { $logger = \OC::$server->getLogger(); OC\Log\ErrorHandler::setLogger($logger); - if (defined('DEBUG') and DEBUG) { + if (\OC::$server->getConfig()->getSystemValue('debug', false)) { OC\Log\ErrorHandler::register(true); set_exception_handler(array('OC_Template', 'printExceptionErrorPage')); } else { @@ -1034,7 +1040,7 @@ class OC { return false; } - if (defined("DEBUG") && DEBUG) { + if (\OC::$server->getConfig()->getSystemValue('debug', false)) { \OCP\Util::writeLog('core', 'Trying to login from cookie', \OCP\Util::DEBUG); } @@ -1087,11 +1093,12 @@ class OC { self::cleanupLoginTokens($userId); if (!empty($_POST["remember_login"])) { - if (defined("DEBUG") && DEBUG) { + $config = self::$server->getConfig(); + if ($config->getSystemValue('debug', false)) { self::$server->getLogger()->debug('Setting remember login to cookie', array('app' => 'core')); } $token = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(32); - self::$server->getConfig()->setUserValue($userId, 'login_token', $token, time()); + $config->setUserValue($userId, 'login_token', $token, time()); OC_User::setMagicInCookie($userId, $token); } else { OC_User::unsetMagicInCookie(); diff --git a/lib/private/activity/event.php b/lib/private/activity/event.php new file mode 100644 index 00000000000..fe6fc485b7b --- /dev/null +++ b/lib/private/activity/event.php @@ -0,0 +1,250 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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\Activity; + +use OCP\Activity\IEvent; + +class Event implements IEvent { + /** @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' => '', + ]; + + /** + * Set the app of the activity + * + * @param string $app + * @return IEvent + * @since 8.2.0 + */ + public function setApp($app) { + $this->data['app'] = (string) $app; + return $this; + } + + /** + * Set the type of the activity + * + * @param string $type + * @return IEvent + * @since 8.2.0 + */ + public function setType($type) { + $this->data['type'] = (string) $type; + return $this; + } + + /** + * Set the affected user of the activity + * + * @param string $affectedUser + * @return IEvent + * @since 8.2.0 + */ + public function setAffectedUser($affectedUser) { + $this->data['affected_user'] = (string) $affectedUser; + return $this; + } + + /** + * Set the author of the activity + * + * @param string $author + * @return IEvent + * @since 8.2.0 + */ + public function setAuthor($author) { + $this->data['author'] = (string) $author; + return $this; + } + + /** + * Set the author of the activity + * + * @param int $timestamp + * @return IEvent + * @since 8.2.0 + */ + public function setTimestamp($timestamp) { + $this->data['timestamp'] = (int) $timestamp; + return $this; + } + + /** + * Set the subject of the activity + * + * @param string $subject + * @param array $parameters + * @return IEvent + * @since 8.2.0 + */ + public function setSubject($subject, array $parameters = []) { + $this->data['subject'] = (string) $subject; + $this->data['subject_parameters'] = $parameters; + return $this; + } + + /** + * Set the message of the activity + * + * @param string $message + * @param array $parameters + * @return IEvent + * @since 8.2.0 + */ + public function setMessage($message, array $parameters = []) { + $this->data['message'] = (string) $message; + $this->data['message_parameters'] = $parameters; + return $this; + } + + /** + * Set the object of the activity + * + * @param string $objectType + * @param int $objectId + * @param string $objectName + * @return IEvent + * @since 8.2.0 + */ + public function setObject($objectType, $objectId, $objectName = '') { + $this->data['object_type'] = (string) $objectType; + $this->data['object_id'] = (int) $objectId; + $this->data['object_name'] = (string) $objectName; + return $this; + } + + /** + * Set the link of the activity + * + * @param string $link + * @return IEvent + * @since 8.2.0 + */ + public function setLink($link) { + $this->data['link'] = (string) $link; + return $this; + } + + /** + * @return string + */ + public function getApp() { + return $this->data['app']; + } + + /** + * @return string + */ + public function getType() { + return $this->data['type']; + } + + /** + * @return string + */ + public function getAffectedUser() { + return $this->data['affected_user']; + } + + /** + * @return string + */ + public function getAuthor() { + return $this->data['author']; + } + + /** + * @return int + */ + public function getTimestamp() { + return $this->data['timestamp']; + } + + /** + * @return string + */ + public function getSubject() { + return $this->data['subject']; + } + + /** + * @return array + */ + public function getSubjectParameters() { + return $this->data['subject_parameters']; + } + + /** + * @return string + */ + public function getMessage() { + return $this->data['message']; + } + + /** + * @return array + */ + public function getMessageParameters() { + return $this->data['message_parameters']; + } + + /** + * @return string + */ + public function getObjectType() { + return $this->data['object_type']; + } + + /** + * @return string + */ + public function getObjectId() { + return $this->data['object_id']; + } + + /** + * @return string + */ + public function getObjectName() { + return $this->data['object_name']; + } + + /** + * @return string + */ + public function getLink() { + return $this->data['link']; + } +} diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php index 938335a87e1..a973db7206f 100644 --- a/lib/private/activitymanager.php +++ b/lib/private/activitymanager.php @@ -24,11 +24,14 @@ namespace OC; +use OC\Activity\Event; use OCP\Activity\IConsumer; +use OCP\Activity\IEvent; use OCP\Activity\IExtension; use OCP\Activity\IManager; use OCP\IConfig; use OCP\IRequest; +use OCP\IUser; use OCP\IUserSession; class ActivityManager implements IManager { @@ -124,36 +127,87 @@ class ActivityManager implements IManager { } /** - * @param $app - * @param $subject - * @param $subjectParams - * @param $message - * @param $messageParams - * @param $file - * @param $link - * @param $affectedUser - * @param $type - * @param $priority - * @return mixed + * Generates a new IEvent object + * + * Make sure to call at least the following methods before sending it to the + * app with via the publish() method: + * - setApp() + * - setType() + * - setAffectedUser() + * - setSubject() + * + * @return IEvent */ - function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) { - foreach($this->getConsumers() as $c) { - try { - $c->receive( - $app, - $subject, - $subjectParams, - $message, - $messageParams, - $file, - $link, - $affectedUser, - $type, - $priority); - } catch (\Exception $ex) { - // TODO: log the exception + public function generateEvent() { + return new Event(); + } + + /** + * Publish an event to the activity consumers + * + * Make sure to call at least the following methods before sending an Event: + * - setApp() + * - setType() + * - setAffectedUser() + * - 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 ($this->session->getUser() instanceof IUser) { + $event->setAuthor($this->session->getUser()->getUID()); } } + + if (!$event->getTimestamp()) { + $event->setTimestamp(time()); + } + + foreach ($this->getConsumers() as $c) { + $c->receive($event); + } + } + + /** + * @param string $app The app where this event is associated with + * @param string $subject A short description of the event + * @param array $subjectParams Array with parameters that are filled in the subject + * @param string $message A longer description of the event + * @param array $messageParams Array with parameters that are filled in the message + * @param string $file The file including path where this event is associated with + * @param string $link A link where this event is associated with + * @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(); + $event->setApp($app) + ->setType($type) + ->setAffectedUser($affectedUser) + ->setSubject($subject, $subjectParams) + ->setMessage($message, $messageParams) + ->setObject('', 0, $file) + ->setLink($link); + + $this->publish($event); } /** @@ -164,7 +218,7 @@ class ActivityManager implements IManager { * * @param \Closure $callable */ - function registerConsumer(\Closure $callable) { + public function registerConsumer(\Closure $callable) { array_push($this->consumersClosures, $callable); $this->consumers = []; } @@ -178,7 +232,7 @@ class ActivityManager implements IManager { * @param \Closure $callable * @return void */ - function registerExtension(\Closure $callable) { + public function registerExtension(\Closure $callable) { array_push($this->extensionsClosures, $callable); $this->extensions = []; } @@ -189,7 +243,7 @@ class ActivityManager implements IManager { * @param string $languageCode * @return array */ - function getNotificationTypes($languageCode) { + public function getNotificationTypes($languageCode) { $notificationTypes = array(); foreach ($this->getExtensions() as $c) { $result = $c->getNotificationTypes($languageCode); @@ -205,7 +259,7 @@ class ActivityManager implements IManager { * @param string $method * @return array */ - function getDefaultTypes($method) { + public function getDefaultTypes($method) { $defaultTypes = array(); foreach ($this->getExtensions() as $c) { $types = $c->getDefaultTypes($method); @@ -220,7 +274,7 @@ class ActivityManager implements IManager { * @param string $type * @return string */ - function getTypeIcon($type) { + public function getTypeIcon($type) { if (isset($this->typeIcons[$type])) { return $this->typeIcons[$type]; } @@ -246,7 +300,7 @@ class ActivityManager implements IManager { * @param string $languageCode * @return string|false */ - function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { + public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { foreach ($this->getExtensions() as $c) { $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); if (is_string($translation)) { @@ -262,7 +316,7 @@ class ActivityManager implements IManager { * @param string $text * @return array|false */ - function getSpecialParameterList($app, $text) { + public function getSpecialParameterList($app, $text) { if (isset($this->specialParameters[$app][$text])) { return $this->specialParameters[$app][$text]; } @@ -287,7 +341,7 @@ class ActivityManager implements IManager { * @param array $activity * @return integer|false */ - function getGroupParameter($activity) { + public function getGroupParameter($activity) { foreach ($this->getExtensions() as $c) { $parameter = $c->getGroupParameter($activity); if ($parameter !== false) { @@ -301,7 +355,7 @@ class ActivityManager implements IManager { /** * @return array */ - function getNavigation() { + public function getNavigation() { $entries = array( 'apps' => array(), 'top' => array(), @@ -321,7 +375,7 @@ class ActivityManager implements IManager { * @param string $filterValue * @return boolean */ - function isFilterValid($filterValue) { + public function isFilterValid($filterValue) { if (isset($this->validFilters[$filterValue])) { return $this->validFilters[$filterValue]; } @@ -342,7 +396,7 @@ class ActivityManager implements IManager { * @param string $filter * @return array */ - function filterNotificationTypes($types, $filter) { + public function filterNotificationTypes($types, $filter) { if (!$this->isFilterValid($filter)) { return $types; } @@ -360,7 +414,7 @@ class ActivityManager implements IManager { * @param string $filter * @return array */ - function getQueryForFilter($filter) { + public function getQueryForFilter($filter) { if (!$this->isFilterValid($filter)) { return [null, null]; } diff --git a/lib/private/app.php b/lib/private/app.php index 9de1c66ee55..f1a1d27ae66 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -1139,7 +1139,7 @@ class OC_App { // check for required dependencies $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l); - $missing = $dependencyAnalyzer->analyze($app); + $missing = $dependencyAnalyzer->analyze($info); if (!empty($missing)) { $missingMsg = join(PHP_EOL, $missing); throw new \Exception( diff --git a/lib/private/app/appmanager.php b/lib/private/app/appmanager.php index 7a61cd53c59..75b1c0a7865 100644 --- a/lib/private/app/appmanager.php +++ b/lib/private/app/appmanager.php @@ -209,4 +209,73 @@ class AppManager implements IAppManager { $settingsMemCache = $this->memCacheFactory->create('settings'); $settingsMemCache->clear('listApps'); } + + /** + * Returns a list of apps that need upgrade + * + * @param array $version ownCloud version as array of version components + * @return array list of app info from apps that need an upgrade + * + * @internal + */ + public function getAppsNeedingUpgrade($ocVersion) { + $appsToUpgrade = []; + $apps = $this->getInstalledApps(); + foreach ($apps as $appId) { + $appInfo = $this->getAppInfo($appId); + $appDbVersion = $this->appConfig->getValue($appId, 'installed_version'); + if ($appDbVersion + && isset($appInfo['version']) + && version_compare($appInfo['version'], $appDbVersion, '>') + && \OC_App::isAppCompatible($ocVersion, $appInfo) + ) { + $appsToUpgrade[] = $appInfo; + } + } + + return $appsToUpgrade; + } + + /** + * Returns the app information from "appinfo/info.xml". + * + * If no version was present in "appinfo/info.xml", reads it + * from the external "appinfo/version" file instead. + * + * @param string $appId app id + * + * @return array app iinfo + * + * @internal + */ + public function getAppInfo($appId) { + $appInfo = \OC_App::getAppInfo($appId); + if (!isset($appInfo['version'])) { + // read version from separate file + $appInfo['version'] = \OC_App::getAppVersion($appId); + } + return $appInfo; + } + + /** + * Returns a list of apps incompatible with the given version + * + * @param array $version ownCloud version as array of version components + * + * @return array list of app info from incompatible apps + * + * @internal + */ + public function getIncompatibleApps($version) { + $apps = $this->getInstalledApps(); + $incompatibleApps = array(); + foreach ($apps as $appId) { + $info = $this->getAppInfo($appId); + if (!\OC_App::isAppCompatible($version, $info)) { + $incompatibleApps[] = $info; + } + } + return $incompatibleApps; + } + } diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index aaad286e843..a2109439177 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -32,6 +32,7 @@ namespace OC\AppFramework\Http; use OC\Security\TrustedDomainHelper; use OCP\IConfig; use OCP\IRequest; +use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; /** @@ -67,6 +68,8 @@ class Request implements \ArrayAccess, \Countable, IRequest { protected $config; /** @var string */ protected $requestId = ''; + /** @var ICrypto */ + protected $crypto; /** * @param array $vars An associative array with the following optional values: @@ -80,17 +83,20 @@ class Request implements \ArrayAccess, \Countable, IRequest { * - string 'method' the request method (GET, POST etc) * - string|false 'requesttoken' the requesttoken or false when not available * @param ISecureRandom $secureRandom + * @param ICrypto $crypto * @param IConfig $config * @param string $stream * @see http://www.php.net/manual/en/reserved.variables.php */ public function __construct(array $vars=array(), ISecureRandom $secureRandom = null, + ICrypto $crypto, IConfig $config, $stream='php://input') { $this->inputStream = $stream; $this->items['params'] = array(); $this->secureRandom = $secureRandom; + $this->crypto = $crypto; $this->config = $config; if(!array_key_exists('method', $vars)) { @@ -415,8 +421,22 @@ class Request implements \ArrayAccess, \Countable, IRequest { return false; } + // Decrypt token to prevent BREACH like attacks + $token = explode(':', $token); + if (count($token) !== 2) { + return false; + } + + $encryptedToken = $token[0]; + $secret = $token[1]; + try { + $decryptedToken = $this->crypto->decrypt($encryptedToken, $secret); + } catch (\Exception $e) { + return false; + } + // Check if the token is valid - if(\OCP\Security\StringUtils::equals($token, $this->items['requesttoken'])) { + if(\OCP\Security\StringUtils::equals($decryptedToken, $this->items['requesttoken'])) { return true; } else { return false; diff --git a/lib/private/backgroundjob/joblist.php b/lib/private/backgroundjob/joblist.php index e8915b47f24..f297bccbc7d 100644 --- a/lib/private/backgroundjob/joblist.php +++ b/lib/private/backgroundjob/joblist.php @@ -87,6 +87,11 @@ class JobList implements IJobList { } } + protected function removeById($id) { + $query = $this->conn->prepare('DELETE FROM `*PREFIX*jobs` WHERE `id` = ?'); + $query->execute([$id]); + } + /** * check if a job is in the list * @@ -134,17 +139,25 @@ class JobList implements IJobList { $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` WHERE `id` > ? ORDER BY `id` ASC', 1); $query->execute(array($lastId)); if ($row = $query->fetch()) { - return $this->buildJob($row); + $jobId = $row['id']; + $job = $this->buildJob($row); } else { //begin at the start of the queue $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` ORDER BY `id` ASC', 1); $query->execute(); if ($row = $query->fetch()) { - return $this->buildJob($row); + $jobId = $row['id']; + $job = $this->buildJob($row); } else { return null; //empty job list } } + if (is_null($job)) { + $this->removeById($jobId); + return $this->getNext(); + } else { + return $job; + } } /** diff --git a/lib/private/config.php b/lib/private/config.php index 20ff02c1abf..3ad800a00be 100644 --- a/lib/private/config.php +++ b/lib/private/config.php @@ -47,8 +47,6 @@ class Config { protected $configFilePath; /** @var string */ protected $configFileName; - /** @var bool */ - protected $debugMode; /** * @param string $configDir Path to the config dir, needs to end with '/' @@ -59,7 +57,6 @@ class Config { $this->configFilePath = $this->configDir.$fileName; $this->configFileName = $fileName; $this->readData(); - $this->debugMode = (defined('DEBUG') && DEBUG); } /** @@ -225,9 +222,6 @@ class Config { private function writeData() { // Create a php file ... $content = "<?php\n"; - if ($this->debugMode) { - $content .= "define('DEBUG',true);\n"; - } $content .= '$CONFIG = '; $content .= var_export($this->cache, true); $content .= ";\n"; diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 82c8f3de690..b333844f8c8 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -252,7 +252,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { $sid = $this->getStorage()->getId(); if (!is_null($sid)) { $sid = explode(':', $sid); - return ($sid[0] !== 'local' and $sid[0] !== 'home' and $sid[0] !== 'shared'); + return ($sid[0] !== 'home' and $sid[0] !== 'shared'); } return false; diff --git a/lib/private/http/client/client.php b/lib/private/http/client/client.php index fb3e06f3c46..323fc0d324f 100644 --- a/lib/private/http/client/client.php +++ b/lib/private/http/client/client.php @@ -144,6 +144,7 @@ class Client implements IClient { * 'debug' => true, * 'timeout' => 5, * @return Response + * @throws \Exception If the request could not get completed */ public function head($uri, $options = []) { $response = $this->client->head($uri, $options); @@ -176,6 +177,7 @@ class Client implements IClient { * 'debug' => true, * 'timeout' => 5, * @return Response + * @throws \Exception If the request could not get completed */ public function post($uri, array $options = []) { $response = $this->client->post($uri, $options); @@ -208,6 +210,7 @@ class Client implements IClient { * 'debug' => true, * 'timeout' => 5, * @return Response + * @throws \Exception If the request could not get completed */ public function put($uri, array $options = []) { $response = $this->client->put($uri, $options); @@ -240,6 +243,7 @@ class Client implements IClient { * 'debug' => true, * 'timeout' => 5, * @return Response + * @throws \Exception If the request could not get completed */ public function delete($uri, array $options = []) { $response = $this->client->delete($uri, $options); @@ -273,6 +277,7 @@ class Client implements IClient { * 'debug' => true, * 'timeout' => 5, * @return Response + * @throws \Exception If the request could not get completed */ public function options($uri, array $options = []) { $response = $this->client->options($uri, $options); diff --git a/lib/private/server.php b/lib/private/server.php index 89001567219..5a3a6328fae 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -40,6 +40,7 @@ use bantu\IniGetWrapper\IniGetWrapper; use OC\AppFramework\Http\Request; use OC\AppFramework\Db\Db; use OC\AppFramework\Utility\SimpleContainer; +use OC\AppFramework\Utility\TimeFactory; use OC\Command\AsyncBus; use OC\Diagnostics\EventLogger; use OC\Diagnostics\NullEventLogger; @@ -56,6 +57,7 @@ use OC\Security\Crypto; use OC\Security\Hasher; use OC\Security\SecureRandom; use OC\Security\TrustedDomainHelper; +use OC\Session\CryptoWrapper; use OC\Tagging\TagMapper; use OCP\IServerContainer; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -159,7 +161,12 @@ class Server extends SimpleContainer implements IServerContainer { }); $this->registerService('UserSession', function (Server $c) { $manager = $c->getUserManager(); - $userSession = new \OC\User\Session($manager, new \OC\Session\Memory('')); + + $session = new \OC\Session\Memory(''); + $cryptoWrapper = $c->getSessionCryptoWrapper(); + $session = $cryptoWrapper->wrapSession($session); + + $userSession = new \OC\User\Session($manager, $session); $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { \OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password)); }); @@ -322,14 +329,14 @@ class Server extends SimpleContainer implements IServerContainer { ); }); $this->registerService('EventLogger', function (Server $c) { - if (defined('DEBUG') and DEBUG) { + if ($c->getSystemConfig()->getValue('debug', false)) { return new EventLogger(); } else { return new NullEventLogger(); } }); - $this->registerService('QueryLogger', function ($c) { - if (defined('DEBUG') and DEBUG) { + $this->registerService('QueryLogger', function (Server $c) { + if ($c->getSystemConfig()->getValue('debug', false)) { return new QueryLogger(); } else { return new NullQueryLogger(); @@ -410,6 +417,7 @@ class Server extends SimpleContainer implements IServerContainer { 'requesttoken' => $requestToken, ], $this->getSecureRandom(), + $this->getCrypto(), $this->getConfig(), $stream ); @@ -461,6 +469,32 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('EventDispatcher', function() { return new EventDispatcher(); }); + $this->registerService('CryptoWrapper', function (Server $c) { + // FIXME: Instantiiated here due to cyclic dependency + $request = new Request( + [ + 'get' => $_GET, + 'post' => $_POST, + 'files' => $_FILES, + 'server' => $_SERVER, + 'env' => $_ENV, + 'cookies' => $_COOKIE, + 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) + ? $_SERVER['REQUEST_METHOD'] + : null, + ], + new SecureRandom(), + $c->getCrypto(), + $c->getConfig() + ); + + return new CryptoWrapper( + $c->getConfig(), + $c->getCrypto(), + $c->getSecureRandom(), + $request + ); + }); } /** @@ -976,4 +1010,11 @@ class Server extends SimpleContainer implements IServerContainer { public function getEventDispatcher() { return $this->query('EventDispatcher'); } + + /** + * @return \OC\Session\CryptoWrapper + */ + public function getSessionCryptoWrapper() { + return $this->query('CryptoWrapper'); + } } diff --git a/lib/private/session/cryptosessiondata.php b/lib/private/session/cryptosessiondata.php new file mode 100644 index 00000000000..60d22b25e97 --- /dev/null +++ b/lib/private/session/cryptosessiondata.php @@ -0,0 +1,147 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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\Session; + +use OCP\ISession; +use OCP\Security\ICrypto; + +/** + * Class CryptoSessionData + * + * @package OC\Session + */ +class CryptoSessionData implements \ArrayAccess, ISession { + /** @var ISession */ + protected $session; + + /** @var \OCP\Security\ICrypto */ + protected $crypto; + + /** @var string */ + protected $passphrase; + + /** + * @param ISession $session + * @param ICrypto $crypto + * @param string $passphrase + */ + public function __construct(ISession $session, ICrypto $crypto, $passphrase) { + $this->crypto = $crypto; + $this->session = $session; + $this->passphrase = $passphrase; + } + + /** + * Set a value in the session + * + * @param string $key + * @param mixed $value + */ + public function set($key, $value) { + $encryptedValue = $this->crypto->encrypt(json_encode($value), $this->passphrase); + $this->session->set($key, $encryptedValue); + } + + /** + * Get a value from the session + * + * @param string $key + * @return string|null Either the value or null + */ + public function get($key) { + $encryptedValue = $this->session->get($key); + if ($encryptedValue === null) { + return null; + } + + try { + $value = $this->crypto->decrypt($encryptedValue, $this->passphrase); + return json_decode($value); + } catch (\Exception $e) { + return null; + } + } + + /** + * Check if a named key exists in the session + * + * @param string $key + * @return bool + */ + public function exists($key) { + return $this->session->exists($key); + } + + /** + * Remove a $key/$value pair from the session + * + * @param string $key + */ + public function remove($key) { + $this->session->remove($key); + } + + /** + * Reset and recreate the session + */ + public function clear() { + $this->session->clear(); + } + + /** + * Close the session and release the lock + */ + public function close() { + $this->session->close(); + } + + /** + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) { + return $this->exists($offset); + } + + /** + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) { + return $this->get($offset); + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) { + $this->remove($offset); + } +} diff --git a/lib/private/session/cryptowrapper.php b/lib/private/session/cryptowrapper.php new file mode 100644 index 00000000000..62bdcbfb719 --- /dev/null +++ b/lib/private/session/cryptowrapper.php @@ -0,0 +1,96 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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\Session; + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IConfig; +use OCP\IRequest; +use OCP\ISession; +use OCP\Security\ICrypto; +use OCP\Security\ISecureRandom; + +/** + * Class CryptoWrapper provides some rough basic level of additional security by + * storing the session data in an encrypted form. + * + * The content of the session is encrypted using another cookie sent by the browser. + * One should note that an adversary with access to the source code or the system + * memory is still able to read the original session ID from the users' request. + * This thus can not be considered a strong security measure one should consider + * it as an additional small security obfuscation layer to comply with compliance + * guidelines. + * + * TODO: Remove this in a future relase with an approach such as + * https://github.com/owncloud/core/pull/17866 + * + * @package OC\Session + */ +class CryptoWrapper { + const COOKIE_NAME = 'oc_sessionPassphrase'; + + /** @var ISession */ + protected $session; + + /** @var \OCP\Security\ICrypto */ + protected $crypto; + + /** @var ISecureRandom */ + protected $random; + + /** + * @param IConfig $config + * @param ICrypto $crypto + * @param ISecureRandom $random + * @param IRequest $request + */ + public function __construct(IConfig $config, + ICrypto $crypto, + ISecureRandom $random, + IRequest $request) { + $this->crypto = $crypto; + $this->config = $config; + $this->random = $random; + + if (!is_null($request->getCookie(self::COOKIE_NAME))) { + $this->passphrase = $request->getCookie(self::COOKIE_NAME); + } else { + $this->passphrase = $this->random->getMediumStrengthGenerator()->generate(128); + $secureCookie = $request->getServerProtocol() === 'https'; + // FIXME: Required for CI + if (!defined('PHPUNIT_RUN')) { + setcookie(self::COOKIE_NAME, $this->passphrase, 0, \OC::$WEBROOT, '', $secureCookie, true); + } + } + } + + /** + * @param ISession $session + * @return ISession + */ + public function wrapSession(ISession $session) { + if (!($session instanceof CryptoSessionData)) { + return new CryptoSessionData($session, $this->crypto, $this->passphrase); + } + + return $session; + } +} diff --git a/lib/private/template.php b/lib/private/template.php index e7acc778de3..920be71abbf 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -233,7 +233,7 @@ class OC_Template extends \OC\Template\Base { $content->assign('file', $exception->getFile()); $content->assign('line', $exception->getLine()); $content->assign('trace', $exception->getTraceAsString()); - $content->assign('debugMode', defined('DEBUG') && DEBUG === true); + $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); $content->printPage(); diff --git a/lib/private/util.php b/lib/private/util.php index 501dbf5c4c5..edd375b5c36 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -1057,7 +1057,8 @@ class OC_Util { /** * Register an get/post call. Important to prevent CSRF attacks. * - * @return string Generated token. + * @return string The encrypted CSRF token, the shared secret is appended after the `:`. + * * @description * Creates a 'request token' (random) and stores it inside the session. * Ever subsequent (ajax) request must use such a valid token to succeed, @@ -1074,7 +1075,10 @@ class OC_Util { // Valid token already exists, send it $requestToken = \OC::$server->getSession()->get('requesttoken'); } - return ($requestToken); + + // Encrypt the token to mitigate breach-like attacks + $sharedSecret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10); + return \OC::$server->getCrypto()->encrypt($requestToken, $sharedSecret) . ':' . $sharedSecret; } /** diff --git a/lib/public/activity/iconsumer.php b/lib/public/activity/iconsumer.php index a55110ababc..e74884d76c5 100644 --- a/lib/public/activity/iconsumer.php +++ b/lib/public/activity/iconsumer.php @@ -37,19 +37,11 @@ namespace OCP\Activity; */ interface IConsumer { /** - * @param $app - * @param $subject - * @param $subjectParams - * @param $message - * @param $messageParams - * @param $file - * @param $link - * @param $affectedUser - * @param $type - * @param $priority - * @return mixed + * @param IEvent $event + * @return null * @since 6.0.0 + * @since 8.2.0 Replaced the parameters with an IEvent object */ - function receive($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority ); + public function receive(IEvent $event); } diff --git a/lib/public/activity/ievent.php b/lib/public/activity/ievent.php new file mode 100644 index 00000000000..184c7ae503f --- /dev/null +++ b/lib/public/activity/ievent.php @@ -0,0 +1,200 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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/> + * + */ + +/** + * Public interface of ownCloud for apps to use. + * Activity/IEvent interface + */ + +// use OCP namespace for all classes that are considered public. +// This means that they should be used by apps instead of the internal ownCloud classes +namespace OCP\Activity; + +/** + * Interface IEvent + * + * @package OCP\Activity + * @since 8.2.0 + */ +interface IEvent { + /** + * Set the app of the activity + * + * @param string $app + * @return IEvent + * @since 8.2.0 + */ + public function setApp($app); + + /** + * Set the type of the activity + * + * @param string $type + * @return IEvent + * @since 8.2.0 + */ + public function setType($type); + + /** + * Set the affected user of the activity + * + * @param string $user + * @return IEvent + * @since 8.2.0 + */ + public function setAffectedUser($user); + + /** + * Set the author of the activity + * + * @param string $author + * @return IEvent + * @since 8.2.0 + */ + public function setAuthor($author); + + /** + * Set the author of the activity + * + * @param int $timestamp + * @return IEvent + * @since 8.2.0 + */ + public function setTimestamp($timestamp); + + /** + * Set the subject of the activity + * + * @param string $subject + * @param array $parameters + * @return IEvent + * @since 8.2.0 + */ + public function setSubject($subject, array $parameters = []); + + /** + * Set the message of the activity + * + * @param string $message + * @param array $parameters + * @return IEvent + * @since 8.2.0 + */ + public function setMessage($message, array $parameters = []); + + /** + * Set the object of the activity + * + * @param string $objectType + * @param int $objectId + * @param string $objectName + * @return IEvent + * @since 8.2.0 + */ + public function setObject($objectType, $objectId, $objectName = ''); + + /** + * Set the link of the activity + * + * @param string $link + * @return IEvent + * @since 8.2.0 + */ + public function setLink($link); + + /** + * @return string + * @since 8.2.0 + */ + public function getApp(); + + /** + * @return string + * @since 8.2.0 + */ + public function getType(); + + /** + * @return string + * @since 8.2.0 + */ + public function getAffectedUser(); + + /** + * @return string + * @since 8.2.0 + */ + public function getAuthor(); + + /** + * @return int + * @since 8.2.0 + */ + public function getTimestamp(); + + /** + * @return string + * @since 8.2.0 + */ + public function getSubject(); + + /** + * @return array + * @since 8.2.0 + */ + public function getSubjectParameters(); + + /** + * @return string + * @since 8.2.0 + */ + public function getMessage(); + + /** + * @return array + * @since 8.2.0 + */ + public function getMessageParameters(); + + /** + * @return string + * @since 8.2.0 + */ + public function getObjectType(); + + /** + * @return string + * @since 8.2.0 + */ + public function getObjectId(); + + /** + * @return string + * @since 8.2.0 + */ + public function getObjectName(); + + /** + * @return string + * @since 8.2.0 + */ + public function getLink(); +} diff --git a/lib/public/activity/imanager.php b/lib/public/activity/imanager.php index 0f5dccd8ba1..b3a4969fb06 100644 --- a/lib/public/activity/imanager.php +++ b/lib/public/activity/imanager.php @@ -38,22 +38,52 @@ namespace OCP\Activity; * @since 6.0.0 */ interface IManager { + /** + * Generates a new IEvent object + * + * Make sure to call at least the following methods before sending it to the + * app with via the publish() method: + * - setApp() + * - setType() + * - setAffectedUser() + * - setSubject() + * + * @return IEvent + * @since 8.2.0 + */ + public function generateEvent(); + + /** + * Publish an event to the activity consumers + * + * Make sure to call at least the following methods before sending an Event: + * - setApp() + * - setType() + * - setAffectedUser() + * - setSubject() + * + * @param IEvent $event + * @return null + * @since 8.2.0 + */ + public function publish(IEvent $event); /** - * @param $app - * @param $subject - * @param $subjectParams - * @param $message - * @param $messageParams - * @param $file - * @param $link - * @param $affectedUser - * @param $type - * @param $priority - * @return mixed + * @param string $app The app where this event is associated with + * @param string $subject A short description of the event + * @param array $subjectParams Array with parameters that are filled in the subject + * @param string $message A longer description of the event + * @param array $messageParams Array with parameters that are filled in the message + * @param string $file The file including path where this event is associated with + * @param string $link A link where this event is associated with + * @param string $affectedUser Recipient of the activity + * @param string $type Type of the notification + * @param int $priority Priority of the notification + * @return null * @since 6.0.0 + * @deprecated 8.2.0 Grab an IEvent from generateEvent() instead and use the publish() method */ - function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority); + public function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority); /** * In order to improve lazy loading a closure can be registered which will be called in case @@ -65,7 +95,7 @@ interface IManager { * @return void * @since 6.0.0 */ - function registerConsumer(\Closure $callable); + public function registerConsumer(\Closure $callable); /** * In order to improve lazy loading a closure can be registered which will be called in case @@ -77,7 +107,7 @@ interface IManager { * @return void * @since 8.0.0 */ - function registerExtension(\Closure $callable); + public function registerExtension(\Closure $callable); /** * Will return additional notification types as specified by other apps @@ -91,21 +121,21 @@ interface IManager { * @since 8.0.0 * @changed 8.2.0 - Added support to allow limiting notifications to certain methods */ - function getNotificationTypes($languageCode); + public function getNotificationTypes($languageCode); /** * @param string $method * @return array * @since 8.0.0 */ - function getDefaultTypes($method); + public function getDefaultTypes($method); /** * @param string $type * @return string * @since 8.0.0 */ - function getTypeIcon($type); + public function getTypeIcon($type); /** * @param string $app @@ -117,7 +147,7 @@ interface IManager { * @return string|false * @since 8.0.0 */ - function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); + public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); /** * @param string $app @@ -125,27 +155,27 @@ interface IManager { * @return array|false * @since 8.0.0 */ - function getSpecialParameterList($app, $text); + public function getSpecialParameterList($app, $text); /** * @param array $activity * @return integer|false * @since 8.0.0 */ - function getGroupParameter($activity); + public function getGroupParameter($activity); /** * @return array * @since 8.0.0 */ - function getNavigation(); + public function getNavigation(); /** * @param string $filterValue * @return boolean * @since 8.0.0 */ - function isFilterValid($filterValue); + public function isFilterValid($filterValue); /** * @param array $types @@ -153,14 +183,14 @@ interface IManager { * @return array * @since 8.0.0 */ - function filterNotificationTypes($types, $filter); + public function filterNotificationTypes($types, $filter); /** * @param string $filter * @return array * @since 8.0.0 */ - function getQueryForFilter($filter); + public function getQueryForFilter($filter); /** * Get the user we need to use diff --git a/lib/public/http/client/iclient.php b/lib/public/http/client/iclient.php index aadb9efd1bb..494ca7d419e 100644 --- a/lib/public/http/client/iclient.php +++ b/lib/public/http/client/iclient.php @@ -80,6 +80,7 @@ interface IClient { * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse + * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function head($uri, $options = []); @@ -109,6 +110,7 @@ interface IClient { * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse + * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function post($uri, array $options = []); @@ -138,6 +140,7 @@ interface IClient { * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse + * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function put($uri, array $options = []); @@ -167,6 +170,7 @@ interface IClient { * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse + * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function delete($uri, array $options = []); @@ -196,6 +200,7 @@ interface IClient { * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse + * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function options($uri, array $options = []); |